Using Vim for C++ Development
gist.github.com
gist.github.com
At some point in time I switched to a full-fledged IDE (CLion) because there are certain refactoring tasks which are probably still possible using this setup but I found them to be mentally too complex to deal with, especially when I am focusing on the task at hand at the same time.
[1] https://github.com/NewProggie/Dev-Utils/blob/master/dotfiles...
CLion is pretty much the best C++ IDE I've found by a mile. Eclipse is so slow and terrible I won't even consider it. Visual Studio is amazing but Windows only and really heavyweight. VSCode, Atom, etc. aren't really "proper" IDEs. Then there's open source contenders like Jucipp which while very fast do lack a lot of the niceties of their paid competitors.
My only gripe with CLion is that it's CMake only. This limits its usefulness for Bazel or Autotools projects.
Maybe that's been added since I used it 1.5 years or so ago though.
Ironically it was a similar experience with InteliJ that made me love Eclipse again.
Very funny! On our codebase CLion is very, very, very slow.
But you cannot escape the reality, refactoring kinda needs a good IDE to get it done quickly.
Using language servers make me wonder why it took so long to figure this out. Pretty much all IDEs are awful for text editing if you're a Vimmer but superior for the refactorings you're going to need. Programs like Vim are excellent text editors but JetBrains is never going to invest in VimScript.
I am occasionally frustrated that sometimes I am still forced to choose between one or the other because a lot of the servers are still in their infancy. Still waiting for the equivalent of Visual Studio 2019 as a language server that actually works with the projects I have to develop.
Once, I tried replacing IntelliJ with vim + deoplete + a linter plugin (whose name I forgot), because Intellij ate my laptop battery too quickly, and because I like editing text in vim more than IntelliJ.
Well, the vim setup ate through it twice as fast, and was slightly less responsive.
There could be something similar for vi too, though I have not explored this route.
Could this be a good compromise?
I found it to be a very good emulation of vanilla Vim, but also found when I was using Vim mode I hardly used any of the IDE features aside from passive JSDoc/outline/etc displays. That lack of integration between the two halves killed it for me, not to mention it'd been a long while since vanilla Vim was what I was really using.
I love the Language Server concept and hope that just becomes the thing going forward. There was never any reason specific language knowledge should be part of the editor, other than we didn't have a vertical enough ecosystem yet to coordinate a different way. Whatever else I can say about Microsoft, they accomplished that vertical pretty skillfully with VSC and TypeScript, and have been running with it like mad. I'm glad some of that has been to our general benefit.
I think you can change that to only on file save, though.
(I'm also a firm believer in getting familiar with the default keybindings for any program, so I don't use any vim emulation in an IDE. There's value in learning a set of shortcuts that are usable everywhere)
Every tool has trade-offs, knowing how to use each and when to used them is going to help you work faster and smarter.
They're not perfect, but I also made the switch from vim to CLion and never looked back. With a half-decent plugin for editing text, there's not much value in using a more minimalist vim setup IMHO.
At that point you might as well use QT Creator.
There are a ton of people that use Vim because it's productive, not because of how "lightweight" it is. I see no reason to shame people if they choose to use "heavy" plugins to achieve whatever functionality they want.
because vim wasn't exactly built with these kinds of features in mind and it shows when you start to throw a lot of IDE features into vim. For example switching to very large text files with vanilla vim, doing some search and replace is no problem, it's super fast.
Doing that with the wrong plugin on that does some CPU heavy stuff can very quickly make vim unresponsive.
IDEs are already built with a rich feature set in mind, so you can expect performance to be relatively stable. Vim or Emacs with the entire kitchen sink thrown in tends to be a little bit dicy
I disagree. Both Vim and Neovim are under active development, and have acquired many features to support IDE functionality.
> For example switching to very large text files with vanilla vim, doing some search and replace is no problem, it's super fast.
This has improved a lot in the last few years thanks to the async APIs.
EDIT: Also, the actual language server (clangd, ccls, etc) which is compiling/reindexing your project behind the scenes is generally the most resource intensive part of the whole setup - so even CoC with node is not a bad choice.
I don't understand why coc.vim is so popular, when Ale is so performant and "debuggable" (i.e. produces useful performance traces).
[long-time ale user here, I don't use clangd but I do use python-language-server, which is similar]
Is it that coc.vim doesn't make you install the binaries of the tools (i.e. go-pls, python-language-server, clangd)? If so, that would be enough to explain the popularity to me.
IMO, one could make the argument that it's somewhat antithetical to vim-culture, to not understand the non-vim binaries your editor is executing.
Do agree that coc is probably the least 'vim-culture'-ey part of such a setup, but hey it does good things. :) I don't think you need to justify using Ale so hard and put coc down, could just use whichever you prefer personally.
I'm trying to see if I'm missing something big/obvious, since coc.vim has been so popular recently.
coc.nvim has pretty much worked pretty well out of the box for many languages, and it works for me on macOS, Linux and Windows.
I use Vim not for philosophical or cultural reasons but because I enjoy the UX.
CoC's autocompletion is much more advanced than Ale. I used Ale for years.
I thought that both are pass-throughs to other completion engines, so isn't this a moot comparison? (maybe coc integrates the completion engine, but it doesn't implement a new one, right?).
I use deoplete, but I'm not married to it (it's kind of slow when you have a big file).
let g:deoplete#enable_at_startup = 1
call deoplete#custom#option({
\'auto_complete_delay': 1000,
\'sources': {
\ '_': ['ale', 'buffer'],
\ 'py': ['ale'],
\}
\})
If so, isn't the completion experience is strictly a function of the completion engine you're passing coc/ale into (ux), and the binaries that you're passing into coc/ale (completion options)?It is possible that coc.vim can do better. But I can't imagine how much better it should have be.
this has been my experience too.
I was editing some prolog the other day, `:ALEInfo` recommended swipl. worked like a charm!
the linters covered are quite incredible: https://github.com/dense-analysis/ale/tree/master/ale_linter....
And if you read each integration file, they are quite small and comprehensible. I've never needed to add one myself, but I feel like I could if I wanted to.
edit/ Of course, Google was able to pop it out after I submit this comment... For anyone else who may be curious about how this works: https://neovim.io/doc/user/lsp.html
Also, qtcreator is pretty darn good for Rust too
vim is lighter than qt creator is lighter than node.js + vim
Besides adding a Node dependency, Coc extensions are basically ported VSCode extensions with some changes necessary to make them work with Coc. This means that for every single extension you want to port to Coc, someone has to maintain a Coc fork and keep up with the upstream VSCode extension.
Personally, I just don't feel that this is sustainable.
let g:lsc_auto_map = {'Completion': 'omnifunc'}
let g:lsc_enable_autocomplete = v:false
let g:lsc_server_commands = {
\ 'c': {
\ 'command': 'clangd',
\ 'log_level': -1,
\ 'suppress_stderr': v:true,
\ },
\ 'cpp': {
\ 'command': 'clangd',
\ 'log_level': -1,
\ 'suppress_stderr': v:true,
\ },
\}
This gives me code completion and other niceness.With Vim 8.1, you don't even need a package manager, since there is one built in. Just clone the Git repo into your ~/.vim/pack/vim-lsc/start/vim-lsc/.
clangd eats big-ass, hairy, template-heavy TUs for breakfast as concerns responsiveness, and I have yet to see it make a mistaken autocomplete suggestion or fail to find a declaration that I agreed with. It’s easily the equal in precision of the Intellisense server that some of my colleagues use and doesn’t bog down the same way. Even the method renaming is pretty solid.
For me at least it’s a small but consistent productivity increase over any of the alternatives I’m aware of and fits into your existing emacs/vim/vscode workflow.
Would download for free again.
> For me at least it’s a small but consistent productivity increase over any of the alternatives I’m aware of
Have you tried Eclipse CDT?
Since nobody mentions it, I guess I have to. It takes some time to set it up (system paths, etc.) but once you do, it makes browsing and refactoring code a breeze.
It works much faster than Intellisense and stuff like dependency trees (call hierarchy, etc.) are much easier to navigate.
I'm using it as my main editor since 2014 or so. Have tried various alternatives on all 3 platforms (Linux, macOS, Windows), but I always go back to it for C++ dev.
I don't have any problem with people who want to use GUI IDEs, some are even pretty tempting (XCode with the DTrace integration is slick as hell), but I'm so married to having my development environment running on a big, always-on box somewhere under a tmux session that it doesn't make a ton of sense for me.
Plus at this point my fingers are so hardwired that I reconfigure my desktop environment to match Emacs and/or Vim text navigation rather than the other way around. Even when editing Algol/brace-type languages I can't imagine moving a lot of code around quickly without paredit-mode and friends.
I've used the `clang-9` chain on Ubuntu 18.04 and the `clang-10` chain on Ubuntu 20.04 and it's been absolutely seamless.
I can very much recommend QtCreator. I don't use Qt, but as an IDE it's very well made. The fact that it can also do Qt isn't even apparent when you use it on normal CMake projects and the like.
Extremely solid syntax highlighting is also important to me, which, in vim, is horribly difficult if not impossible. If I want something nested like `std::this_thread::get_id` to be highlighted properly, want macros to be a different color, want local scope variables to be a different style than class-scope or globals, vim is out of the question.
So yes, I can recomment vim for C++ development as long as you're not using a ton of libraries and are okay with most of your code being the exact same color.
On the PC I don't see much reason not to use an IDE anymore. VSCode and Eclipse are both great free options and full Visual Studio is pretty great if someone else pays for it.
With cscope as the front-end to vim, this problem rarely becomes unmanageable... especially if you get bindings set up and/or use NERDTree or better yet vim-vinegar ..
I think you might be misinformed; I've been getting code-completion and error reporting through vim for at least 8 years on C++, and I don't know how much longer before that it's been available.
I use xmonad, despite not knowing any Haskell. I basically just copy pasted little snippets from all over the place and changed random characters until it started working.
Pretty sure I still prefer this state of affairs to XML.
bindsym $mod+Tab exec --no-startup-id rofi -show windowmadness :) I could never manage to get to my window like that, it's alt + tab tab tab tab tab tab tab until I get where I want when on windows vs alt + tab down down down down on rofi so I don't see a lot of difference
Terminator isn't for everyone. But it can be for anyone.
But I set up Terminator in full tiled rig for everything else, and rarely every leave it. Its a delight.
add picom with dual kawase blur https://github.com/tryone144/picom/tree/feature/dual_kawase
add rofi
add zathura and some opacity to it via picom
gg
I love me some drop shadows and a nice theme though.
It can make a huge difference in the highlighting of the source, compared to Vim's regex-based approach. The reasoning for wanting this is to be able to soak in more information about the source without needing more text; the color alone can convey a lot of useful info about the code.
(somewhat) intelligent complete (ccomplete)
debugging (termdebug)
jump to definition (ctags)
reference finding (cscope)
file explorer (netrw)I found it needed some extra configuration before it felt like stock vim.
C-u isn’t bound to "scroll up" by default with evil, for example. You have to (setq evil-want-C-u-scroll t). That took a while to figure out and I expected it to work that way by default.
Then you have to (setq evil-want-keybinding nil) and install evil-collection and evil-magit to be able to use vim movements in other Emacs buffers.
Or you have to learn Emacs shortcuts and just use vim for editing, but that feels awkward for a vim native.
As an Emacs newbie I still find it unsettling that documentation for packages and for Emacs itself all uses Emacs-style shortcuts. Of course it couldn’t really be another way, but I either have to learn those or remap things to vim-style leader bindings. It makes vim usage in Emacs feel a little uncanny at times.
I could use a distro like Doom which has it all wired up for vimheads already, but then I’m learning someone else’s shortcuts and system, which takes away from the point of Emacs a little for me (the idea that you make it your editor).
Maybe it gets easier with time. If any other vim users made the move to Emacs I’d love to hear some tips!
It's like the small things such as binding C-u to scroll up by default are taken care of for you out of the box. How many people who use Evil who are already familiar with Vim are going to not end up setting that configuration option in plain Emacs at some point? (Maybe those who use page up and page down, but not even those commands do the same thing as C-u and C-d, at least in Evil.) Everyone else will have to add that line to their config, and that quickly gets unmaintainable if what you actually want to do is focus on work instead of building a proper Emacs framework. So it is set for you already.
Doom is basically that for Evil configuration and several thousand other things.
One downside is having to undo some of Doom's built-in things if they're annoying. One example of this is having to insist that it uses a specific set of completion handlers and not globally set a hardcoded list for every mode. Still, the amount of time you'd have to do this is less than the amount of time it takes to build 30% of Doom for just the features you want from scratch.
And yes, it is at times a sad feeling needing to fall back on someone else to do the hard work for you, but the realities of work and limited free time essentially dictated that decision for me.
I started with Doom, loved it, but wanted to understand Emacs better so ended up writing my own config.
I now understand Emacs a _little_ more, I know what every line of my config is doing, and there isn't an extra layer of abstraction or opaque bash commands I have to periodically run. I also feel more comfortable that Emacs with a home-grown config will last me 10+ years. It's incredible what Henrik has achieved and the level of dedication and support is astounding, but I wonder how sustainable it is as the community inevitably grows.
Rolling my own setup has certainly made me appreciate Doom a whole lot more, though. And it's hard to shake the feeling that I could spend a year or two just hacking on Emacs to get half of what Doom offers.
It's like the trap of wanting to get into game programming and finding yourself writing a game engine instead of a game. (I picked up Emacs to learn Clojure and other Lisp dialects better.)
It's also a little silly to have an expectation that I need to understand everything that Doom does behind the scenes. I don't have that expectation with Emacs itself, or with IntelliJ or vim, or VS Code…
Maybe the answer is to just read the (smallish) Doom codebase to understand it better and contribute however I can, be it in Discord, with cash, or PRs. And get back to hacking instead of hacking Emacs…
FWIW, I went with this approach and found it to be the sanest/best for me. The attractiveness of vim is the text editing experience.
If I put the setup in my home directory, I am limited to a single setup (compiler, stdlib).
If I put it into the project directory, I am limited to a single build for, and potentially a single branch, and I will have multiple instances of the lsp running if I stop working on one project.
If I put it into the build for, which seems sensible, how does the editor decide which server to run or ask?
It seems to me that I need some sort of project manager that acts like a lap proxy and then resolves these issues. Do I need to write one myself?
+ ~/repos/repoA
| - CMakeLists.txt
| - compile_commands.json -> ../../build/repoA_config1/compile_commands.json
|
+ ~/build/repoA_config1
| - CMakeCache.txt
| - build.ninja
| - compile_commands.json
|
+ ~/build/repoA_config2
| - CMakeCache.txt
| - build.ninja
| - compile_commands.json
etc. for repoB, C, ...That said I'm thinking on flipping it around - keep the LS configuration in the build folder, and symlink the source folder in it. That way I can have several different configurations in effect at once and switch between them easily. Currently I have to change where compile_commands.json points to if I want to change YouCompleteMe's completions.
Your editor should manage the language server and you should just have to provide the configuration it expects in the directory you start it in.
There is one clangd process per editor process. If you have multiple projects with distinct compile_commands you might be able to come-up with a scheme to switch between DBs and relaunch clangd. Alternatively, you could try merge all compile_commands together, but I'm not sure the effort would be worth it -- just running separate vims seems like it makes the most sense to me.
For switching between branches, you can typically regenerate the compile commands DB pretty easily (if you use cmake...), but it's really only necessary if there are big differences in files and/or compile settings between branches.
At the moment, I run everything (toolchain, LSP server, nvim) in a toolbox. Works well, but it feels unecessary to have a copy of vim in every container.
vim nowadays can find all the headers, do linter and auto-fix and auto completion, and when you're coding, you rarely need move the mouse at all, vim 8.2+ is itself a modern IDE inside a terminal, again, light and fast, I enjoy it.
Basically, most the slowness/bulkiness of IDEs does not come from the text editing portion.
I currently am using that, along with the https://github.com/stefandtw/quickfix-reflector.vim for quick refactors, and it seems to work fairly well.
Is it because of method signatures or is there a killer feature that I'm unaware of ?
Is there an LSP server that can do something similar?
- Constant API updates to IntelliJ internals requiring updates to the extension.
- The need to run a full copy of IntelliJ in the background, and breakage in the copy leading to requiring a full restart.
- A lot of concurrency problems I didn't understand because by necessity it required going deep into IntelliJ's codebase.
- I was no longer using Java in my work environment or for personal projects, so I had no need to use IntelliJ.
But despite this, the repo stands as the most-starred one I have. People are still starring it even though I haven't made changes in over a year and a half!
https://github.com/Ruin0x11/intellij-lsp-server
So it sounds like this is a thing people would use, if someone wants to take up the maintenance burden.
Plug -> built-in vim packages CoC -> built-in LSP client
Pithy claims aside, it has been an interesting journey actually using Vim for producing C++ code for large projects. There's a strong DIY element and you end up with a setup that's much more customised than a vanilla IDE - on the one hand giving you a certain degree of comfort in your work, but on the other a lot of what you do is unportable to other people. The language server protocol has been a pretty good development, I'm keeping an eye on what comes out of those efforts, though I'm not using one personally right now.
My current setup consists mainly of exuberant-ctags for static indexing, YouCompleteMe for intellisense and simple navigation, rtags for rich search and light refactoring and fzf for searching. As to why I ended up settling on these plugins:
- ctags:
Absolutely essential. Yes, a full semantic index straight from the compiler is better, but ctags is more than good enough for just telling you "where is this defined". When paired with fzf, you can type part of the tag, space, part of the directory and you'll get all tags in that directory. That's due to fzf doing super simple substring matching in the TAGS file's lines based on your query, there's nothing smart going on here. This tool is also very good when you are developing multiplatform software, because the compiler cannot see all places where a function or class is defined, since you have one definition per platform. Technically being able to query a plethora of language servers, one for each configuration, would be ideal, but we're not there yet, and such a setup sounds way too fragile for my taste.
- YouCompleteMe:
Aside from C++, it provides amazing completion support for many other languages. It can even deal with java project files, if the readme is to be believed. Other than for C++, I mainly use it for python. It's also good in plaintext files for completing words and file paths. I really love its substring matcher, just type gTFBT to get "getTheFooBarThing". Downsides: 1. if your build system cannot produce a compile_commands.json file, you will need to write some python code to specify compilation flags; 2. compiles each file individually, so if some of your headers aren't freestanding, you'll get spurious errors when editing them.
- rtags:
Basically a language server from before the LSP protocol existed. Mainly developed for Emacs, but the Vim integration is good enough. Continuously indexes your source based on a compile_commands.json file. It can show you who calls a function or uses a variable, class hierarchies, virtual method overloads and more. I've seen it get confused if you jump branches and require you to delete and recreate the index, but I haven't experienced that lately.
Apart from those I use a bunch of standard productivity plugins like unimpaired, surround, nerdtree, etc. etc. Vim 8's gdb integration has been very welcome, too. I struggled to find a good interface to gdb previously, as everything seems to want to be Visual Studio and give you a restricted clicky interface, whereas I just wanted the current vim buffer to be synchronised with the instruction pointer and to write gdb commands directly.
I don't feel like I'm substantially slower in Vim than my colleagues in other editors and IDEs. Colleagues across the company use Vim, Emacs, VSCode, QtCreator, and Code::Blocks. The Windows developers are all on Visual Studio naturally. I haven't noticed being slower than any of them. The above plugins are enough for the odd rename-refactor I have to do, and anything more substantial is so infrequent, that doing it manually doesn't really have an impact. Also, developing on Linux, I always feel like any graphical application will just disintegrate or cause an unrecoverable freeze in the graphics driver at any random keystroke.
Back in the day I had to make do with just ctags, cscope and debugging straight from the commandline, lol. I do believe that Stallman's refusal to allow easily plugging into GCC's AST, like one can just use clang for indexing, held back C++ development tools on the Linux platform significantly. Eclipse's CDT, KDevelop, Anjuta, Code::Blocks, NetBeans, etc. - they all suffered from having to write their own C++ indexers that were always inferior to getting semantic information straight from the compiler. I'm extremely grateful to the open-source community for the continuing work on development tools, and try to donate where I can. Watching the development tools landscape change over the years has been interesting - things don't so much improve, as they evolve chaotically with an overall trend towards improvement. Like anything else on this platform. So far many IDEs have come and gone, but Vim is eternal.
I will say that using Vim is entirely up to your taste, it doesn't seem to be substantially faster or slower than an IDE, so use what you like. It is much cheaper than Qt Creator or CLion (AFAIK for both you need a license to develop commercial software), so that might be a factor for you. Companies tend to have a big Windows development team, with a few Linux developers, so they buy bulk Visual Studio licenses and individual Linux IDE licenses, making the latter end up costing more per developer.
Qt Creator is GPLv3 (https://github.com/qt-creator/qt-creator/blob/master/LICENSE...). You can do whatever you want with it.
You might also have good luck with a curated vim distribution such as SpaceVim[1], where everything should be pre-installed for you.
Later, NeoVim 0.5 is going to have LSP out of the box[2]. So things are very positive.
Its such a joy to have cscope bindings and gdb-based debugging direction within vim itself.
This has been a joy to re-discover after forgetting that vim has a great integrated debugger now. I keep forgetting it, but man has it been great to use lately.
To me, VSCode is much better experience from performance and LSP integration perspective. Everything works pretty much as you would expect. I use `ccls` and `goto-symbol-stack` for better tag browsing -- which gives pretty close experience to Emacs "M-." or Vim's "C-[" and go back shortcuts.
I don't use any CMake extension. You can basically create tasks and assign one of them as build. I just use those task to run the CMake commands, or my own scripts which sync and compile code remotely.
There are some other awesome plugins (I use Git ones), which are incredibly good.
If you work on Windows, I hear integration with Linux Subsystem is excellent, and there is some other features for general remote development that I often try.
Often, eventually after a day or two of use, I would still get back to Emacs, because what I absolutely miss is years of conditioning and customizability of Emacs. The keybindings are exactly how I want (and they make sense), window splits and workspaces (are in my opinion superior to VSCode), things popup and close when I need them and keybindings around them are straightforward.
Bottom line is, if you are hardcore Vim/Emacs user --
(1) you'd probably appreciate the convenience, excellent performance with LSP and bigger files, things working out of box, font sizes/colors being consistent, and nicer UI around code browsing. (2) At the same time you'd miss certain essential things that are heavily engrained into you. Lot of things will be good, but not good enough -- specifically related to keybindings/window splitting/buffer management.
What am I missing?
It's still very very young and in development, so not ready for prime time, but it "just works" and doesn't make me feel stupide the way CMake does...
It has almost the same syntax as Bazel, but its implemented in C++.
Super-fast and tend focus on Ninja build files.
I've even created a fork that works for Swift here and it works pretty well.
Its the meta-build project for Chrome, so its optimized for big projects with a lot of dependencies. I dont actually understand why is not much widespread giving the bad shape of meta-build systems (like CMake).