Bea for VS Code
Language support for Bea, the
ergonomic language that transpiles to Gleam.
- Syntax highlighting for
.bea files, and for ```bea blocks
in Markdown.
- The Bea language server (
bea lsp): diagnostics (Bea syntax errors
and Gleam type errors, on your .bea code), hover, go to definition,
references, rename, completion, signature help, formatting and document
symbols.
- Snippets for the things Bea adds to Gleam: clauses, records,
@derive,
guards (else), ? else, cond.
- Tasks and commands to build, check, test and run the project, with the
compiler's errors in the Problems panel.
Requirements
bea and gleam on your PATH (or set bea.path / bea.gleamPath), and
Erlang, which Gleam needs to build. Without them you still get highlighting
and snippets; the extension says so and points at the install page.
Install
The extension is built from this folder into a .vsix file:
cd editors/vscode
npm ci # dependencies (once)
npm run package # bundles the client -> bea-vscode-<version>.vsix
npm run install-extension # code --install-extension bea-vscode-<version>.vsix
scripts/package_vscode.sh at the top of the repository does the first two
(and the tests); scripts/package_vscode.sh --install all three. The
commands are the same in PowerShell, fish and bash. Without the code
command on your PATH, install the file from VS Code: Extensions → ... →
Install from VSIX.... For VSCodium: CODE=codium npm run install-extension.
What you get
Completion works while you type (after module., inside unfinished
calls, in every branch of case/cond/if, after ? and early returns),
offers functions from modules you haven't imported yet and adds the import,
and completes calls as name(|) with the signature popping up. It knows
what only exists in Bea:
- after
User., the functions of the type (decoder, guest, ...),
derived or written by hand, by the name they have through the type;
- after
@, the attributes, and inside @derive(, the deriver modules
(adding the import of one that isn't imported);
- keywords:
return, case, cond... in a function, def, pub,
type... at the top level.
Rename renames the Bea code: every clause of a function, the type and
the constructor of a record, the functions reached through a renamed type
(User.guest() → Member.guest()), and Point(x:, y:) keeps its labels
when a variable is renamed (Point(x: left, y:)).
Signature help shows parameter names and follows the argument you are
on. Go to definition and hover on @external(...) open the Erlang /
JavaScript function, and on a deriver in @derive(...), its module.
Formatting is Bea's formatter; it also organises imports (see
bea.format.organizeImports). With "editor.formatOnSave": true this
happens on every save.
Commands
| Command |
What it does |
| Bea: Build Project / Check Project / Run Tests / Run Project |
bea build, check, test, run in the project of the file you are in, as a task |
| Bea: Run This Module (also the ▷ button of the editor) |
bea run -m <module> for the file you are in |
| Bea: Show Generated Gleam |
The Gleam the current buffer compiles to, unsaved changes included |
| Bea: Restart Language Server |
After installing bea or gleam, or when something looks stuck |
| Bea: Show Language Server Output |
The server's log (set bea.trace.server to see the messages) |
Tasks
The same commands are tasks (Terminal → Run Task... → bea), one set per
project (bea.toml) in the workspace; Run Build Task (Ctrl+Shift+B) offers
build and check. In tasks.json:
{
"type": "bea",
"command": "run",
"args": ["run", "-m", "app/tasks/seed", "--", "--dry-run"],
"problemMatcher": "$bea"
}
$bea is the problem matcher for Bea's and Gleam's errors; use it with any
task that prints them.
Settings
bea.path, bea.gleamPath: where the executables are
bea.completion.callParentheses (default on): complete functions with ()
bea.format.organizeImports (default on): formatting also organises
imports — grouped (standard library, dependencies, project) and sorted,
unused ones removed, obvious missing ones added
bea.trace.server: log the messages exchanged with the server
The extension turns off VS Code's word based suggestions for .bea files, so
completions come from the language server only, and makes itself the
formatter of .bea files.
How it works
| File |
Role |
package.json |
The manifest: the bea language, its grammar, snippets, settings, commands, the task type and the $bea problem matcher |
syntaxes/bea.tmLanguage.json |
Highlighting: a TextMate grammar, which VS Code runs by itself (no code). It follows src/bea/lexer.bea: the same keywords, string escapes, #{...} interpolation, raw strings and number literals |
syntaxes/bea.markdown.tmLanguage.json |
Injects that grammar into Markdown's ```bea blocks |
language-configuration.json |
Comments, brackets, auto-closing pairs, indentation |
snippets/bea.json |
Snippets |
extension.js |
The client: starts bea lsp and talks LSP to it over stdio (with vscode-languageclient), provides the tasks and the commands. Bundled into dist/extension.js by npm run build |
Everything about the language itself is in the server, which is part of the
bea command (src/bea/lsp/): the extension doesn't need a new release
when the language changes, only when keywords or literals do (the grammar).
Development
npm ci
npm test # the grammar on real code (vscode-textmate), and the manifest
npm run build # dist/extension.js
npm run package # the .vsix
npm run test:vscode # installs the .vsix into a real VS Code and tries it
# (downloads VS Code once; on headless Linux: xvfb-run -a ...)
Open this folder in VS Code and press F5 to try the extension in a second
window without packaging it.