Fun (FLS) for VS Code
Official VS Code support for the Fun language. This extension provides syntax highlighting and connects VS Code to the fls language server.
Features
- Syntax highlighting for
.fn files
- Language Server support via
fls (hover, completion, diagnostics, go-to-definition, formatting, etc. as provided by fls)
- ▶ Run and ⚙ Debug code lenses above every
fun main( for single-action run and debug workflows
- Full debugger experience: breakpoints on
.fn files, call stack, variables panel with Fun type names
- Output channel: Fun Language Server (useful for debugging startup issues)
- Bundled color theme: Fun Web (matches the reference website palette)
Snippets
fls offers these directly in completion, snippet-aware clients included (works the same in any other editor fls is wired into, not just VS Code). Two kinds:
Type a real keyword to write its whole shape — accepting fun writes a full declaration with the places to fill marked, not just the bare word:
fun, compound, enum, shape, impl, als, fit, if, elif, else, for, test, sequential, fuzz, asm, use, let, const, ret, defer, assert, panic, allow, expect, async, await
Short triggers for a common multi-line shape, offered alongside ordinary completions:
| Trigger |
Writes |
main |
a program's entry point |
mainio |
an entry point that prints |
fore |
for each item |
fori |
for each item, with its index |
forw |
for while a condition holds |
impls |
implement a shape for a type |
iferr |
return early when a Result failed |
ifnone |
return early when an Option is empty |
fitr |
fit over a Result |
fito |
fit over an Option |
vecnew |
a Vec, initialized and freed |
mapnew |
a Map, initialized and freed |
Requirements
You need the Fun tooling installed:
fls language server
fun compiler (required for Run / Debug)
For the Debug feature you also need:
- A native debugger extension — CodeLLDB (recommended on macOS/Linux/Windows) or C/C++ (cpptools)
- A system C compiler:
cc/clang/gcc on macOS/Linux, clang-cl or cl.exe on Windows
By default the extension will try, in order:
- Workspace-local binaries:
fun-out/bin/fls(.exe) and fun-out/bin/fun(.exe)
- Your system
PATH (e.g. fls, fun)
Getting Started
- Install the extension.
- Open a
.fn file.
- If the server doesn’t start, open View → Output and select Fun Language Server.
For local development builds, the extension prefers workspace binaries from fun-out/bin before falling back to executables on PATH.
Optional: set Preferences → Theme → Color Theme → Fun Web to use the same code palette as the website.
Settings
These settings live under Settings → Extensions → Fun:
fun.fls.path
- Path to the
fls executable.
- Default:
fls (falls back to fun-out/bin/fls when available)
fun.fls.funPath
- Optional path to the
fun executable.
- When set to a valid executable, it is passed to
fls via the FLS_FUN_PATH environment variable.
fun.fls.stdlibDir
- Optional stdlib root (folder containing
std/).
- Passed to
fls via FUN_STDLIB_DIR and takes precedence over derived paths.
fun.debugger.type
- Debugger to use for ⚙ Debug. One of
lldb (CodeLLDB) or cppdbg (cpptools).
- Leave empty for auto-detection.
Notes:
- Settings support
${workspaceFolder} / ${workspaceRoot}.
- On Windows,
%VAR% environment variables inside paths are expanded (best-effort).
Run and Debug
Every .fn file that defines fun main( shows two buttons above it:
- ▶ Run — compiles and runs the file in an integrated terminal (equivalent to
fun -in file.fn).
- ⚙ Debug — compiles with debug info, then launches the native debugger.
Tests are not lensed: they get VS Code's own Testing integration, described next.
Tests and code coverage
Every test "..." block is a real test in VS Code's Testing view (the flask icon), and VS Code draws its own green play button in the gutter beside each one. The view lists your tests by folder, file and test, and updates as you type.
- Run a single test, a whole file or a folder from the gutter, the Testing view, or the context menu. Results appear per test with the assertion message on a failure, and the full output in the test results panel.
- Coverage is the second run profile (the play button with a shield, or Run with Coverage). It runs
fun test ... -cover, then VS Code shades the lines of your source green (ran) and red (never ran) in the editor, shows a percentage per file and in total in the Testing view, and a Test Coverage view lists every file. Toggle Inline Coverage (Test: Show Inline Coverage) turns the shading on and off. Only project files are measured: not the standard library, not [deps] dependencies, not fixtures directories.
- Debug runs one test under the native debugger, the same as ▶ Debug for a program.
Tests run one at a time inside a file so a failed assertion's message is attached to the right test. A file's tests are compiled once per run, and up to three files run at a time. A [coverage] exclude list in fun.toml (see the compiler docs) is honoured.
Only lines that hold a statement are counted, so a blank line, a comment or a closing brace is never red. A test's own body is not measured, only the code it exercises.
Every fuzz "..." block gets a ▶ Fuzz button, which runs that one target (fun -in file.fn -fuzz -fuzz-target "target name"). There's no Debug variant for fuzzing: the fuzzing engine's own driver takes over the process and runs indefinitely, so attaching a debugger up front isn't useful the way it is for a single deterministic test — reproduce a crash fuzzing found from its saved input instead, then debug that.
Debug experience
Breakpoints are set directly on .fn source lines. When a breakpoint is hit:
- Call stack shows Fun function names and
.fn file/line numbers.
- Variables panel shows variable names from your Fun code with Fun type names (
num, str, flag, dec, raw*, etc.) instead of C equivalents.
- Watch and Debug Console expressions also display Fun types.
- Internal C boilerplate frames (e.g. async helpers) are marked as secondary and collapsed by default.
Debugging async functions and fork
await expressions (and fork, which spawns an async function the same way) lower to a chain of C trampoline functions (__fun_async_call_, __fun_async_spawn_, __fun_async_entry_, then the real function body). The actual function call goes through pthread_create (or CreateThread on Windows) — which is opaque C runtime code the debugger cannot step through at the Fun source level.
This means step into on an await/fork line will not automatically land inside the called async function. Instead the debugger follows the C runtime path:
await to_consumer.send_async(out); // step into → enters trampoline C code
fork worker(&wg); // same story
Every line of the trampoline itself is still correctly attributed back to the async function's own declaration line (so stepping through it doesn't show garbage or unrelated source), but it's still generated C, not your function's own body.
The right way to debug async calls:
- Set a breakpoint inside the async function you want to inspect (e.g. a line inside
send_async in channel.fn, or inside the function fork spawns). The debugger will break there when the spawned thread executes it, and you can step normally from that point.
- Alternatively, set a breakpoint on the line after the
await to resume once the call has returned.
Step-over (F10) on an await line works correctly — it blocks until the async call completes and advances to the next Fun source line.
How it works
- The compiler is invoked with
-g which embeds #line N "file.fn" directives in the generated C. These tell the C compiler to attribute all DWARF symbols to the original Fun source locations.
- The C file is compiled with
-g (or /Zi on Windows) to produce a native binary with full DWARF/CodeView debug info.
- VS Code launches the native debugger (CodeLLDB or cpptools) against the binary. Because DWARF points to
.fn files, the debugger shows Fun source natively — no extra mapping layer.
- A DAP message tracker intercepts variable type strings and remaps them from C types back to Fun types in real time.
- Temp files (
.c and compiled binary) are cleaned up automatically when the debug session ends.
Debugger auto-detection
The extension picks the debugger in this order:
fun.debugger.type setting (if set explicitly)
- CodeLLDB (
vadimcn.vscode-lldb) if installed
- cpptools (
ms-vscode.cpptools) if installed
- CodeLLDB on macOS/Linux, cpptools on Windows (best-effort fallback)
On Windows the extension tries clang-cl first (produces DWARF-compatible debug info for CodeLLDB), then cl.exe (MSVC, works with cpptools).
Commands
Open the Command Palette and run:
- Fun: Restart Language Server
- Fun: Show Language Server Output
- Fun: Run — run the current file
- Fun: Debug — debug the current file
The per-test/per-fuzz-target commands (Run Test, Debug Test, Fuzz) aren't in the Command Palette — they need a specific test/fuzz block's name, so they only appear as codelenses above each test/fuzz block (see Run and Debug).
Troubleshooting
No hover / completions / diagnostics
- Verify
fls is found:
- Either ensure it’s on
PATH, or set fun.fls.path to the full path.
- If you’re building Fun from source, ensure
fun-out/bin exists (or point settings at the built executables).
Standard library isn’t found
- The extension attempts to derive
FUN_STDLIB_DIR from the directory containing fls / fun.
- Check the Fun Language Server output for the resolved paths.
Developing This Extension
From this folder (editors/vscode):
npm install
npm run compile
- Press
F5 in VS Code (Extension Development Host)
Packaging a .vsix:
npm install
npm run package
code --install-extension fun-language-*.vsix
See CONTRIBUTING.md for repo-wide contribution guidelines.