Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Completion and signature help

The language server completes names everywhere a name can go, the way gopls does for Go: as you type, with the candidates whose types fit the place first.

What’s offered where.

  • In an expression: the values in scope where the cursor is (the function’s parameters, the values its statements bound before the cursor, a for loop’s variable inside its body, and after an if a value both branches bind), then the file’s dims, functions, models, records, imports and import python modules, then every built-in, the prelude’s included, and Train(seed=…), Eval, true, false and inf. Each has its kind, its type or signature as the detail (y: [b n D], dim Dh = D / H = 4, fn softmax(x: [... n] Logit over n, over: Axis[n]) -> [... n] Prob over n), and the hover’s Markdown, ## docs included, as its documentation, sent when the editor asks for it (completionItem/resolve). A function inserts a call with a tab stop for each parameter without a default, keyword ones by name: softmax(${1:x}, over=${2:n}); to an editor without snippets, just its name.
  • In a call’s arguments: after ( or ,, the keyword arguments not given yet (over=, weight=, eps=, a stub’s parameter names), which open the list again once chosen; after over=, exactly the axes of the operand (the piped value, or the first argument); for a Mode parameter, Train(seed=…) and Eval; for a mut parameter, mut.
  • After |>: the functions whose first parameter takes the piped value, as stages without it: softmax(over=${1|D,n,b|}) with the value’s axes to choose from, last first, or gelu alone. A function whose first parameter can’t take the value isn’t offered.
  • In a pattern of rearrange, reduce, repeat, einsum or split: before ->, the operand’s axes, all of them as one item first, then the next one; after it, the names the inputs have. After reduce(x, b n D -> b D, , the reductions.
  • In a type: where a type starts (after x: , -> , as or to ), a shape, the file’s models, records, logs records, shape aliases and domains, and Mode, int, float, bool, str, Grad[M], AdamW[M], SGD[M], Dataset[E], Schedule, LogSink, Axis[n], Axes[n] and Size[k]; after as or to, the value’s own type first. Inside […], the axes the signature names, the dims and shape aliases, and ...; after the shape, the domains and dtypes; after over, the shape’s axes; inside Index[, Axis[, Axes[ and Size[, axes and dims, and inside Grad[, AdamW[ and SGD[, models.
  • After a .: a model value’s fields and functions (completion after m.); a batch’s fields with their types (field img: [b D]), a logs record’s metrics, a gradient’s params; an imported file’s declarations (attention.); init after a model’s name (Mlp.init(seed=…)); and a Python module’s names (torch., torch.nn.functional.), with their signatures and docstrings.
  • After import : the .ss files of the file’s folder, the workspace’s folders and the search path, as dotted paths a part at a time (models. then mlp), the standard library and installed packages with .ss files (Packages), and python. After import python : the Python modules on the interpreter’s path, and a package’s submodules after torch..
  • Keywords where they go: at the top level, declarations as snippets (fn name(x: T) -> T: with its block, model Name: with a param line, data, logs, dim, domain, extern fn, export fn, import, import python, devices); at the start of a statement, for i in 0..n:, if cond:, else: and return, with the names; after a value and a space, as, to, and, or, for, if and else, and in after for x .
  • Names of other files: once a letter is typed, the declarations of the workspace’s other .ss files, the standard library’s and installed packages’ that the file doesn’t import yet, qualified (metrics.accuracy), with an edit that adds import metrics after the file’s imports, or before its first declaration. They come after the names in scope, marked metrics · adds its import. A file that imports this one isn’t offered, which would be a cycle.

Ranking. The place’s expected type comes from the parameter of the call the argument is for (the overload its first argument’s model picks), the function’s return type after return, the other operand of an operator (one whose axes are the same or fewer broadcasts; for @, a matrix whose first axis is the left’s last), the parameter after name=, and the piped value for a stage’s first parameter. A candidate fits exactly if it has the same shape with the same dims (a [b n D] for x: [b n D]), the same model, record or mode; it fits if its rank and dims agree with a generic or ... shape; it doesn’t if the call would be an error. Exact fits come first, and the first is preselected; then those that fit; then the rest, values in scope before the file’s declarations, built-ins, keywords and other files’ names; and last those that don’t fit. A function’s fit is its return type’s. Within each, the editor’s own matching orders them, and the server’s fuzzy matching decides what’s in the list: the letters typed must appear in the name in order, ignoring case, and matches at the starts of words (lnorm for layer_norm) and runs of letters score higher. A list is marked incomplete where typing more brings other items (other files’ names, a Python module still being listed, more than 300 items), so the editor asks again.

Edits. An item replaces the word typed so far, from its start to the cursor, or the whole word around the cursor if the editor supports both; after ., = or \, the word after it.

While the file is broken. A line being typed rarely parses. The names in scope come from the text with the cursor’s line left out, parsed, so the statements before it count, and their types from the last analysis of the file without syntax errors, matched by name and place; the type of an expression on the line itself, as pool(y) + 1 in mean(pool(y) + 1, over=, comes from checking a copy of the file cut after it, with its brackets closed. On a file of 2,400 lines, a completion takes about 3 ms in a release build and 12 ms in a debug build, 22 ms over the protocol.

Python. The modules on the path come from the interpreter that go to definition uses, listed with pkgutil without importing anything. A module’s names come from its source, read with ast, when it’s one of yours (under the file’s folder or the workspace’s), so completing a name never runs your code; others, such as torch and the standard library, are imported and asked with dir() and inspect, once per interpreter for the session.

Signature help (textDocument/signatureHelp, after (, , and =) shows the signature of the call being typed, with the parameter being typed highlighted and its type: the positional one for the argument, the one named before =, or once the positional arguments are given, the keyword one still missing, as over. In a pipe stage, the first parameter is the piped value. Functions of the file, overloads (all of them, with the one the first argument’s model picks active), generic functions, imported files’ functions, built-ins with their documentation, extern fns, Mlp.init and Python functions (their inspect or ast signature) all have it.

Trigger characters. The list opens by itself after .; after [ in a type; after ( and , where the argument’s type is known; after = that follows a keyword argument’s name; after a space that follows |>, import or import python; and after \ and { in math in comments. Anywhere else these characters open nothing.