Neovim + Arduino + PlatformIO
Hello,
I am working on a project that uses the arduino framework, and I am having issues running clangd/arduino-language-server LSP's on my .ino project file.
This may be the completely wrong place to post such questions but maybe someone here has a solution?
The problem lies with needing to run the arduino cli in order to get lsp support for an .ino file that contains arduino specific code, that is not available in the pio cli.
This forum post perfectly outlines my issue: https://community.platformio.org/t/using-arduino-language-server-with-platformio-in-neovim-possible/44134
This is the error I get upon trying to load the arduino language server
[START][2025-06-24 09:11:36] LSP logging initiated [ERROR][2025-06-24 09:11:36] ...p/_transport.lua:36 "rpc" "arduino-language-server" "stderr" "09:11:36.503010 Path to ArduinoCLI config file must be set.\n"
I've struggled with this, too. It's ugly on ESP32 family. When p*ini has multiple base envs for a few dozen targets and some of them invoke esp32-g++ -D THIS and some invoke esp32-g++ -D THAT and some invoke esp32s3-g++ and some riscv-something-g++ but the THIS and THAT defines set up a different #ifdef heirararchy and, perhaps worse, a radically different set of 197 (not even an exaggeration) flags that slum around in .pio/build/target1 ~/.platformio/huge/variable/path and so on, I've not figured out a survival strategy for neovim at all. Which of the 11 different gpio_reg.h's should you be taken to? Well, you have to know what you're building for, right? So you just crank off a pio run -t compiledb -e target . Oh, wait. That doesn't put it in .pio/build/foo or something; it gets clobbered on each run. You can't really use compile_commands.json at all, really, since it uses an implicit path instead of the compiler it actaully lists.
I've had some luck scripting runs of pio run compiledb to grab the compilers and flags and then grabs some fields from pio project config to build a .clangd configuration, but then I have to remember to manually regenerate it when I need to see a defintion that might change between two of my targets.
If you don't care about popping to system symbol declarations and definitions and use ONLY your own code, scripting the 'find .pio/build/target -type d' to build the list of (in my case) up to 78 directories for each or my 49 targets brings SOME relief to the issue and at least lets you reference what's in your own directly dependent headers/source.
I get that we're trying hard to avoid the E of an IDE and I don't know a great solution (fixing the compiledb generation and location would help) but I've not found the workarounds very satisfying.
Mostly, I keep an ack -il WHATEVER src include .pio/build/$TARGET in my command line history and go hunt for decls and defns myself. :-( 🦖
I have created a work around that generates a .clangd configuration that provides what is needed to use clangd as a Neovim LSP with PlatformIO. I have only tested with 2 boards (both arduino framework). There is some flexibility to add compiler flags ignores in the pio_targets.py script if other platforms run into problems...and of course pull requests are welcomed. See my project here: https://github.com/gunlock/pio-clangd. Hope this helps.