OpenJML for Visual Studio Code

JML specification type-checking and extended static checking (ESC) for Java, powered by the OpenJML tool.

Installation

  1. Install VSCode, and have the executable findable on PATH or in a standard Applications folder
  2. Download a current OpenJML release into a clean folder.
  3. Unzip the release .zip file in that folder.
  4. On a Mac, you may need to run ./mac-setup to clear the security check. You can check that the server runs ok on your platform by running ./openjml-lsp --version.
  5. Run ./install-vscode-extension from that folder (do not move the script or any other contents of the installation folder).

The script installs the VS Code extension and sets openjml.serverPath automatically. If the script fails it emits instructions for a manual workaround.

Requirements

The extension requires an OpenJML installation on the local machine. openjml.serverPath must point to the OpenJML installation folder or the openjml-lsp launcher script from the OpenJML distribution; the installer sets this automatically.

Version requirements:

Features

Commands

Command Palette title Default keybinding
openjml.checkJml OpenJML: Check JML
openjml.runEsc OpenJML: Run ESC Cmd+E / Ctrl+E
openjml.runEscForMethod OpenJML: Run ESC for Method Cmd+Alt+E / Ctrl+Alt+E
openjml.saveAndRunEsc OpenJML: Save and Run ESC Ctrl+Cmd+E / Ctrl+Shift+E
openjml.runEscSplitByFile OpenJML: Run ESC Split by File
openjml.runEscSplitByMethod OpenJML: Run ESC Split by Method
openjml.runEscDir OpenJML: Run ESC on Project
openjml.runRac OpenJML: Compile RAC
openjml.indexProject OpenJML: Index Project
openjml.clearMarkers OpenJML: Clear Markers
openjml.clearMarkersSelected OpenJML: Clear Markers for Selection
openjml.clearAndReindex OpenJML: Clear Caches and Reindex
openjml.cancelEsc OpenJML: Cancel ESC
openjml.abortMethodProof OpenJML: Abort Method Proof

Run ESC for Method uses the code lens at or above the cursor to identify the method and its fully-qualified name, so it works correctly for methods in secondary classes and nested classes.

Run ESC Split by File / Split by Method launch parallel ESC invocations — one per file or one per method — to take advantage of concurrency.

Save and Run ESC saves the file first, then runs ESC. Useful when dirtyFileAction is set to run, which means the default behavior is to check the editor content.

Index Project runs --check on all source files in the workspace, populating the declaration index used by various navigation command.

Clear Markers for Selection clears OpenJML diagnostics for the file(s) or folder(s) selected in the Explorer. Clear Markers clears all OpenJML diagnostics workspace-wide.

Status Bar

While ESC tasks are running, a status bar item appears at the bottom of the window:

OpenJML N ESC task(s) running …

Clicking the item invokes Cancel ESC, stopping all in-progress proofs.

Syntax Highlighting

OpenJML uses LSP semantic tokens to color JML constructs. Tokens are emitted for the following categories, all using names from the standard LSP token vocabulary so that VS Code color themes apply automatically:

Category Examples
keyword requires, ensures, invariant, ghost, model, also, behavior, loop_invariant, …
modifier pure, spec_public, spec_protected, nullable, non_null, strictly_pure, helper, …
function \result, \old, \forall, \exists, \nothing, \fresh, \typeof, …
type int, boolean, \bigint, \real, \locset, …
operator ==>, <==, <==>, <:, +, -, ==, …
string String literals and text blocks inside JML expressions
number Numeric and character literals inside JML expressions
macro Reserved; not currently emitted
decorator @Override, @NonNull, and other annotations
comment Reserved; not currently emitted
method, property, variable, parameter Resolved symbols inside JML expressions (AST strategy only) (property = Java class field)
class, interface, enum, struct Class, interface, enum, and record type references in JML expressions (AST strategy only; struct = Java record)
namespace Package-qualified name prefixes in JML expressions (AST strategy only)
typeParameter Type parameter references in JML expressions (AST strategy only)
enumMember Enum constant references in JML expressions (AST strategy only)

In addition, the declaration site of a symbol (e.g. a ghost field declaration) receives the declaration modifier, final fields and locals receive readonly, static members receive static, deprecated symbols receive deprecated, abstract methods and classes receive abstract, and type references whose fully-qualified name begins with java. or javax. receive defaultLibrary.

Modes

preserve Java coloring (default, openjml.syntaxColoringScope; recommended with Red Hat Java Extension installed): tokens are emitted only inside JML annotation context. Java constructs outside JML annotations are left to the Red Hat Java Extension.

overwrite Java coloring (set openjml.syntaxColoringScope to overwrite Java coloring): tokens are emitted for all Java and JML constructs throughout the file, replacing whatever the Java language server produced. Use when no other Java extension is active.

Strategies

ast (default): uses the attributed AST after the first --check completes. Precise — no false positives for Java identifiers named requires, ensures, etc.

regex: always uses regex-based line scanning instead of the AST. Available as a fallback but may incorrectly color Java identifiers that share a name with a JML keyword.

Color adaptation

For .jml files: semantic token colors follow the active color theme automatically. To override individual token category colors, add a semanticTokenColorCustomizations entry to your VS Code settings:

"editor.semanticTokenColorCustomizations": {
    "rules": {
        "keyword": "#569cd6",
        "modifier": "#c586c0",
        "function": "#4ec9b0"
    }
}

The names match the token type strings listed in the table above.

