The Language Server

FantASM is also a language server, so an editor can show you errors as you type, jump to a label’s definition, and tell you what an instruction costs in T-states. It is the same binary — no separate download, and no second copy of the assembler that might disagree with the one building your program.

fantasm --lsp

Starting it

The server speaks JSON-RPC over stdin and stdout, which is how every LSP client expects to talk to one.

--lsp is handled before the banner would be printed, and logging goes to stderr, so stdout carries protocol and nothing else. --nologo is harmless but does nothing here.

An editor starts this for you; you never run it by hand except to check the binary works.

What it gives you

DiagnosticsErrors and warnings, as you type
Go to definitionJump to where a label, constant or macro is defined
Find referencesEvery place a name is used
HoverWhat a name is worth, or what an instruction costs
CompletionInstructions, directives, and your own names
Workspace symbolsSearch every symbol in the project
Signature helpThe operand forms an instruction accepts

Diagnostics

The file is assembled on every keystroke and the result reported in place — the same errors and warnings fantasm build produces, from the same code.

A diagnostic lands in the file it came from. An error inside an included file is reported against that file, at its own line, not against the include line that pulled it in.

Hover

A label or constant answers with its value and where it was defined:

Constant: MY_CONST = 100 (0x64)
test.asm, line 2

An instruction answers with its timing, including the forms that have two answers:

Instruction: JR
Timing: 12/7 T-states (taken/not taken)
Instruction: LDIR
Timing: 21/16 T-states (repeated/final)

That is the same timing data assert_cycles checks against in Testing.

Completion

Instructions and directives are offered from a fixed list; labels, constants and macros come from your source as it stands. . triggers a completion, so typing Gfx. offers that module’s members — see Modules.

Signature help

Triggered by a space or a comma, so it appears as you start an operand and again after each one.

VSCodium and VS Code

Install 0x1DE from Open VSX (twistedraven.0x1de). It starts the language server for you and adds the rest of the environment around it:

Syntax highlightingA TextMate grammar and semantic tokens for the fantasm language
SnippetsFor .asm and .inc files
Build taskTask type fantasm, taking a file and an optional target
Problem matcherNamed fantasm, so build errors land in the Problems panel
DebuggingBreakpoints and stepping under Bizmuth — see Debugging a Build

Three commands: Assemble (FantASM), Run on Bizmuth, and Restart FantASM Language Server.

Settings, if the defaults do not suit:

SettingDefault
0x1de.fantasm.serverPathfantasm — an absolute path if it is not on your PATH
0x1de.fantasm.serverArgs["--lsp", "--nologo"]
0x1de.fantasm.targetzxnext — passed as -t by the build task

The extension is BSD-3-Clause, and its source is at codeberg.org/TwistedRaven/0x1DE.

Any other editor

Any LSP client will do. The command is fantasm --lsp, and the file extensions are whatever your project uses — .asm, .inc, .z80.

Neovim’s built-in client, as an example:

vim.lsp.start({
  name = 'fantasm',
  cmd = { 'fantasm', '--lsp' },
  root_dir = vim.fs.dirname(vim.fs.find({ 'fantasm.toml', '.git' }, { upward = true })[1]),
  init_options = { target = 'zxnext' },
})

Send target in initializationOptions, or every Z80N instruction is an error. The server assembles as a bare zx48 otherwise, so a Next project shows E1016 Z80n extended instructions are not enabled on every nextreg and mul while the build succeeds on the same lines. It is read once at startup, so changing it means restarting the server.

You get diagnostics, hover, completion and the rest — everything in the table above — but none of the highlighting, snippets or debugging, which are the extension’s rather than the server’s.

How it finds your project

The workspace root is the nearest directory above the open file containing fantasm.toml, .git or Cargo.toml. Whichever is found first walking upwards wins.

include and incbin are then searched for in two places: the open file’s own directory, and that workspace root.

fantasm.toml is found but not read. It marks the root and nothing more, so include_dirs and [defines] are not applied. A project keeping sources in src/ and lib/ and listing both in include_dirs builds correctly from the command line while the editor reports file not found, because the server only ever looks in those two directories.

The machine is the exception: the editor can send a target in initializationOptions, as above. It still does not come from the project file.

Why an included file shows errors

The open file is assembled as its own root, whatever it is. A fragment written to be included — one that uses constants its parent defines, or continues from an org set elsewhere — has none of that context when the editor assembles it alone, so you get E1003 for names that are perfectly fine in a real build.

Nothing is wrong with your source. Assemble the project to see the truth of it:

fantasm build

What it does not do

No formatting, no rename, no code actions, and no inlay hints. The server advertises exactly the seven capabilities in the table above, and an editor offering a command outside that list will find nothing behind it.

What next

  • Diagnostics — reading the messages the editor is showing you.
  • The Project File — what fantasm.toml configures for the build, if not for the editor.

Documentation