The tools/vscode-m2plus/ directory contains a VS Code extension that provides Modula-2/Modula-2+ language support via the mx --lsp language server.
Build and install the compiler:
cd /path/to/m2
cargo build --release
sudo cp target/release/mx /usr/local/bin/mxcd tools/vscode-m2plus
npm install
npm run compile
ln -s "$(pwd)" ~/.vscode/extensions/m2plusRestart VS Code. The extension activates when you open a .mod or .def file.
To run the extension without installing:
- Open
tools/vscode-m2plus/as a folder in VS Code. - Press
F5to launch an Extension Development Host window. - Open a Modula-2 project in the new window.
For continuous compilation during development:
cd tools/vscode-m2plus
npm run watchVS Code <--> vscode-languageclient <--> mx --lsp (stdio)
The extension is a thin client. All language intelligence (diagnostics, hover, completion, etc.) is provided by the mx LSP server. The extension:
- Launches
mx --lspas a child process - Forwards LSP messages over stdio
- Provides TextMate syntax grammar for basic highlighting
- Registers build/run/test/clean tasks
All settings are under the mx.* namespace. Configure in VS Code settings (JSON or UI).
| Setting | Type | Default | Description |
|---|---|---|---|
mx.serverPath |
string | "mx" |
Path to the mx binary. Set to full path if not on PATH. |
mx.m2plus |
boolean | true |
Pass --m2plus flag to the LSP server |
mx.includePaths |
string[] | [] |
Additional -I paths passed to the LSP server |
mx.diagnostics.debounceMs |
number | 250 |
Diagnostics debounce delay in milliseconds |
mx.m2dapPath |
string | "m2dap" |
Path to the m2dap debug adapter binary |
The debounce setting is passed to the server via initializationOptions. See LSP configuration for all server-side options.
Open the Command Palette (Cmd+Shift+P / Ctrl+Shift+P) and type "Modula-2+":
| Command | Description |
|---|---|
| Modula-2+: Restart Language Server | Stop and restart the mx --lsp process |
| Modula-2+: Reindex Workspace | Force the server to rebuild its workspace index. Displays file and symbol counts on completion. |
| Modula-2+: Initialize Project | Scaffold a new project (creates m2.toml, src/Main.mod, tests/Main.mod) |
| Modula-2+: Create Debug Configuration | Create .vscode/tasks.json and launch.json for debugging. Does not overwrite existing files. |
The extension provides four tasks via a TaskProvider. Access them from Terminal > Run Task, or the Command Palette > "Tasks: Run Task":
| Task | Command | Description |
|---|---|---|
| build | mx build |
Compile the project (requires m2.toml) |
| run | mx run |
Compile and run |
| test | mx test |
Compile and run tests |
| clean | mx clean |
Remove .mx/ build directory |
| init | mx init |
Initialize a new project |
The build task is assigned to the Build group; test to the Test group.
Tasks use the mx problem matcher, which parses error output in the format:
file:line:col: error: message
Errors and warnings from mx are highlighted in the editor and appear in the Problems panel.
The extension includes a TextMate grammar (syntaxes/modula2.tmLanguage.json) covering:
- Keywords (PIM4 + Modula-2+ extensions)
- Built-in types (
INTEGER,CARDINAL,REAL,BOOLEAN,CHAR, etc.) - Built-in functions (
ABS,CAP,ORD,CHR,HIGH,SIZE,NEW, etc.) - Comments (
(* ... *)with nesting) - Strings (single and double quoted)
- Numbers (decimal, hex
0AH, octal77B, character codes65C, floats)
The LSP server provides semantic tokens for richer highlighting (procedures, types, parameters, etc.) on top of the TextMate grammar.
The extension provides:
- Bracket matching:
(),[],{},(* *) - Auto-closing pairs: parentheses, brackets, braces, comments, strings
- Folding: based on block keywords (
PROCEDURE,MODULE,IF,BEGIN,RECORD, etc.) - Indentation: auto-indent after
BEGIN,THEN,ELSE,DO,OF, etc.
The LSP server automatically detects projects by looking for m2.toml manifests. When found, include paths and m2plus mode are read from the manifest, and no manual -I configuration is needed. See LSP configuration for details.
The extension supports two debugging modes:
- m2dap (recommended for LLVM backend) — a Modula-2 Debug Adapter Protocol server with M2-idiomatic variable display
- CodeLLDB (fallback) — direct LLDB integration via the CodeLLDB extension
- Build m2dap:
cd tools/m2dap && mx build(ensurem2dapis on PATH or setmx.m2dapPath) - Run Modula-2+: Create Debug Configuration from the Command Palette.
- Set a breakpoint by clicking the gutter next to a line number in a
.modfile. - Press
F5to build in debug mode and launch the debugger.
The generated launch.json includes both "Debug (m2dap)" and "Debug (lldb)" configurations. Select the one you want from the debug dropdown.
| Feature | m2dap | CodeLLDB |
|---|---|---|
| Backend | LLVM (--llvm -g) |
C (-g) or LLVM |
| Variable types | M2 names (BOOLEAN, INTEGER) |
C names (unsigned int, int) |
| Value formatting | TRUE/FALSE, NIL, CHR(N) |
Raw C values |
| Procedure names | Module.Proc |
Module_Proc |
| Requirements | m2dap binary | CodeLLDB extension |
The command creates four files in .vscode/:
| File | Purpose |
|---|---|
tasks.json |
Build task that runs mx build -g |
launch.json |
m2dap and CodeLLDB launch configs with the binary name read from m2.toml |
extensions.json |
Recommends the CodeLLDB extension |
settings.json |
Sets debug.allowBreakpointsEverywhere: true (required for .mod breakpoints) |
Existing files are not overwritten. Delete a file and re-run the command to regenerate it.
C backend (-g): Emits #line directives mapping generated C back to .mod source lines.
LLVM backend (--llvm -g): Emits native DWARF metadata with M2 type names. m2dap queries DWARF type info and formats variables idiomatically.
See debug builds for full compilation details.
- Breakpoints: Click the gutter to set breakpoints on any executable line
- Step over (
F10): Advance one Modula-2 statement at a time - Step into (
F11): Enter a procedure call - Step out (
Shift+F11): Return from the current procedure - Continue (
F5): Run to the next breakpoint - Variables: Local variables appear in the VARIABLES panel with M2 types (via m2dap)
- Watch: Add expressions to the WATCH panel to track values across steps
- Call stack: View the full call stack with demangled Modula-2 procedure names
- Debug console: Type LLDB commands directly (e.g.,
p myVar,frame variable)
# C backend
src/
Main.c # generated C (preserved for source mapping)
Main.o # object file (kept for DWARF debug info)
# LLVM backend
src/
Main.ll # generated LLVM IR
# Both backends
.mx/
bin/
<name> # executable with debug info
<name>.dSYM/ # macOS debug symbol bundle
Local variables map 1:1 to their Modula-2 names in both backends. Module-level variables appear as Module_varName. With m2dap, procedure names in stack traces are demangled: Module_ProcName → Module.ProcName.
Breakpoints not working (red dots don't appear):
- Ensure
debug.allowBreakpointsEverywhereistruein.vscode/settings.json - Re-run "Create Debug Configuration" to regenerate the settings file
Breakpoints appear but program doesn't stop:
- Run
mx clean && mx build -gto force a fresh debug build - Ensure the binary name in
launch.jsonmatches thenamefield inm2.toml - Check the Debug Console for error messages
m2dap not found:
- Build it:
cd tools/m2dap && mx build - Add to PATH or set
mx.m2dapPathin VS Code settings
Variables not showing in m2dap:
- Ensure you compiled with
--llvm -g(setbackend=llvminm2.toml) - The LLVM backend emits native DWARF; the C backend's
#linedirectives don't include variable metadata
Output doesn't appear when stepping:
- This should work automatically (stdout is unbuffered in debug mode)
- If using a non-debug build, rebuild with
mx build -g
Debugger shows assembly instead of source:
- The
.dSYMbundle may be missing — rebuild withmx clean && mx build -g - Ensure the
.modsource files haven't moved since the last build
- Output panel: Select "Modula-2+ Language Server" from the Output panel dropdown to see LSP server stderr output (errors, warnings, debug messages).
- Extension log: Check the Extension Host log for client-side issues.
- Verify
mxworks: runmx --version-jsonin a terminal. - If
mxis not on PATH, setmx.serverPathto the full path (e.g.,/usr/local/bin/mx). - Check the Output panel for error messages.
- Ensure the file extension is
.modor.def. - Check that the file is syntactically valid enough to parse (the server reports parse errors as diagnostics).
- Try
mx.diagnostics.debounceMs: 0for immediate feedback.
- Save all files, then run "Reindex Workspace" from the command palette.
- If the issue persists after reindexing, restart the language server.
- Verify the symlink exists:
ls -la ~/.vscode/extensions/m2plus - Ensure the extension compiled: check that
tools/vscode-m2plus/out/extension.jsexists. - Restart VS Code.
- Ensure an
m2.tomlmanifest exists in the workspace root. - Verify
mxis accessible (the task provider uses the configuredserverPath).