openFrameworks / Nvim / Arch Linux

Published February 7, 2020

Lately I have had to get into c++ programming in order to do some live analysis of our MiniBees. We also want to start working with live visuals in our performances as well, so last year I got a hold of a second-hand Kinect, and the last couple of weeks I’ve been playing around with this in oF, a c++ version of the excellent java-based graphics programming environment Processing.

I have spent a lot of time tweaking my preferred coding environment, which is based on the text editor neovim, a modern fork of vim, so I wanted to keep using this for my c++ coding as well. Turns out this is not too hard!

Installing openFrameworks on Arch Linux

In order to get oF to work in Arch Linux you need to follow this guide. It’s all pretty straightforward: download the repository to your computer and put it in a reasonable location, run a couple of scripts, and off you go! The first script you need to run is $OF_ROOT/scripts/linux/archlinux/ This is a wrapper script for pacman that will download and install all the necessary dependencies. After this you probably want to run $OF_ROOT/scripts/linux/ This will compile all the libraries you need. Finally, you probably want to use the projectGenerator to create new projects, so run $OF_ROOT/scripts/linux/ as well.

The oF developers do not officially support the AUR package. Sticking with the official method of installation makes it easier to juggle different versions of oF later on.

Set up Neovim

In order to get an ergonomic environment set up, I use a couple of plugins: a.vim and vim-gutentags. I don’t use YouCompleteMe or any such thing, as I find nvims omnicomplete function sufficient for my needs. Use your favorite plugin manager to install these. I use vim-plug:

Plug 'ludovicchabant/vim-gutentags'
Plug 'vim-scripts/a.vim'


To get autocompletion (somehow) working for oF, you need to tweak gutentags a little bit. The plugin uses some default markers (the presence of .git, for example) to define the root of a project. Setting up the entire oF tree as one git project would be total overkill and defeat the whole purpose of this kind of framework, so it is easier and better to disable gutentags' defaults and add your own. In $XDG_CONFIG_HOME/nvim/init.vim add these lines:

let g:gutentags_project_root = ['.gutctags']
let g:gutentags_add_default_project_roots = 0

Next, add the .gutctags file to the oF root folder:

$ touch $OF_ROOT/.gutctags

Now, whenever you do anything in your oF tree, gutentags will quietly fill up a tag file with anything you need for nvim’s omnicomplete to work as expected.

The one downside to this approach is that your tag file does indeed fill up with EVERYTHING, so every little typo you made or other apps you wrote in the same oF tree become food for the tag generator. I don’t have a good solution for this yet.

[EDIT, 19 April 2020] In the meanwhile, my good friend Mads Kjeldgaard has gone through the trouble of setting up YouCompleteMe with openFrameworks, and has described the process in his blog. He’s also describing a better way of dealing with ctags.

[EDIT, 29 August 2020] After a bit of an openFrameworks hiatus, I am now getting back into it again, and have picked up a thing or two about the Language Server Protocol, an editor-agnostic way to deal with autocompletion, debugging, linting, tagging and so on. In order to make this work in neovim you need to install a couple of plugins:

Plug 'autozimu/LanguageClient-neovim', {
            \ 'branch': 'next',
            \ 'do': 'bash',
            \ } 
Plug 'Shougo/deoplete.nvim', { 'do': ':UpdateRemotePlugins' }

Install ccls, a language server for the C language family:

sudo pacman -S ccls

Use your favorite AUR installation method to get hold of compiledb:

yay -S compiledb

Configure LanguageClient-neovim in your init.vim:

let g:LanguageClient_serverCommands = {
            \ 'cpp': ['ccls', '--log-file=/tmp/cc.log'], 
            \ }

function LC_maps()
    if has_key(g:LanguageClient_serverCommands, &filetype)
        nnoremap <buffer> <silent> K :call LanguageClient#textDocument_hover()<cr>
        nnoremap <buffer> <silent> gd :call LanguageClient#textDocument_definition()<CR>
        nnoremap <buffer> <silent> <F2> :call LanguageClient#textDocument_rename()<CR>

autocmd FileType * call LC_maps()

We will need to generate a database. Go to the root directory of your project and create it like this:

cd ~/openFrameworks/apps/myExcellentApp
compiledb -n make

Finally we will generate the tags file for the entire openFrameworks tree locally. I experimented with a few different approaches, but this seems to work. Assuming that we are in the myExcellentApp directory, run this command:

ctags -R ~/openFrameworks/*

This will take a little while. In my case it generated roughly 42.000 tags. But now you should be able to go to any class definition by hitting Ctrl-]

Make Make Work

Coming from an interpreted language like SuperCollider to a compiled language like c++ is a bit painful, but with a couple of tweaks to nvim the transition can be smoother. This is where nvim’s excellent terminal capabilities enter into the picture. Add these lines to your init.vim:

augroup c
    autocmd FileType c,cpp,h,hpp,glsl call MakeRun()
augroup end

function! MakeRun()
    nnoremap <C-e> :terminal make -j8 && make run<cr>
    inoremap <C-e> <esc>:terminal make -j8 && make run<cr>

This will make nvim behave in a similar way to the scnvim plugin that I use for SuperCollider development. Whenever I’m in a source file I can hit ctrl-E, and a terminal window will open up, compile the project and then run it. Very sweet indeed.

One of the pains of c++ is that whole header/implementation file splitup. I’m sure there are very good reasons for it, but it’s just a bit frustrating sometimes. To ease the pain I’m using a.vim and mapping it to a keyboard sequence. In init.vim this becomes:

" new split with alternate file  
nnoremap mv :AV<cr>
" switch in same window
nnoremap ma :A<cr>

So, in normal mode, if I am working on ofApp.h and want ofApp.cpp in a vertical split, I hit mv. If I just want to switch in the current view, I hit ma. Very handy.