For .java files: the Red Hat Java Language Support extension registers its semantic token provider asynchronously and can override OpenJML’s tokens for JML content inside //@ … and /*@ … */ lines, treating those ranges as plain comments. To ensure JML constructs remain distinctly colored, the extension also applies text editor decorations for the key JML token types (keyword, modifier, function, method, type). Decorations take unconditional precedence over semantic tokens in VS Code’s rendering pipeline.

The decoration colors are fixed approximations of the VS Code Dark+/Light+ defaults for each token type; they do not follow editor.semanticTokenColorCustomizations overrides and do not adapt to custom color themes that assign non-standard colors to those token types. The VS Code extension API does not expose the active theme’s semantic token colors (only workbench color registry IDs are accessible via ThemeColor), so exact theme-adaptive coloring is not achievable with the current API.

Settings

Setting Default Description
openjml.serverPath `` Path to the OpenJML installation folder or a openjml-lsp launcher script. Leave empty to auto-discover it in the OpenJML installation. Requires OpenJML to be installed separately.
openjml.checkTriggerOn edit When to run --check: edit (on every change), save (only on file save), or manual (never automatic; use the OpenJML: Check JML command).
openjml.escTriggerOn manual When to run --esc: manual (only via command) or save (on file save).
openjml.dirtyFileAction ask What to do when ESC is invoked on a file with unsaved changes: ask (prompt each time), save (always save silently first), or run (always run on the editor content).
openjml.toolOptions [] Project-independent OpenJML command-line options prepended to every tool invocation. Use ["--properties", "/path/to/file"] to pass a properties file — one idiomatic way to supply a sequence of options; alternatively put individual flags directly in this array. Project-dependent settings (source path, class path, specs path) belong in the named settings below.
openjml.specsPath `` User additions prepended to the OpenJML specspath. If empty, OpenJML uses its default (sourcepath plus built-in system library specifications). If non-empty, this value is prepended to the assembled sourcepath and the result is passed as --specs-path.
openjml.sourcePath `` User additions prepended to the server-assembled sourcepath (which already includes the workspace folder roots).
openjml.classPath `` User additions prepended to the server-assembled classpath (which already includes the Java output directory).
openjml.escEngine fresh ESC execution engine: fresh (default — spawns a fresh OpenJML compilation context for each ESC task) or concurrent (share OpenJDK compilation contexts, which shares parsing and typechecking effort).
openjml.escThreads 5 Maximum number of concurrent ESC tasks.
openjml.syntaxColoringScope preserve Java coloring How OpenJML’s semantic tokens interact with Java coloring: preserve Java coloring (default) — emit tokens only inside JML annotation context, leaving Java code to the Java language server; overwrite Java coloring — emit tokens for all Java and JML constructs, replacing whatever the Java language server produced.
openjml.syntaxColoringStrategy ast JML syntax coloring strategy: ast (uses the attributed AST when available — precise, no false positives) or regex (always uses regex line scanning — may color Java identifiers that share a name with a JML keyword).
openjml.useIntegratedOutline true When true, the Outline panel shows all Java and JML symbols together. When false, only JML-specific symbols are shown (complementing the Red Hat Java Extension’s outline).
openjml.javaMode jml-only Controls which inlay hints are shown. jml-only (default) — suppress var-type hints for plain Java locals (assumes the Red Hat Java Extension shows those); JML ghost/model var hints are always shown. full — show var-type hints for all locals.
openjml.client vscode-java Client identifier sent to the server. Controls javaMode defaults. Leave as vscode-java when the Red Hat Java Extension is active.
openjml.racOutputDir `` Output directory for RAC-compiled class files (-d). Relative paths are resolved against the workspace root. Leave empty to use the Java output directory (java.project.outputPath).

Paths

The classpath, sourcepath, and specspath are crucial quantities for OpenJML. The server assembles them automatically from the workspace:

Path values may contain environment-variable references of the form $VARNAME, ${VARNAME}, or $(VARNAME); the server substitutes the variable’s value before passing the result to OpenJML.

Co-existing with the Red Hat Java Extension

This extension is designed to work alongside the Red Hat Language Support for Java extension. With the default settings, OpenJML only adds JML-specific capabilities — type-check diagnostics, ESC code lenses, JML completions, JML hover, JML syntax coloring — and defers Java navigation, outline, and plain-Java var inlay hints to the Red Hat extension (openjml.javaMode: "jml-only"). Java syntax coloring is preserved by default via openjml.syntaxColoringScope: "preserve Java coloring". The JML semantic tokens are merged additively on top of whatever the Red Hat extension produces, so JML keywords and modifiers are colored without disturbing Java coloring.

Warning: The Red Hat Java formatter normalizes // comment lines by inserting a space after //, changing //@ requires ... to // @ requires ..., which silently disables all JML annotations on those lines. The extension warns once per workspace on activation and offers to disable java.format.enabled. Manual formatting (Shift+Alt+F) is still available; configure a formatter profile that excludes comment reformatting if you need both.

Outline duplication: With useIntegratedOutline: true (the default), the Outline panel receives declarations from both the Red Hat Java extension (Java symbols) and the OpenJML extension (Java + JML symbols together). This produces duplicate Java entries. To remove the duplicates either fold/close the Java contribution in the Outline panel, or set openjml.useIntegratedOutline to false so OpenJML shows only JML-specific symbols and defers Java symbols entirely to the Red Hat extension.