Editors and the language server
velt lsp is a Language Server Protocol server on stdin/stdout. It works in any editor with an
LSP client; the repository ships a Visual Studio Code extension in editors/vscode, published on the
Marketplace as andtii.velt.
Features
-
Diagnostics from the parser, the module loader and the type checker, as you type. Unsaved changes of other open files are taken into account. A standard library file opened from the toolchain (go to definition into
velt/fs, say) is checked as that std module, as it is when your program imports it. -
Formatting with
velt fmt.
Navigation
Document outline, go to definition (also through ns.x namespace imports and
re-exports, landing on the original declaration), find references, rename, document highlight
(reads and writes), workspace symbols (the workspace folders are indexed once and kept up to
date from the editor's file change events, or by checking modification times when the editor
does not send them).
Hover
Declaration signatures, including inferred throws types, and the inferred type of
locals and expressions. Below the signature comes the declaration's doc comment (a /** … */
block or /// lines right above it; see doc comments), rendered
as Markdown: the description, then @param, @returns, @throws, @example, @deprecated
and @see. Hovering a parameter shows its @param text. This works for your own code,
imports and the standard library (the prelude's methods and the fetch globals included):
functions (generic ones too), classes and their members (constructors, methods, getters,
static members, and #private members inside the class), interfaces, enum variants, and
the fields of object types and of intersections (A & B).
Completion
Locals, module items, imports, prelude items, keywords, and members after .
(also while the file doesn't parse). In JSX: tag names after < (the elements of the JSX
runtime's JSX.IntrinsicElements and the components in scope) and attribute names inside an
opening tag (the element's attributes or the component's props, minus those already written).
After </ the element still open there comes first. Tags are offered once the file contains
JSX that parses, which is when its JSX runtime loads. The editor shows the doc comment of the
selected item (JSX tags and attributes included), and items documented @deprecated are
struck through.
-
Imports, as in TypeScript editors (all of it also while the import doesn't parse yet):
- inside the braces of
import { … } from "velt/fs", completion offers the module's exports with their signatures, minus the names already listed (import type { … }: types only); - inside the quotes after
from(orimport("…")), completion offers module specifiers: the standard library's modules (velt/fs,velt/collections, with a line about each), files and folders next to the file for./and../, and the package's dependencies. Typing"or/there opens the list. Files are named as imports name them: without the extension, unless two files share a name (./dup.tsnext to./dup.vlt); - auto-import: typing a name that isn't imported yet offers the exports of std modules,
of the dependencies and of the package's other files (unsaved changes of open files
included) that start with what you typed, marked with the module (
readFile velt/fs). Accepting one adds it to the file'simport { … } fromthat module (in order, if its names are sorted), or adds that import after the other imports. A file is named so that the import loads it: with its extension when another file shares its name, and a folder module as./shapes/indexwhen a fileshapes.vltwould win over the folder. - the exports offered inside the braces and by auto-import show their doc comments, and
@deprecatedones are struck through.
- inside the braces of
-
JSX: go to definition, hover, references and rename on tags, opening and closing (
<Cardand</Card>→function Card), and on attributes (the attribute's or prop's declaration and type). -
Signature help while typing call arguments, with the function's description, return value and exceptions from its doc comment, and each parameter's
@paramtext. -
Deprecation: uses of a definition documented
@deprecatedget a hint with the reason, and editors show them struck through.
Inlay hints
Inferred types of const / let / for...of bindings and parameter names at
call sites. On declarations, what inference decided (memory model,
errors): throws E after a function without a throws clause that
can throw, modifies this after the signature of a method that modifies its receiver, and
modified before each parameter whose contents the function modifies.
Semantic highlighting
Types, functions, methods, parameters, properties and enum members;
let bindings carry a mutable modifier, and calls of functions and methods declared in
Velt source that modify their receiver or an argument a mutating modifier (built-in methods
such as push are not marked), which you can style.
-
Quick fixes for compiler errors: remove
mut, turn"a" + xs(a string and a value+does not convert, such as an array) into a template literal, replaceexport defaultwith a named export, addawaitorspawn(...)to a floating promise, addasyncto a method whose promise must carry its errors, and import a name the file uses without importing it ("ImportreadFilefromvelt/fs", one fix per module that exports it). A fix that applies in several places is also offered as "Fix all in file", and Fix all (source.fixAll, e.g. on save) applies every preferred fix of the file. -
Code shared with TypeScript: in a file inside one of the package's
tsCompatfolders, the findings ofvelt check --ts-compatappear as you type, with the rule as the diagnostic's code andvelt ts-compatas its source (see below).
Package manifests
package.vlt is read as data, the way velt reads it, not checked as a
program. Its diagnostics are exactly velt's; completion offers the fields valid at the cursor
and fixed values (native targets, true/false); hover explains each field. Saving a changed
package.vlt reinstalls the package's dependencies for the other open files
(package.vlt).
-
Live registry data in
package.vlt, from the package's registry: completion of versions (typing"after a dependency's name) and of package names independencies; hover on a dependency shows its newest and locked versions; a requirement no published version matches, or a package the registry doesn't have, is an error, a requirement that leaves out a newer version gets an informational note, and a quick fix moves it to^<newest>. The data is fetched in the background; an offline registry just adds nothing. A manifest'sregistryis asked only when it ishttps://or on this machine ($VELT_REGISTRYalways is), so opening a checkout never makes the editor contact a plain-HTTP host it names.
The server answers even when the program has errors, and a failing request never takes the server down.
.ts and .tsx modules (TypeScript files)
are part of the programs the server analyzes, and it asks the editor to report changes to them.
The workspace symbol index takes .vlt files anywhere in the workspace folders, but .ts and
.tsx files only in a package's src/ and tests/ (and open documents), so the TypeScript
frontend of a monorepo is not indexed as Velt. Whether the editor sends it .ts and .tsx documents to
analyze is up to the client: the VS Code extension leaves them to VS Code's TypeScript support.
Code shared with TypeScript
A file in a folder that package.vlt's tsCompat lists gets the TypeScript-compatibility
lint's findings next to the compiler's diagnostics: errors where tsc would reject the code or
JavaScript would run it differently, warnings where it may. Each finding's code is its rule
(velt-number-type) and its source is velt ts-compat; the message ends with what TypeScript
does and what to write. A finding with a mechanical replacement (f64 → number, bool →
boolean, a dropped suffix) offers it as a preferred quick fix, so Fix all applies it too.
- The findings follow your edits, also unsaved edits to
package.vlt'stsCompat; closingpackage.vltwithout saving goes back to the file on disk. Changes made outside the editor (package.vltsaved by another program, folders created or deleted) apply when the editor reports them (VS Code does). A foldertsCompatlists that doesn't exist is a warning inpackage.vlt. - The files in the folders are the ones
velt check --ts-compatfinds: not undernode_modules/,target/, hidden or symlinked directories, or in a nested package. - Like the command, the lint skips a file with errors of its own: fix those first.
- An import of a file outside the folders is the finding
outside-import. - Files outside the folders never get findings, and nothing changes for them.
- The VS Code extension sends only
.vltfiles to the server;.tsand.tsxfiles in the folders get findings in editors that send them, and fromvelt check --ts-compat.
Visual Studio Code
The extension adds syntax highlighting, starts velt lsp, and debugs Velt programs with F5
(Debugging). Above main the server shows ▶ Run | Debug, and above
each test of a test file (export function test_* in *.test.vlt) ▶ Run test | Debug test,
to a client that asks for these lenses ("initializationOptions": {"runLenses": true}, as the
VS Code extension does): they run the extension's velt.runFile, velt.debugFile,
velt.runTest and velt.debugTest commands, so other editors don't get them. A program
written as top-level statements, without main, gets no lens: run or debug it with the editor
title's buttons.
- Build or install
veltand put it onPATH, or setvelt.serverPathto the executable. - Install the extension from the Marketplace: search for Velt in the Extensions view, or run
code --install-extension andtii.velt. To build it from source instead, seeeditors/vscode/README.md.
| Setting | Default | Meaning |
|---|---|---|
velt.serverPath | velt | the velt executable (~ and ${workspaceFolder} are expanded); changing it restarts the server |
velt.trace.server | off | messages / verbose log the LSP traffic to the output channel |
velt.debug.engine | auto | the debugger extension F5 starts: codelldb, lldb-dap, cpptools, or auto (the first installed, in that order) |
With the default velt.serverPath, the velt on PATH is the launcher, which starts the
language server of the toolchain the first workspace folder's package pins
(velt toolchain). A window holding packages pinned to different
versions uses that one for all of them; open each in its own window to get its own.
The command Velt: Restart Language Server restarts it, for example after rebuilding velt.
Velt: Run File and Velt: Debug File run or debug the open file (also as buttons in the
editor title), and Velt: Generate launch.json writes .vscode/launch.json like
velt init --editor vscode. For debugging, see Debugging.
Not yet available: packaged support for other editors. Any editor that can start velt lsp as a language server works today.