Skip to content

Instantly share code, notes, and snippets.

@ironlungx
Last active February 28, 2025 07:14
Show Gist options
  • Save ironlungx/a2b620de74f875c49c1d06999a8c41f8 to your computer and use it in GitHub Desktop.
Save ironlungx/a2b620de74f875c49c1d06999a8c41f8 to your computer and use it in GitHub Desktop.
PlatformIO with Neovim

Extensions

Following are the extensions required for neovim:

I use lazy.nvim so, just add the following to your plugins table, or create a new file in lua/plugins/lsp.lua

return {
	{
		"williamboman/mason.nvim",
		config = function()
			require("mason").setup({})
		end,
	},
	{
		"williamboman/mason-lspconfig.nvim",
		config = function()
			require("mason-lspconfig").setup({
				ensure_installed = { "lua_ls", "clangd" },
			})
		end,
	},
	{
		"neovim/nvim-lspconfig",
		config = function()
			local lspconfig = require("lspconfig")
			lspconfig.lua_ls.setup({})
            		lspconfig.clangd.setup({}) -- Minimal setup
		end,
	},
}

For the LSP server setup (as recommended by @omani) this is what I use, it improves auto complete functionality

lspconfig.clangd.setup({
    on_attach = on_attach,
    capabilities = capabilities,
    cmd = {
      "clangd",
      "--background-index",
      "-j=12",
      "--query-driver=**",
      "--clang-tidy",
      "--all-scopes-completion",
      "--cross-file-rename",
      "--completion-style=detailed",
      "--header-insertion-decorators",
      "--header-insertion=iwyu",
      "--pch-storage=memory",
      "--suggest-missing-includes",
    },
})

Setting up the project

I've made a script to do all this for you:

$ cd path/to/the/project
$ curl -sSL https://gist.github.com/ironlungx/a2b620de74f875c49c1d06999a8c41f8/raw/script.sh | sh
$ rm compile_commands.json
$ pio run -t compiledb

BUT I DONT LIKE curl|sh:

  1. Initialize a project with pio init

  2. In the project root create the following files

    • .clangd: (thanks @omani for fixing and adding flags to the file)

      # clangd controls options for the LSP *server*
      CompileFlags:
        Add: [
          -DSSIZE_MAX,
          -DLWIP_NO_UNISTD_H=1,
          -Dssize_t=long,
          -D_SSIZE_T_DECLARED,
          -Wno-unknown-warning-option
        ]
        Remove: [
          -mlong-calls,
          -fno-tree-switch-conversion,
          -mtext-section-literals,
          -mlongcalls,
          -fstrict-volatile-bitfields,
          -free,
          -fipa-pta,
          -march=*,
          -mabi=*,
          -mcpu=*
        ]
      Diagnostics:
        Suppress: "pp_including_mainfile_in_preamble"
    • .clang-tidy: (thanks @omani for adding more flags!)

      # Formatter options
      Checks: "*,
              -abseil-*,
              -altera-*,
              -android-*,
              -fuchsia-*,
              -google-*,
              -llvm*,
              -modernize-use-trailing-return-type,
              -zircon-*,
              -readability-else-after-return,
              -readability-static-accessed-through-instance,
              -readability-avoid-const-params-in-decls,
              -cppcoreguidelines-non-private-member-variables-in-classes,
              -misc-non-private-member-variables-in-classes,
      "
      WarningsAsErrors: ''
      HeaderFilterRegex: ''
      FormatStyle:     none
    • gen_compile_commands.py:

      import os
      Import("env") # platformio specific stuff
      
      # include toolchain paths (i.e. to have stuff like the Arduino framework headers present in the compile commands)
      env.Replace(COMPILATIONDB_INCLUDE_TOOLCHAIN=True)
      
      # override compilation DB path
      env.Replace(COMPILATIONDB_PATH="compile_commands.json")
  3. Now add the following to your platformio.ini

    extra_scripts = pre:gen_compile_commands.py
  4. Now in your terminal run: pio run -t compiledb (Just delete the old compile_commands.json before executing this)

Hopefully this helped, I spent a lot of time figuring it out

