Neovim Code Completion & Buffer Bar
Last updated: 3 days, 13 hours ago
Intro
Since my last post, I have noticed a few things that I want to adjust: add in tab completion and add in an open buffer viewer. Right now I am typing out every single function and variable, and it's making me want to quit life. Also, having multiple buffers open but not having them visually represented anywhere is adding to mental load or wasting time looking for the correct buffer.
Reminders and Notes:
- I am working with C++ so this tab completion will be focused for that.
- I am working on Fedora, instructions will be tailored to that.
- For all code blocks I will try to add extra comments for explanation, these are not required in your work
Code Tab Completion
First we need to make sure that we have everything installed. For this we need:
- Neovim
- rust
- cargo
- git
- clangd
I already have Nvim and Git installed from our last work. Apparently, I already have rust installed as well but not sure where that came from. Might have been prepackaged with Fedora but don't feel like checking. The command to install all at once is:
sudo dnf install neovim clangd rust cargo git
Next, we'll build the Neovim side of things. Let's make the plugin directory:
mkdir -p ~/.config/nvim/lua/plugins
We should already have part of that directory structure from last post: ~/.config/nvim but if not, recall that the -p will make all directories in path if they are not there already.
Now we are going to edit our ~/.config/nvim/init.lua to add the following:
-- Bootstrap lazy.nvim into the nvim plugins folder, should work
-- out to be something like ~/.local/share/nvim
local lazypath = vim.fn.stdpath("data") .. "/lazy/lazy.nvim"
-- if statement to check if plugin already there
if not vim.loop.fs_stat(lazypath) then
vim.fn.system({ -- this is equequivalent iv to a git clone command to download the plugin
"git",
"clone",
"--filter=blob:none",
"https://github.com/folke/lazy.nvim.git",
"--branch=stable",
lazypath,
})
end
-- tells neovim where lazy.nvim is
vim.opt.rtp:prepend(lazypath)
-- starts lazy.nvim
require("lazy").setup({
spec = {
{ import = "plugins" },
},
})
Next, we need to create and fill in the completion plugin file ~/.config/nvim/plugins/completion.lua to install the blink plugin:
return {
{
-- installs blink.cmp plugin
"saghen/blink.cmp",
-- also installs dependancy plugins
dependencies = {
"saghen/blink.lib",
"rafamadriz/friendly-snippets",
},
-- tells lazy.nvim to build the blink component using rust
build = function()
require("blink.cmp").build():pwait()
end,
opts = {
keymap = {
preset = "default",
},
completion = {
documentation = {
auto_show = false,
},
},
-- defines where suggestions come from
sources = {
default = {
"lsp",
"path",
"snippets",
"buffer",
},
},
fuzzy = {
implementation = "rust",
},
},
},
}
Save and exit neovim :wq! and restart neovim. This should the start the installation process of the plugins. After the installation finishes, it should give you a summary screen. This screen told me that I had an update to do. I pressed u to update and it did. I thought that it was neat that neovim has a built-in installer and updater system in it. Hopefully that was all successful for you, because there is more to do! By the way, :q to get out of updater system; I was stuck for a moment.
Now for the other half, installation of Clangd. Clangd is the part that understands the C++, while the blink takes the completion information and gives it to the user. We need another file: ~/.config/nvim/lua/plugins/lsp.lua and in it:
return {
{
-- installs nvim-lspconfig to provide ready-made configs for language servers (including clangd)
"neovim/nvim-lspconfig",
config = function()
-- tells neovim to enable the lsp configuration named clangd
vim.lsp.enable("clangd")
end,
},
}
Save, quit, and restart neovim to get another install process.
Next, we need to adjust out projects CMakeList.txt so that our project gets connected to the clangd. This is important for accuracy of the autocomplete, but at this point clangd should be working without the cmake flag. Even though it is working, it is recommended to add the cmake flag. That also means adding the cmake flag to any c++ project you're working on IF you want accurate code completion. Move to whatever project directory you are working on and nvim ./CMakeLists.txt and add the following:
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
I put this flag after the project() flag. If we are still working on the file from the last post, it should now read:
# Min Version. Every CMakeList should have this. can be a range to allow better compatibility
cmake_minimum_required(VERSION 3.15...4.4)
# Setting the project. Every top level CMake will have this.
project(MyProject VERSION 1.0 DESCRIPTION "ReLearning CMake" LANGUAGES CXX)
# Add export for clangd
`set(CMAKE_EXPORT_COMPILE_COMMANDS ON)`
# tell cmake, when compiling main, use C++23
target_compile_features(main PRIVATE cxx_std_23)
And now we can run cmake commands, or script if you made that, to rebuild the project. I did make the script so I'm going to run: ./startup.sh. The cmake will create a new file "./build/compile_commands.json". And now we can test it to see if it works. I had to close all nvim windows and reopen them before it worked for me. In my project, I have a fileData string variable. If I type in file, it gives me a dropdown with the fileData completion as well as a whole bunch of other completions. Or if I write fileData. I will get autocompletions for all the string functions for it! So cool!
To select the dropdown suggestion, you need to highlight and then type the next character that you need after the completion, most likely a space. I was confused by this because I am used to tabbing to select the item and that wasn't working.
Open Buffer Viewer
Now that I am looking at it, Nvim does have features for tab viewing.
:buffers or :ls will show all the tabs that you have open
With the buffers open :b #, # = number of the buffer, this will switch you to the file in the buffer that you specified. But this isn't what I want; I want a persistent visual representation of what buffers are open.
It turns out that Neovim has a built-in tabline which shows the buffer. It has a few config states that we can set:
:set showtabline=0 - never shows tabline
:set showtabline=1 - shows tabline when there are multiple buffers
:set showtabline=2 - always shows tabline
We can put this setting into our init.lua as well so every nvim instance gets same setting: vim.opt.showtabline=2
I'm going to use setting 2 because I always want to see them. Once done, your buffers should show along the top of the nvim window like tabs in a browser.
Conclusion
That was a big morning for me. Hours of research, editing conf files, and typing this post. I hope you all have as much fun as I did. If you want any specific topics covered, please reach out, but I think I may look at editing the visuals next. Get a nicer theme going instead of the stock, boring theme. Maybe. I really should be doing actual programming.
If you want me to elaborate on anything, please message me on Discord, Mastodon, X, or email me. I’ll be happy to go into more detail one-on-one or to create more posts.
