Contents

Language Servers

Zasper uses the Language Server Protocol for what an editor cannot work out on its own: errors as you type, completion with documentation, hover, go to definition, find references, rename, quick fixes, symbols and formatting. Files and notebook cells both get them.

Zasper ships no language servers. It starts one you already have, and tells you when you have none.

What You Need

Language Server Install
Python basedpyright, pyright or pylsp pip install basedpyright
Go gopls go install golang.org/x/tools/gopls@latest
TypeScript, JavaScript typescript-language-server npm install -g typescript-language-server typescript
Rust rust-analyzer rustup component add rust-analyzer
C, C++ clangd brew install llvm, or your package manager
R languageserver R -e 'install.packages("languageserver")'
Julia LanguageServer.jl julia -e 'using Pkg; Pkg.add("LanguageServer")'

For Python, the first of the three that is found is used, in that order. basedpyright and pyright are type checkers; pylsp is jedi with a set of plugins, so the type-checking setting below does not apply to it.

Zasper looks on your PATH first, then in the project’s .venv/bin, venv/bin and node_modules/.bin, then in $GOPATH/bin, ~/go/bin, ~/.cargo/bin, ~/.local/bin, ~/.juliaup/bin and Homebrew. An app started from the Dock has a thinner PATH than your shell, and without that list gopls in ~/go/bin would look uninstalled.

The Status Bar

The item at the right of the status bar names the server for the file or notebook in front, with a dot for its state: starting, ready, failed or stopped. Its menu restarts the server, shows what it wrote to standard error (Show log), stops it and, for Python, names the interpreter the server reads imports with. When nothing is installed it says No server, and the menu gives the command that installs one.

The counts beside it are the problems in the project. Pressing them opens the Problems panel under the editor.

Problems

An error or a warning is a squiggle under the code, a mark in the gutter, a row in the Problems panel and a number in the status bar. In a notebook, the row names the cell, such as analysis.ipynb cell 3, 2:5. Pressing a row opens the file, or the cell, with the cursor on the problem.

Some things a server reports are not faults: an unused import, a branch that can never run. Those are drawn as faded text, with no squiggle and nothing in the gutter, as other editors draw them. They are listed in the Problems panel but not counted in the status bar.

Settings

Under Settings → Language servers:

A project’s own configuration wins over the Type checking setting: a pyrightconfig.json, or [tool.pyright] or [tool.basedpyright] in pyproject.toml, is read by the server itself.

Which Type-Checking Mode

off still reports the mistakes worth stopping for: undefined names, imports that cannot be found, and a module whose type stubs are missing. That is the same set Pylance keeps with its defaults.

The stricter modes also report correct code that uses libraries without type information. The checker reads the library’s source, infers a type it cannot narrow, and faults an attribute that is there when the code runs. scikit-learn is the common example: under standard, load_iris(as_frame=True).frame is reported although it works. Choose basic or standard for code you maintain and have types for, and silence a single line with # type: ignore[attr-defined].

In a Notebook

A notebook’s cells are given to the server as one document, so a name defined in one cell is known in the next.

IPython’s own syntax is hidden from the server. Line magics, ! shell escapes and obj? are replaced before the server sees them, line for line, so the server never reports them as errors and every line keeps its number. %%time and its relatives hide only their first line; %%bash and other cell magics hide the whole cell.

Imports resolve against the kernel’s Python. The server is told which interpreter the notebook’s kernel runs, so a notebook on a conda or uv environment resolves its imports with no configuration. A plain .py file has no kernel, so it is read with Settings → Python interpreter. Left on Automatic, that is the project’s .venv or venv, or the python on your PATH when there is neither. The same interpreter runs the file with Run Python File in Terminal.

Completion comes from both the server and the kernel. The kernel knows what df is right now, because it ran the cell that made it; the server knows the source, including cells that have not run. While you type, suggestions come from the server alone. After a ., on Tab and on Ctrl-Space, both are asked and merged, and each suggestion is tagged kernel or source. The kernel is kept out of typing on purpose: it answers one request at a time, so asking it on every keystroke would queue behind a running cell.

Shift+Tab shows the kernel’s documentation for the name at the cursor, as Jupyter does.

Formatting

Format Document formats a file, and Format Cell and Format Notebook format a notebook; all three are in the command palette. Settings can also format a file whenever it is saved. A notebook is formatted a cell at a time, so an edit never reaches across cells, and IPython’s lines are put back untouched.

basedpyright and pyright do not format, and Zasper says so when you ask. To format Python, set a server that does under Settings → Language servers, such as ruff server.

Not There Yet

When Something Looks Wrong