Skip to content

Editor Setup

This project does not require an IDE, and no editor is privileged over any other. Everything below is optional — the build works from a terminal with nothing but CMake and a compiler.

The reason this page exists is history. The FX3 sources arrived as an Eclipse CDT project, the GUI as a Qt Creator qmake project, and the gateware as a Quartus GUI project. Each brought its own build definition, each drifted from the real one, and each made contributing conditional on installing a particular large application. All three are gone. What replaces them is one mechanism that every modern editor already speaks: the Language Server Protocol.

How it works, in one paragraph

Every CMake component sets CMAKE_EXPORT_COMPILE_COMMANDS, so configuring a build writes build/compile_commands.json — the exact compiler invocation for every source file. Each component also has a .clangd file pointing at that build directory. Start clangd in any editor and you get completion, go-to-definition, find-references, inline diagnostics and rename across the C and C++ in that component. Verilog gets the same treatment from verible-verilog-ls. Both language servers come with the Nix dev shells, so there is nothing to install per developer.

Once, before anything else

Get the toolchain, then configure each component you intend to work on.

Run all of this from the repository root. The nix develop line would work from any subdirectory — there is one flake.nix, at the root, and Nix walks up to find it — but the cmake lines below use paths relative to the root, so the root is the one place every command here is correct.

nix develop                      # everything free, across all components
cmake -B ddd-gui/build -S ddd-gui
cmake -B fx3/programmer/build -S fx3/programmer
cmake -B fx3/firmware/build -S fx3/firmware \
      -DCMAKE_TOOLCHAIN_FILE=../arm-none-eabi-toolchain.cmake

The configure step is what produces compile_commands.json.

Configure first

Until a component has been configured at least once, clangd has nothing to read and will report errors on every include. That is the single most common cause of "the language server does not work here".

Re-run configure after adding or removing a source file.

With direnv

If you use direnv, .envrc in the repository root activates the default shell automatically:

direnv allow

Editors launched from inside the directory then inherit the toolchain, which matters for GUI editors that do not read your shell profile. To use a component shell instead, put use flake .#ddd-gui in .envrc.local.

Per-component notes

Component Language server Notes
ddd-gui/ clangd Qt's moc output lands in the build tree; the compile database already includes those paths
fx3/programmer/ clangd Nothing special — ordinary host C
fx3/firmware/ clangd .clangd sets Compiler: arm-none-eabi-gcc. Without it clangd probes the host compiler for system headers and reports hundreds of false errors, because the firmware is freestanding and never sees the host libc
fpga/ verible-verilog-ls nix develop .#fpga — free tools only, no Quartus needed to edit, lint or simulate
docs/ Markdown; any editor
hardware/ KiCad's own GUI. nix develop .#hardware

VS Code

Install two extensions:

  • clangd (llvm-vs-code-extensions.vscode-clangd)
  • CMake Tools (ms-vscode.cmake-tools)

Disable the C/C++ extension's IntelliSense if you have it installed — it and clangd fight over the same files and produce contradictory diagnostics:

{
  "C_Cpp.intelliSenseEngine": "disabled",
  "clangd.arguments": ["--background-index", "--clang-tidy"]
}

Open the component directory (ddd-gui/, fx3/firmware/) rather than the repository root, so clangd finds the right .clangd. If you prefer a single window, use a multi-root workspace with one folder per component.

Do not commit a .vscode/ directory — it is gitignored at the root deliberately.

Neovim

With the built-in LSP client and nvim-lspconfig:

require('lspconfig').clangd.setup {
  cmd = { 'clangd', '--background-index', '--clang-tidy' },
}

require('lspconfig').verible.setup {
  cmd = { 'verible-verilog-ls', '--rules_config_search' },
}

clangd locates the right configuration by walking up from the file being edited, so opening a file anywhere in the tree works — there is no per-project setup beyond the above.

For Verilog, verible-verilog-ls is in the fpga shell. Start it with --rules_config_search (as in the Neovim example below) and it finds fpga/.rules.verible_lint by walking up from the file being edited, so the diagnostics in your editor are the same ones the fpga-style CI check enforces. Formatting is ./fpga/tests/run-format.sh, not a bare verible-verilog-format — the settings live in fpga/.verible-format and the script is what applies them.

Emacs

With eglot (built in since Emacs 29):

(add-hook 'c-mode-hook #'eglot-ensure)
(add-hook 'c++-mode-hook #'eglot-ensure)
(add-hook 'verilog-mode-hook #'eglot-ensure)

(with-eval-after-load 'eglot
  (add-to-list 'eglot-server-programs
               '(verilog-mode . ("verible-verilog-ls" "--rules_config_search"))))

lsp-mode works equally well; nothing in the repository depends on which you choose.

Helix

Helix has an LSP client built in and needs no configuration for C and C++ — it looks for clangd on PATH, which the dev shell provides. For Verilog, add to languages.toml:

[[language]]
name = "verilog"
language-servers = ["verible"]

[language-server.verible]
command = "verible-verilog-ls"
args = ["--rules_config_search"]

Qt Creator

Qt Creator opens CMake projects natively. Use File → Open File or Project and select ddd-gui/CMakeLists.txt.

Do not look for .pro files — the qmake project files were removed. They were a second build definition maintained by hand alongside the CMake one, and they drifted. Qt Creator loses nothing: it reads the CMake project directly, including the Qt-specific targets.

Its generated *.user files are gitignored.

CLion and KDevelop

Both open CMakeLists.txt directly and manage their own build directory. Point them at the component directory. Neither needs anything from this repository beyond the CMake project.

If CLion cannot find Qt or libusb, it is being launched outside the Nix shell — start it from a terminal inside nix develop, or use direnv.

Formatting

Formatting is enforced for the gateware and not for anything else, so the rules differ by component. What exists:

  • .editorconfig at the repository root, honoured by every editor listed above (VS Code and Emacs need a plugin; Neovim, Helix, CLion, KDevelop and Qt Creator support it natively). It sets indentation, line endings and trailing-whitespace behaviour per file type.
  • verible-verilog-format for fpga/, and here formatting is enforced — the fpga-style check fails the build on a file that is not exactly what the formatter would produce. Run ./fpga/tests/run-format.sh rather than the tool directly; the settings are in fpga/.verible-format. The gateware has already had its one-off reformat, so there is no history left to bury.
  • clang-format ships in the dev shells. ddd-gui/ has a .clang-format and gates on it; fx3/ does not, so running it there would reformat against its default style. Do not run it across those files.

The rule that matters, everywhere except fpga/: do not reformat code you are not otherwise changing. Whitespace-only diffs bury the actual change and break git blame.

Paths that must never be reformatted at all — vendored or generated — are listed in AGENTS.md §3 and marked unset in .editorconfig so a format-on-save cannot touch them.

When the language server misbehaves

Symptom Cause
Every #include is red The component has not been configured — no compile_commands.json yet
Only some files are broken Those files were added after the last configure; re-run cmake -B build
Firmware sources show hundreds of libc errors clangd is not using fx3/firmware/.clangd; you probably opened the repository root rather than the component
Qt headers not found The editor was launched outside the Nix shell
Stale diagnostics after a rebuild Clear clangd's index: rm -rf .cache/clangd