Unixification

We write about ricing different Unix systems such as macOS, and Linux. Other than Unix systems we…

How To Setup Clangd With GCC Headers and Neovim LSP for Competitive Programming

--

Screenshot by author

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:

Screenshot by author

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 to clang, use gcc-<version> where <version> is the GCC version you installed.

The output should look somewhat like this:

Screenshot by author

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.

Screenshot by author

Errors

One of the errors I got when configuring clangd was:

In included file: __float128 is not supported on this target.

Screenshot by author

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.

--

--

Unixification
Unixification

Published in Unixification

We write about ricing different Unix systems such as macOS, and Linux. Other than Unix systems we write on various tools, like Neovim and Obsidian.

Michael Bao
Michael Bao

Written by Michael Bao

Neovim | Arch Linux | macOS | I love to write about random tech stuff. Tinkering around with Linux, Neovim, and computers.

Responses (4)