- [Intro](#intro)
  * [Problem](#problem)
  * [Solution](#solution)
- [Setup](#setup)
  * [A server](#a-server)
  * [A client](#a-client)
  * [compile_commands.json file generation](#compile-commandsjson-file-generation)
- [Linting](#linting)
- [Code formatting](#code-formatting)
- [Snippets](#snippets)
- [One shot compilations](#one-shot-compilations)

## Intro

I want to share how I use Vim as C++ development environment, adding things like
code completion, linting, formatting and snippet support. If you come from the IDE land and
have been set your options with the checkbox on a GUI, you might need a mental shift to use
text based configuration tools, like Vim.  

### Problem

There has been plugins to provide IDE-like features to Vim. Some of them were
successful, some others evolved, yet others deprecated. I tried plugins like
YouCompleteMe, they were great but the process that making them work wasn't a
pleasant experience. 

Creating a completion engine admittedly hard, porting those code to other editors
without touching implementation details is virtually impossible.

### Solution

[The language server protocol](https://microsoft.github.io/language-server-protocol) developed solve
this problem. The idea is, create a set of rule and implement a server and a
client that follows the rules, then communicate over the protocol and provide
functionalities like auto complete, go-to definition, search function calls,
diagnostics.

Quoting from its [website](https://langserver.org):

> LSP creates the opportunity to reduce the m-times-n complexity problem of
> providing a high level of support for any programming language in any editor,
> IDE, or client endpoint to a simpler m-plus-n problem. 

## Setup

The *server* needs to installed on the host system, and *client* needs to provided
by the editor, either as a plugin or build-in. In Vim, it must be a plugin, NeoVim has
an [experimental support](https://github.com/neovim/nvim-lspconfig). Some IDEs,
like CLion, ship both out of package... with a price.

### A server

There are two actively developed language servers for C++:
[clangd](https://clangd.llvm.org/) and [ccls](https://github.com/MaskRay/ccls).
Both are great. I found clangd is somewhat easier to
install. Precompiled binaries are available in
[here](https://releases.llvm.org/download.html). APT packages also available at
[https://apt.llvm.org](https://apt.llvm.org). On my fresh installed Debian
10, I just use:

```bash
apt-add-repository 'deb http://apt.llvm.org/buster/ llvm-toolchain-buster-11 main'

wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key|sudo apt-key add -

apt-get update

apt-get install -qq clang-11 clang-tools-11 clang-11-doc libclang-common-11-dev \
   libclang-11-dev libclang1-11 clang-format-11 clangd-11 clang-tidy-11 \
   libc++-11-dev libc++abi-11-dev
```
... and that's it. Now along with the clangd, I have the latest stable C++
compiler, standard library implementation (that I can enjoy latest features
added to the language), a linter and a code formatter. We got the server, what
about the client? 

### A client

To make use of clangd, we'll need a language client.
[Various](https://langserver.org) implementations are available, I'll use
[coc.nvim](https://github.com/neoclide/coc.nvim/). It's written in
[TypeScript](https://www.typescriptlang.org) needs
[Node.js](https://nodejs.org/en) runtime:
```
apt-get install npm
```

To add the coc.nvim and other plugins in easy way, a plugin manager required. Again, various
options are available, I'll use [vim-plug](https://github.com/junegunn/vim-plug):

`.vim/vimrc`:
```
call plug#begin('~/.vim/plugged')

Plug 'neoclide/coc.nvim', {'for':['zig','cmake','rust',
     \'java','json', 'haskell', 'ts','sh', 'cs',
     \'yaml', 'c', 'cpp', 'd', 'go',
     \'python', 'dart', 'javascript', 'vim'], 'branch': 'release'}

call plug#end()
```

This is from my vimrc file. Plugins are listed between `plug#begin` and
`plug#end` as `Plug 'Github_URI_path'`. It allows plugins work with certain filetype, which listed after
`for`. I don't need and want this plugin to work other with filetypes, like .txt, .md etc.

`:PlugInstall` will install and `:PlugUpdate` update the plugins. A common 
ritual among the vimmers, once plugin installed, reading its doc. Usually available with `:help pluginName`.

coc.nvim holds server configurations in a JSON file called `coc-settings.json`
(with `:CocConfig`). You can customize almost everything the way server behave.
Check out full scheme from
[here](https://github.com/neoclide/coc.nvim/blob/master/data/schema.json). For
now, I'll just register language server and leave others default: 

`.vim/coc-settings.json`:
```
{
  "languageserver":{
    "clangd":{
      "command":"clangd",
      "filetypes":[
        "c",
        "cpp"
      ],
      "rootPatterns":[
        "compile_commands.json",
        ".git"
      ],
      "args":[
        "--compile-commands-dir=build",
        "--compile_args_from=filesystem",
        "--all-scopes-completion",
        "--background-index",
        "--clang-tidy",
        "--cross-file-rename",
        "--completion-parse=always",
        "--completion-style=detailed",
        "--function-arg-placeholders",
        "--header-insertion-decorators",
        "--query-driver=/usr/bin/**/clang-*,/home/adem/GCC-10/bin/g++*",
        "--header-insertion=never",
        "--limit-results=0",
        "-j=6",
        "--pch-storage=memory",
      ]
    }
  }
}
```
 
This calls clangd with the parameters listed on args array. Check out all options of clangd with `clangd --help`.

You can add any number of server to `languageserver` object. That way you can
have same editing experience, mapping, theme, etc for different languages on the same editor:
```
{
  "languageserver":{
    "clangd":{
       // clangd options
     }, 
    "rls" {
      // rls options
    },
    "bash-lsp" {
      // bash-lsp options
    }
  }
}
```
For mappings (a term used for reassignable shortcuts in Vim), begin with the [example
configuration](https://github.com/neoclide/coc.nvim/#example-vim-configuration).
If you want to take a look, this idiot holds [his configurations](https://github.com/p1v0t/dotfiles) on
Github.

Now got the server and client, but need one more thing.

## compile_commands.json file generation

The last part of the ceremony involves using [CMake](https://cmake.org). 
All we need is, adding a single line definition on toplevel CMakeLists.txt:
```
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
```
This generates a file called
[compile_commands.json](https://clang.llvm.org/docs/JSONCompilationDatabase.html)
on build directory. It contains include paths, compiler command and options.
These helps clangd to figure out what is where. 

If the project don't use CMake, but make, you can use
[Bear](https://github.com/rizsotto/Bear) to generate this file with `bear make`
command.

## Linting 

The [clang-tidy](https://clang.llvm.org/extra/clang-tidy/) linter can be
called via clangd. A linter is a tool that shouts out the fishy parts of
the code. Make sure passed `--clang-tidy` arg to clangd on coc-setting.json file.

You can enable some family of checks, and dump it into a file:
```
clang-tidy --checks='-*,bugprone-*' --dump-config > .clang-tidy
```
Clangd will detect this file and lint your code based on some criteria. See all
checks with: `clang-tidy --list-checks`

## Code formatting

People used [AStyle](http://astyle.sourceforge.net) and some other tools to format their code. As a millennial, my
first encounter with such a tool happen with
[clang-format](https://clang.llvm.org/docs/ClangFormat.html). It's fairly easy
to use:
```
clang-format -i source.cpp
```

[vim-clang-format](https://github.com/rhysd/vim-clang-format) plugin allows us to use clang-format
in automatized way, like formatting on every save:

`.vim/vimrc`:
```
" between plug#begin/plug#end
Plug 'rhysd/vim-clang-format', {'for' : ['c', 'cpp']}

let g:clang_format#auto_format=1
```

## Snippets

Snippets are kind of underappreciated helpers. They reduce repetitive,
no-brainer jobs. Install coc.nvim snippet extension: `:CocInstall coc-snippets`

Let extension know where to find snippets:

`.vim/coc-settings.json`:
```shell
"snippets.textmateSnippetsRoots": ["/home/adem/.config/snippets"],
```

On daily coding, when I type `cmake`, it extends to:
```
cmake_minimum_required(VERSION |)

DESCRIPTION "|"
HOMEPAGE_URL https://github.com/p1v0t/ |
LANGUAGES CXX)

set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
 |
```
where each `|` waiting to be typed. Writing such a snippet is pretty easy. Just
like in VSCode, it's a JSON file:

`/home/adem/.config/snippets/cmake.json`:
```
{
    "cmake": {
        "prefix": [
            "cmake"
        ],
        "body": [
            "cmake_minimum_required(VERSION ${1:version})",
			 "",
            "project(${2:projname}",
            "DESCRIPTION \"${3:description}\"",
            "HOMEPAGE_URL https://github.com/p1v0t/${4:reponame}",
            "LANGUAGES CXX)",
			"",
            "set(CMAKE_EXPORT_COMPILE_COMMANDS ON)",
            "${0}"
        ]
    }
}
```

There is also [an extension](https://github.com/voldikss/coc-cmake) for CMake which
provides basic completion. See list of extensions in [here](https://github.com/neoclide).

## One shot compilations

If you like to write small programs and don't need to build,
[SingleCompile](https://github.com/vim-scripts/SingleCompile) plugin is just for
this. Here is an example mapping:
```
nnoremap <F5> :SCCompileRunAF -g -Wall -Wextra -std=c++2a<cr>
```
When I press `<F5>`, it compiles and run the executable. More on `:h SingleCompile`

---

This client-server architecture in mind, you can use similar setup on other
platforms. In Windows, you can get Windows binaries of LLVM and use clangd on
VSCode with [clangd
extension](https://marketplace.visualstudio.com/items?itemName=llvm-vs-code-extensions.vscode-clangd).
On Emacs, there is a [lsp mode](https://github.com/emacs-lsp/lsp-mode) support.

Feel free to embarrass me what I made with this post :) Thanks for reading.

---