#!/bin/bash
create_file() {
echo "$2" > "$1"
echo "Created $1"
}
create_file ".clangd" "# clangd controls options for the LSP *server*
CompileFlags:
Add: [
-DSSIZE_MAX,
-DLWIP_NO_UNISTD_H=1,
-Dssize_t=long,
-D_SSIZE_T_DECLARED,
-Wno-unknown-warning-option
]
Remove: [
-mlong-calls,
-fno-tree-switch-conversion,
-mtext-section-literals,
-mlongcalls,
-fstrict-volatile-bitfields,
-free,
-fipa-pta,
-march=*,
-mabi=*,
-mcpu=*
]
Diagnostics:
Suppress: \"pp_including_mainfile_in_preamble\""
create_file ".clang-tidy" "# Formatter options
Checks: \"*,
-abseil-*,
-altera-*,
-android-*,
-fuchsia-*,
-google-*,
-llvm*,
-modernize-use-trailing-return-type,
-zircon-*,
-readability-else-after-return,
-readability-static-accessed-through-instance,
-readability-avoid-const-params-in-decls,
-cppcoreguidelines-non-private-member-variables-in-classes,
-misc-non-private-member-variables-in-classes,
\"
WarningsAsErrors: ''
HeaderFilterRegex: ''
FormatStyle: none"
create_file "gen_compile_commands.py" "import os
Import(\"env\") # platformio specific stuff
# include toolchain paths (i.e. to have stuff like the Arduino framework headers present in the compile commands)
env.Replace(COMPILATIONDB_INCLUDE_TOOLCHAIN=True)
# override compilation DB path
env.Replace(COMPILATIONDB_PATH=\"compile_commands.json\")"
@ironlungx
Copy link
Author

Hey guys, sorry for the late reply (I never knew gists had comments 😅)
I'm glad I could help you all, honestly I could add a line-by-line explanation, it's just that I got all that after hours of googling and most of the line just add/remove compiler parameters... I'll still try my best to add documentation about it

@omani
Copy link

omani commented Feb 26, 2025

for those with errors or issues:
the script provided in this gist at https://gist.githubusercontent.com/ironlungx/a2b620de74f875c49c1d06999a8c41f8/raw/3f0d5d46c0d6369fc7e51e32487772ac194c27b1/script.sh has syntax errors.

here is a working (no errors, no warnings, at all) .clangd content placed in the project root:

CompileFlags:
  Add: [
    -DSSIZE_MAX,
    -DLWIP_NO_UNISTD_H=1,
    -Dssize_t=long,
    -D_SSIZE_T_DECLARED,
    -Wno-unknown-warning-option,
  ]
  Remove: [
    -mlong-calls,
    -fno-tree-switch-conversion,
    -mtext-section-literals,
    -mlongcalls,
    -fstrict-volatile-bitfields,
    -free,
    -fipa-pta,
    -march=*,
    -mabi=*,
    -mcpu=*,
  ]
Diagnostics:
  Suppress: "pp_including_mainfile_in_preamble"

.clangd-tidy:

Checks: "*,
        -abseil-*,
        -altera-*,
        -android-*,
        -fuchsia-*,
        -google-*,
        -llvm*,
        -modernize-use-trailing-return-type,
        -zircon-*,
        -readability-else-after-return,
        -readability-static-accessed-through-instance,
        -readability-avoid-const-params-in-decls,
        -cppcoreguidelines-non-private-member-variables-in-classes,
        -misc-non-private-member-variables-in-classes,
"
WarningsAsErrors: ''
HeaderFilterRegex: ''
FormatStyle:     none

and here my lspconfig setup for clangd:

lspconfig.clangd.setup({
    on_attach = on_attach,
    capabilities = capabilities,
    cmd = {
      "clangd",
      "--background-index",
      "-j=12",
      "--query-driver=**",
      "--clang-tidy",
      "--all-scopes-completion",
      "--cross-file-rename",
      "--completion-style=detailed",
      "--header-insertion-decorators",
      "--header-insertion=iwyu",
      "--pch-storage=memory",
      "--suggest-missing-includes",
    },
})

^ this also makes auto-completion work smoothly.

you can check for formatting errors of both the .clangd and .clangd-tidy file with the following python one-liner:

python -c "import yaml; print(yaml.safe_load(open('.clangd')))"
python -c "import yaml; print(yaml.safe_load(open('.clangd-tidy')))"

@ironlungx
Copy link
Author

hi @omani, thank you so much for the corrections (I didn't know there were syntax errors, unless you are talking about the script - it is very likely there are syntax errors there). I will put your changes into the main gist.
Thanks again

@manuelfarzini
Copy link

Many thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment