← Back to Blog

Hovering on a Pipeline

April 2026 toolinglsp

When you hover over a function call in a typical editor, you get a tooltip with the signature. Useful. When you hover over a *pipeline stage*, you get… the same tooltip. Which is fine, but it leaves you mentally reconstructing what value just flowed in and what value is flowing out. In Lateralus 0.5 we fixed that.

Full implementation: compiler/lsp/hover.ltl.

◉ What hovering a stage shows you

Given:

let report = txns
    |> filter(is_recent)
    |> group_by(|t| t.category)
    |> map_entries(|cat, ts| (cat, ts |> map(.amount) |> sum()))
    |> sort_by(|(_, total)| 0 - total)

Hover on group_by:

Pipeline stage
in  : [Transaction]
out : Map<Category, [Transaction]>

Hover the call itself for the function signature.

You get the input type and the output type of *this stage*, not the function's generic signature. If your pipeline is wrong, the mismatch jumps out at the hover, not at the diagnostic several stages later.

◉ Why this was trivial

Lateralus' LSP backend has the type environment that produced the program. Pipeline stages are AST nodes (ast::PipeStage). The hover handler has two lookups:

let in_ty  = module |> inferred_input(stage)
let out_ty = module |> inferred_output(stage)

Both are already populated by the type checker for diagnostic purposes. The hover provider just re-uses them. The whole render_stage function is 12 lines.

◉ Literal hover does more than you'd expect

A bonus we shipped at the same time: numeric literals show their hex and binary form on hover.

let mask = 0x0F_FF

Hover:

4095: Int

hex: 0xFFF | bin: 0b111111111111

Saves a trip to the REPL every time you're wrangling a bitfield or a Unicode codepoint.

◉ Function-call hover with docstrings and return types

pub fn map(xs: [T], f: fn(T) -> U) -> [U]

Returns: [String]

Apply `f` to each element of `xs`, returning the results.
The mapping is lazy: no work is done until a consumer
requests the first element.

Standard fare, but note the explicit Returns: line showing the *inferred* return type at this call site — [String] rather than [U]. Generics get monomorphized in the tooltip, which matters when you're debugging why the next stage doesn't type-check.

◉ Editor integrations

The LSP ships with the Lateralus compiler. Your editor picks it up if it supports the protocol:

- VS Code — install lateralus-vscode - Neovim:LspInstall lateralus once the community metadata lands - Helix — add to languages.toml: [language.language-server] command = "lateralus"

All three call the same handle(HoverParams) -> HoverResult entry point.

◉ What's next in the LSP

Signature help, go-to-definition across crates, and a rename refactor that respects pipeline semantics (renaming a pipe stage's callee must update call sites but not inner closures). All small, all in-flight. None of them require new compiler infrastructure — the LSP is a *reader* of the existing type environment, not a second implementation of it.

The lesson repeats: if you put it in the compiler, the editor gets it for free.

Try it in the playground

Every snippet on this page runs in your browser. No install, no signup.

▶ Open Playground Star on GitHub