How To Setup Clangd With GCC Headers and Neovim LSP for Competitive Programming
I recently got frustrated with ccls
because it was the only thing slowing down my blazingly fast Neovim experience. Don’t get me wrong it’s a great tool, however, the gripe I had with it is that it didn’t have single-file support, which… in competitive programming is almost always the case. Single-file support means that clangd
or ccls
won’t index anything. This was partially possible with ccls
, however, indexing would still use an excessive amount of resources.
I have always desired to switch to clangd
, but it usually just didn’t work or I got a weird error at the top of the document which with my OCD was unacceptable. I also did quite a bit of research about this topic, however, nothing really was complete (which is why I’m writing this).
For this configuration, I expect that you have a Neovim Lua config, but if you are using Vimscript the same should still apply. I am using an M1 Mac (which was why it was hard to find resources), but if you are using another device the same should apply.
Installation
You can skip the following if you have a working Neovim LSP config.
Install Neovim LSP and Clangd
To install packages I use brew, which is probably the most popular macOS package manager. To install Neovim, clangd
, and GCC, run the following command.
brew install neovim --HEAD
brew install llvm gcc@12
I like to install Neovim with the --HEAD
argument because then I will get the most up-to-date features. As for llvm
, it is to install clangd
, and installing GCC is to get the sweet old header files.
Configure Neovim LSP with Clangd
Next, you need to install LSP. To do this, you need to add a few lines of code wherever you install your Neovim plugins. For me, I use lazy.nvim, which is a great package manager for speed (visit this article), so the installation would be the following.
return {
"neovim/nvim-lspconfig",
event = "BufReadPre",
dependencies = { "hrsh7th/cmp-nvim-lsp" }, -- if you use nvim-cmp
config = function()
require'lspconfig'.clangd.setup{}
end,
}
I personally don’t have it setup like this, because it gets very messy when you have more than just a few LSP plugins. The way I do it is by having a file named servers.lua
which I can include all my configurations.
The server.lua
file should look something like this:
return {
bashls = {},
clangd = {},
jdtls = {},
cssls = {},
dockerls = {},
tsserver = {},
svelte = {},
eslint = {},
html = {},
pyright = {},
sumneko_lua = {},
yamlls = {},
tailwindcss = {},
}
Then, in your LSP config file, include the following code (credit: folke):
local capabilities = require("cmp_nvim_lsp").default_capabilities(vim.lsp.protocol.make_client_capabilities())
local servers = require("plugins.lsp.servers")
for server, opts in pairs(servers) do
opts.capabilities = capabilities
require("lspconfig")[server].setup(opts)
end
I have this code when I require mason-lspconfig.nvim
so that mason.nvim
can install all the LSP servers.
Now if you go into a .cpp
file you should see some diagnostics. For me, it looks something like this:
Don’t worry about the errors, they will all be gone soon enough.
Configuring Clangd
There are two ways that I like to use when configuring clangd
. The way I prefer (and the way I will be showing) is a global config file: .clangd
. However, it is also possible to have a file called compile_flags.txt
or compile_commands.json
. The latter of the two ways is a lot more flexible and is better for large projects.
To configure clangd
add the following to the ~/.clangd
file.
CompileFlags:
Add:
- -I/opt/homebrew/Cellar/gcc/12.2.0/bin/../lib/gcc/current/gcc/aarch64-apple-darwin22/12/../../../../../../include/c++/12
- -I/opt/homebrew/Cellar/gcc/12.2.0/bin/../lib/gcc/current/gcc/aarch64-apple-darwin22/12/../../../../../../include/c++/12/aarch64-apple-darwin22
- -I/opt/homebrew/Cellar/gcc/12.2.0/bin/../lib/gcc/current/gcc/aarch64-apple-darwin22/12/../../../../../../include/c++/12/backward
- -I/opt/homebrew/Cellar/gcc/12.2.0/bin/../lib/gcc/current/gcc/aarch64-apple-darwin22/12/include
- -I/opt/homebrew/Cellar/gcc/12.2.0/bin/../lib/gcc/current/gcc/aarch64-apple-darwin22/12/include-fixed
- -I/Library/Developer/CommandLineTools/SDKs/MacOSX13.sdk/usr/include
- -I/Library/Developer/CommandLineTools/SDKs/MacOSX13.sdk/System/Library/Frameworks
Furthermore, the directories listed after the -I
are the GCC header directories. To find out what they are on your system run the command:
gcc-12 -E -v -xc++ /dev/null
Note: Don’t type
gcc
as it refers toclang
, usegcc-<version>
where<version>
is the GCC version you installed.
The output should look somewhat like this:
The code that goes after the -I
, is inside the orange box. More specifically, the directories listed after #include <…> search starts here:
.
Compile Flags
Next, we will be adding compile flags. These are useful for seeing possible errors/warnings in your code before compiling. I personally have the following:
- -DLOCAL
- -Wall
- -O2
- -Wextra
- -Wshadow
- -Wconversion
- -Wfloat-equal
- -Wno-unused-const-variable
- -Wno-sign-conversion
- -std=c++17
This should be most of the config, however, a big part of why I am using clangd
is because it can run in single-file mode. To do this I add the following to the same .clangd
file.
Index:
Background: Skip
Now, clangd
should finally be working. Also including GCC headers that aren’t available in bits/stdc++.h
won’t be flagged as an error.
Errors
One of the errors I got when configuring clangd
was:
In included file: __float128 is not supported on this target
.
There are two ways to solve this. One of which involves using mason.nvim
and the other involved commenting out some code.
Mason.nvim Method (My Preferred Method)
If you don’t know, mason.nvim
is basically a plugin to manage your LSP, lining, formatting, etc. It is very useful because you won’t need to install all the dependencies manually. As for clangd
, it works because mason.nvim
gets clangd
from source and therefore neovim-lspconfig
doesn’t use Apple’s clangd
. After installing and configuring mason.nvim
and neovim-lspconfig
, you should have a working config.
Second Method
The second method involves commenting some #define
directives. To get started go to your GCC include directory for aarch64-apple-darwin22
. For me, it was:
/opt/homebrew/Cellar/gcc/12.2.0/lib/gcc/current/gcc/aarch64-apple-darwin22/12/include
Continuing, the file we want is stddef.h
. Therefore, edit the file with a text editor of choice (hopefullyNeovim) and comment out the following lines:
#if defined(__i386__) || (__APPLE__ && __aarch64__)
__float128 __max_align_f128 __attribute__((__aligned__(__alignof(__float128))));
#endif
If you have trouble editing the file you can just type chmod 600 stddef.h
to change the permissions.
That should be it. Now you should have a perfectly working config.
Hopefully, this helped you get clangd
setup with Neovim LSP for competitive programming and spend a whole lot less time trying to get this to work than I did. If you have any problems, check out my dotfiles or comment and I will try to help.
Thank you for reading.
Keep vimming.