Mutant Language Tools
The security family. These report a program that compiles and runs, which is what makes them worth having: nothing else in the toolchain objects.
VS Code language support for Mutant.
Features#
- A dedicated Mutant language server (
mlsp) over stdio (gopls-style), with prebuilt binaries bundled for Windows, Linux, and macOS. - Diagnostics: missing/redundant semicolons, unused / undefined / duplicate declarations, deep-nesting hints, unreachable code, and OS-aware platform-support warnings — flags a builtin that isn't supported on the operating system the server is running on. Each rule's severity is configurable, and several carry quick fixes.
- Canonical formatting (format-on-save by default): four-space indentation and strict semicolons, emitted from the AST so the same source always formats to the same bytes.
- Hover & completion showing each builtin's signature, summary, capability category, and any platform constraint.
- Semantic highlighting, go-to-definition, references, rename, document and workspace symbols, and signature help. Rename reaches files you have not opened.
- Call hierarchy (Shift+Alt+H, or "Peek Call Hierarchy"): who calls a
function and what it calls, across files. A call into another module --
stats.mean(...)-- is followed to the module that declares it, and a caller in a file nobody has opened still appears. - Debugging (press F5 on a
.mut): breakpoints with hit counts, step over/into/out, the call stack, and Arguments / Locals / Globals with arrays, hashes and structs expandable. The CLI is the debug adapter -- the extension runsmutant debugand speaks the Debug Adapter Protocol to it. Conditional breakpoints and watch expressions beyond a plain variable name are deliberately not supported; seedocs/DEBUGGING.mdfor why. - Syntax highlighting via a TextMate grammar.
Configuration#
mutant.languageServer.path: executable/command path for Mutant LSP (default: empty string; auto-detect packaged binary first, then workspace)mutant.languageServer.args: additional CLI args for the servermutant.lint.rules.duplicateTopLevelDeclaration.severity: severity for the duplicate declaration lint rule (error,warning,information,hint,off)mutant.lint.rules.unusedDeclaration.severity: severity for the unused declaration lint rule (error,warning,information,hint,off)mutant.lint.rules.unusedImport.severity: severity for an import that brings nothing this file uses (error,warning,information,hint,off;informationby default, because an imported module's top-level statements run whether or not its namespace is used). An import brings more than its namespace: a struct name, an enum name and a macro all arrive bare, soimport a "shapes.mut";abovePoint{x: 1}is a used import even though the wordanever appears again.mutant.lint.rules.undefinedDeclaration.severity: severity for the undefined identifier lint rule (error,warning,information,hint,off)mutant.lint.rules.nestingComplexity.severity: severity for deep nesting diagnostics in function bodies (error,warning,information,hint,off)mutant.lint.rules.semicolon.severity: severity for missing/redundant semicolon diagnostics (each with a quick fix)mutant.lint.rules.unreachableCode.severity: severity for unreachable-code diagnostics (statements after an unconditionalreturn/break/continue, and amatcharm written after_)mutant.lint.rules.platformSupport.severity: severity for OS-aware platform-support warnings (a builtin unsupported on the host OS)mutant.lint.rules.builtinArity.severity: severity for wrong-argument-count calls to fixed-arity builtinsmutant.lint.rules.builtinArgType.severity: severity for arguments of a type a builtin parameter cannot acceptmutant.lint.rules.builtinArgChoice.severity: severity for a string literal that is not one of the words a builtin parameter takes --db_bfs's direction,ledger_path's cost model -- which the builtin refuses when it runsmutant.lint.rules.builtinSingleReturn.severity: severity for binding several names from a builtin that returns one valuemutant.lint.rules.builtinDeprecated.severity: severity for calling a builtin that is kept only for compatibility; names its replacement. Defaults tohint, because the call still works.mutant.lint.rules.builtinPairReturn.severity: severity for binding one name from a builtin that returns a(value, err)pair, which leaves the name holding the pair rather than the valuemutant.lint.rules.spawnGlobalWrite.severity: severity for aspawn/pmap/peachcallback writing a global, which lands in that worker's copy and is lost when the callback finishesmutant.lint.rules.unclosedResource.severity: severity for opening a handle (ntfs_open,zip_open,chan_new,net_connect, ...) that nothing in the same scope closes. Quiet whenever the handle escapes the scope, the closer is named in it, or the resource is deliberately held for the program's lifemutant.lint.rules.uncheckedError.severity: severity for binding the error half of a(value, err)builtin and never reading it before the name is rebound or the scope ends. Quiet when the error is read in any way, when the failure is caught through the value instead, or when_is bound to say the failure is deliberately ignoredmutant.lint.rules.matchExhaustiveness.severity: severity for amatchwhose arms are variants of one enum declared in the same file, with no_arm and a variant left out. An unmatched subject raises at run time rather than yielding null, so a variant added to an enum leaves every existing match over it one arm short. Quiet unless it is certain: a literal pattern, two different enums, an imported enum, a_arm, or a name that is both an enum and a binding all suppress itmutant.lint.rules.assignmentTarget.severity: severity for an assignment target the compiler refuses -- one with no variable under it ([1, 2][0] = 9), or an index before the last one that is not a name or a literal and would therefore be evaluated more than once. Both are build failures, so this reports them where they are written. Defaults toerror, matching the build
The security family. These report a program that compiles and runs, which is what makes them worth having: nothing else in the toolchain objects.
mutant.lint.rules.tlsVerificationDisabled.severity: severity for turning off certificate verification, or accepting a TLS version that is no longer safemutant.lint.rules.weakCrypto.severity: severity for reaching for a broken or obsolete algorithm where a current one takes the same callmutant.lint.rules.hardcodedSecret.severity: severity for a credential written into the source, where it outlives the program and travels with the filemutant.lint.rules.commandInjection.severity: severity for building a shell command out of a value the program did not choosemutant.lint.rules.pathTraversal.severity: severity for building a filesystem path out of a value the program did not choosemutant.lint.rules.unboundedResource.severity: severity for reading something whose size the program does not control into memory with no ceiling on itmutant.lint.rules.classifiedPlaintext.severity: severity for handing plaintext read out of a classified record to a builtin that refuses it --putln,fs_write,http_postand the rest -- before the run time doesmutant.lint.rules.evidenceMutation.severity: severity for writing to evidence a case opened read-only -- the one rule here about the report rather than the machine, because an altered artifact is an artifact that proves nothing
The case family. Each reports a call the case, record and ledger builtins refuse when the program runs -- often after it has asked for a passphrase -- in the run time's own words, from lists the run time exports and its tests hold to the code.
mutant.lint.rules.roleLiteral.severity: severity for a rolecase_openorledger_openrefuses in its options: a word that is not a role, a recipient's role such aslegal, orunassertedmutant.lint.rules.filteredLedgerHandle.severity: severity for passing the handleledger_under_viewreturns to a builtin that does not read under a view -- any writer, and every read but the eight that filtermutant.lint.rules.secretOption.severity: severity for an option namedpassphrase,password,secretorkeyhanded to a builtin that asks for its passphrase at the terminal and refuses one as an argumentmutant.lint.rules.lifecycleState.severity: severity for askingcase_transitionfor a state it never moves a case to --in_review,concluded,retainedorregistered-- naming the builtin that makes the move
One is about when code runs rather than what it does.
mutant.lint.rules.macroSafety.severity: severity for a builtin a macro body calls that the compiler refuses while expanding it -- one that reads or writes a file, reaches the network, starts a process, draws entropy, reads the clock or decodes cryptographic material -- in the expander's own words. Nothing insidequote(...)is reported, since that is the source the macro emits; anunquote(...)inside it ismutant.strictFormatting: master on/off switch for canonical formatting (trueby default)mutant.format.onType.enabled: opt-in on-type formatting while typing (falseby default)
By default, Mutant files use editor.formatOnSave: true via extension
configuration defaults, so canonical style is enforced when saving. On-type
formatting is off unless mutant.format.onType.enabled is set, and then it runs
on ; and }. The extension writes nothing to your settings to arrange this:
it turns editor.formatOnType on for Mutant files as a default and asks the
language server only while mutant.format.onType.enabled is set. Your own
"[mutant]": { "editor.formatOnType": false } still turns it off. Earlier
versions wrote an editor.formatOnType line into the open folder's workspace
settings; you may delete it.
When mutant.languageServer.path is empty, the extension first tries packaged
platform binaries in bin/:
mlsp-windows-amd64.exemlsp-windows-arm64.exemlsp-linux-amd64mlsp-linux-arm64mlsp-darwin-amd64mlsp-darwin-arm64
If no packaged binary matches the current platform/arch, it then checks common
local paths such as mlsp(.exe) in the workspace and parent folder before
falling back to command lookup from PATH.
Setting a lint rule severity to off suppresses that rule.
Local Development#
npm installnpm run compile- Build the language server binary when needed:
../lsp/build.ps1 -HostOnly
../lsp/build.sh --host-only
This produces mlsp artifacts in lsp/dist/ for local extension use.
- Press
F5in VS Code to launch the extension development host.
For publishing with bundled binaries:
- Build all LSP targets:
../lsp/build.ps1
../lsp/build.sh
- Stage binaries into extension
bin/:
npm run prepare:lsp-bins
- Package/publish the extension.
vscode:prepublishalready runsprepare:lsp-binsautomatically.
Debug configuration is included in .vscode/launch.json and build tasks are in
.vscode/tasks.json.
If your LSP binary is not on PATH, set mutant.languageServer.path to an
absolute path.
Troubleshooting#
For a deeper walkthrough of the language server and extension (PATH setup, settings, logs, restart flow), see ../docs/LSP_EXTENSION_ONBOARDING_60_MIN.md and ../docs/LSP_EXTENSION_LLD.md.
Quick commands:
Mutant: Show LSP StatusMutant: Show LSP LogsMutant: Copy LSP LogsMutant: Restart LSP
Key settings:
mutant.languageServer.pathmutant.languageServer.args
Smoke Test Workflow#
- Run the VS Code task
smoke: lsp features. - Press
F5to start the extension development host. - Run command
Mutant: Open Smoke File. - Run command
Mutant: Run LSP Smoke Checks. - Run command
Mutant: Show LSP Statusto verify server state. - Run command
Mutant: Show LSP Logsto inspect Mutant-only server logs. - Run command
Mutant: Copy LSP Logsto copy recent logs for bug reports. - If needed, run
Mutant: Restart LSPto recover from startup failures. - Verify:
- Linting: warning on duplicate top-level
answerdeclaration. - Semantic colors: keywords, numbers, strings, enum/type identifiers receive semantic highlighting.
- Formatting: run
Format Documentand confirm trailing spaces are removed and a single final newline remains.
License#
This extension is licensed under GNU AGPL v3.0 only.
See LICENSE.