“What is a computer, Dad?” I asked this question 25 years ago as a 10-year-old boy. “It’s something like your FX-3600P CASIO calculator, but a bit more advanced!” my dad replied.
It was a well-defined answer, especially for a theoretical physicist working with a Commodore 64 at the university to solve complex quantum physics problems. At that time, the scientific calculator that I had inherited from my dad was the most advanced technology I could program.
This definition, coupled with the DOS-based PC I bought five years later, changed my perspective on computing systems forever. Even now, after 25 years — when GUI-based operating systems have become the dominant technology — any computing system still feels to me like a more advanced calculator.
I believe this is a familiar story for many of us as embedded system engineers. We navigate through Bash, synchronize with the kernel’s heartbeat, and connect via the serial port. Notifications from emails and social media, and colorful UIs send us into a panic. All we need is a black screen with lines of code to do our jobs.
This is a short tutorial for such engineers. For those who still think a terminal is enough to do their job. For those who believe a computer with 16KB of RAM and 70KB of disk is enough to launch a project and still be operational after 48 years of continuous working while escaping from our “Pale Blue Dot” with the speed of 17 km/sec (see Voyager program). For those who don’t require wide 4K UHD double monitors with a Core-i9 and 32GB RAM stack and an AI-based IDE to do their tasks.
Don’t get me wrong, I’m not a Luddite or a Technophobe. I just think that less is usually better — until we need more, As Exupéry once said:
Perfection is achieved, not when there is nothing more to add, but when there is nothing left to take away.
As we do in our profession as embedded engineers!
I therefore want to use a development environment that runs in a terminal (thus vi
), but still offers enough features to work efficiently (thus vi-improved, i.e. vim
).
Enough storytelling, let’s get started!
Installing prerequisites
No matter what distro you are using, these packages should be available from your distro.
For Ubuntu/Debian users:
$ sudo apt install vim ctags
and Fedora:
$ sudo dnf install vim ctags
Installing Plugin manager
Most of the features that make vim
improved are available with plugins. There are so many that downloading and installing them manually becomes cumbersome. So, we need a plugin manager to handle all stuff related to plugins. There are a wide variety of plugin managers that you may use. Here we will use vim-plug to install required plugins.
You can download and install vim-plug
with curl
as follows:
curl -fLo ~/.vim/autoload/plug.vim --create-dirs \
https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim
Install required plugins
There are a lot of different plugins that can be used, but as embedded engineers let’s not forget our nature and be minimalistic as much as possible by using the plugins that are required. We will use the following plugins:
coc.nvim
for code completion and language server integrationtagbar
for code structure navigationnerdtree
for file system navigationvim-cpp-enhanced-highlight
for better syntax highlighting in C/C++
Edit your ~/.vimrc
(with vim, obviously!) to include these plugins:
call plug#begin('~/.vim/plugged')
" Code completion and navigation
Plug 'neoclide/coc.nvim', {'branch': 'release'}
Plug 'preservim/tagbar'
Plug 'scrooloose/nerdtree'
" Syntax highlighting for C and C++
Plug 'octol/vim-cpp-enhanced-highlight'
call plug#end()
Install the plugins by running:PlugInstall
in Vim.
Configure Kernel Coding Style
The Linux kernel uses a strict coding style defined in Documentation/process/coding-style.rst
. Now we are going to configure Vim to adhere to it.
To do that, add the following lines to ~/.vimrc
:
" Kernel coding style
set tabstop=8 " Display tab as 8 spaces
set shiftwidth=8 " Indent with 8 spaces
set expandtab " Convert tabs to spaces
set cindent " Use C-style indentation
set autoindent " Auto-indent new lines
set smartindent " Enable smart indentation
" Show line numbers and ruler
set number
set ruler
" Highlight trailing whitespace
highlight ExtraWhitespace ctermbg=red guibg=red
match ExtraWhitespace /\s\+$/
" Enable mouse support (optional)
set mouse=a
" Folding based on syntax
set foldmethod=syntax
set foldlevel=99
" For device tree files, we need tabs
autocmd FileType dts,dtsi setlocal noexpandtab tabstop=8 shiftwidth=8
If each of the above settings does not suit your needs, remove them from the ~/.vimrc
file.
Enable Ctags for Easy Navigation
By calling make tags
in the kernel directory, the tags would be made automatically. However, for other sources, you can use ctags
.
Ctags generates an index of all identifiers in your codebase, allowing you to jump between functions, variables, and headers.
Generate tags for the any other source:
Go to the source directory and then run:
ctags -R --languages=C --c-kinds=+p --fields=+iaS --extras=+q .
This command will generate a huge tags
file that includes all used tags in the source code with their full path.
Add tags to vim
To navigate through the generated tags in vim, we should inform vim
that such file is available. To to that add this to ~/.vimrc
:
set tags+=/path/to/source/tags
You can now navigate with:
Ctrl-]
: Jump to a tag definition.Ctrl-t
: Return to the previous location.
Enable Language Server for Advanced Features
For features like autocomplete, linting, and code navigation, coc.nvim
should be configured with the ccls
language server.
ccls
is not available in the official repositories of the Fedora project. You can either directly download a binary executable from Releases · MaskRay/ccls .
As an alternative, you can build it from source on your host with the following command:
git clone --depth=1 --recursive https://github.com/MaskRay/ccls
cd ccls
cmake -H. -Bbuild -DCMAKE_BUILD_TYPE=Release
cmake --build build --target install
Then we should let coc.nvim
to use it: Open ~/.vimrc
and add:
let g:coc_global_extensions = ['coc-clangd']
tagbar
provides an outline of your code structure, such as functions, variables, and macros. To use it in vim
you can invoke it via the following command:
:TagbarToggle
You could assign a shortcut key to the TagbarToggle
, e.g. nmap <F4> :TagbarToggle<CR>
. Now, by pressing F4
you can toggle it.
Generate compile_commands.json
for code completion and syntax highlighting
cclc
or any language server require an input file that contains detailed information about how each source file in a project is compiled (compiler that is used, include paths, macro definitions, etc.). We generate this file by running the normal build steps within bear
command. The generated file is compile_commands.json
. For example, to generate the environment for the kernel:
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- defconfig
bear -- make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- -j$(nproc)
Place the compile_commands.json
in the kernel source directory or configure ccls
to locate it.
Enable Syntax Highlighting
Enable syntax highlighting for kernel source files:
:syntax on
Enable Error Checking with gcc
Integrate gcc
or clang
for real-time syntax and error checking.
Add the following to your ~/.vimrc
:
set makeprg=make\ ARCH=arm64\ CROSS_COMPILE=aarch64-linux-gnu-
set errorformat=%f:%l:%c:\ %m
Run:make
to compile the current file and display errors.
Optimize for Large Files
Kernel files can be large, so optimize Vim’s performance:
set lazyredraw
set nocompatible
set nohidden
set nowrap
Optional: Enable NERDTree for File Navigation
To navigate the source files, NERDTree
can be used. You can launch it with:
:NERDTreeToggle
Save and Reload Vim Configuration
Save your changes to ~/.vimrc
and reload Vim to apply the settings:
source ~/.vimrc
Full vimrc
file
Here you can find the full .vimrc
file from my system setup. You can modify it based on your needs:
call plug#begin('~/.vim/plugged')
" Code completion and navigation
Plug 'neoclide/coc.nvim', {'branch': 'release'}
Plug 'preservim/tagbar'
Plug 'scrooloose/nerdtree'
" Syntax highlighting for C and C++
Plug 'octol/vim-cpp-enhanced-highlight'
call plug#end()
" Kernel coding style
set tabstop=8 " Display tab as 8 spaces
set shiftwidth=8 " Indent with 8 spaces
set expandtab " Convert tabs to spaces
set cindent " Use C-style indentation
set autoindent " Auto-indent new lines
set smartindent " Enable smart indentation
" Show line numbers and ruler
"set number
set ruler
" Highlight trailing whitespace
highlight ExtraWhitespace ctermbg=red guibg=red
function! Preserve(command)
let l:search=@/
let l:line = line(".")
let l:col = col(".")
execute a:command
let @/=l:search
call cursor(l:line, l:col)
endfunction
match ExtraWhitespace /\s\+$/
" Enable mouse support (optional)
"set mouse=a
" Folding based on syntax
set foldmethod=syntax
set foldlevel=99
" Enable ctags for linux kernel
set tags+=/home/javad/workspace/embedded/linux-kernel/tags
let g:coc_global_extensions = ['coc-clangd']
"set makeprg=aarch64-linux-gnu-gcc\ -Wall\ -Wextra\ -c\ %
set makeprg=make\ ARCH=arm64\ CROSS_COMPILE=aarch64-linux-gnu-
set errorformat=%f:%l:%c:\ %m
set lazyredraw
set nocompatible
set nohidden
set nowrap
" Use <Tab> and <Shift-Tab> to navigate completion menu
inoremap <expr> <Tab> pumvisible() ? "\<C-n>" : "\<Tab>"
inoremap <expr> <S-Tab> pumvisible() ? "\<C-p>" : "\<S-Tab>"
" Use <Enter> to confirm selection or insert a newline
inoremap <expr> <CR> pumvisible() ? coc#_select_confirm() : "\<CR>"
"
highlight Pmenu ctermbg=black ctermfg=green
highlight PmenuSel ctermbg=blue ctermfg=white
highlight PmenuSbar ctermbg=gray
highlight PmenuThumb ctermbg=blue
" For dts
autocmd FileType dts,dtsi setlocal noexpandtab tabstop=8 shiftwidth=8
" For toggle bar
nmap <F4> :TagbarToggle<CR>