Skip to content

Plugins That Make (Neo)Vim A Usable Terminal IDE

Right out of the box Neovim and Vim are rather primitive looking terminal text editors. Although complex and rich in features, most people stay away from these programs and shoot for nano. Fortunately these editors have a feature to install a 3rd party plugins to integrate into the environment. If you spend a lot of time in the terminal and want a quick way to modify text, programming or configuration files without leaving to modify in a GUI text editor, continue on to learn how to super-charge the new fork of Vim called NeoVim. NeoVim makes plugin integration even easier so here is how to install it and manage plugins with Vim-Plug to have a better terminal experience.

Installing Neovim and the Vim-plug plugin manager

Installing on Windows and Linux is trivial. You can use the Chocolately package manger to install on Windows. If you are on Linux you can use your distribution’s package manager to install.

# Windows installation with Chocolately
choco install neovim 

# Linux installation
[apt|apt-get|dnf|pacman] install neovim 

With Neovim installed, we can now install Vim-Plug to install and manage our plugins.
Here are the commands to install on both platforms.

# Windows Installation
md ~\AppData\Local\nvim\site\autoload
$uri = ''
(New-Object Net.WebClient).DownloadFile(

# Linux installation
sh -c 'curl -fLo "${XDG_DATA_HOME:-$HOME/.local/share}"/nvim/site/autoload/plug.vim --create-dirs \'

Now we need to hook Vim-Plug into our init.vim configuration file. If the config file does not exist just create a new file and add the following call to Vim-Plug with the call to plug#begin()/plug#end().

# init.vim

# Windows config location

call plug#begin('~/AppData/Local/nvim/plugged')
Plug 'tpope/vim-surround'
Plug 'easymotion/vim-easymotion'
call plug#end()

# Linux config location

call plug#begin('/home/<user>/.local/share/nvim/plugged')
Plug 'tpope/vim-surround'
Plug 'easymotion/vim-easymotion'
call plug#end()

Open up Neovim and type the :PlugInstall command to confirm that our plug-in manager is working.

We can now install Neovim plugins! Although there are many formats to install plugins, I normally use the following format that uses the explicit URL to the plugin’s repository on Github.

Plug ''

The best plugins for NeoVim

Here are some plugins that will spruce up your editor and make file navigation quick and simple.

  • Limeline

    Limeline creates a configurable status line on the bottom of NeoVim
Plug ''
  • NerdTree

    NerdTree is the de facto file explorer plugin.
Plug 'scrooloose/nerdtree', { 'on':  'NERDTreeToggle' }
  • Control-P

    Have quick access to your project files with Control-P.
Plug ''

Depending on your needs there are plenty of plugins to fit your specific situation. Now that you know how to install them, you can just find a plugin and enter the plug <URL> command in your init.vim file now.

Don’t forget to configure your configuration file even more.

You can even make edits to the init.vim to make your even better like having mouse capabilities, clipboard defaults, plugin shortcut commands, syntax highlighting and more. If you are looking for pointers you can check my config file below or grab the raw copy at my gist on github.

scriptencoding utf8

" system clipboard (requires +clipboard)
" Set clipboard^=unnamed,unnamedplus

" additional settings
set modeline           " enable vim modelines
set hlsearch           " highlight search items
set incsearch          " searches are performed as you type
set number             " enable line numbers
set confirm            " ask confirmation like save before quit.
set wildmenu           " Tab completion menu when using command mode
set expandtab          " Tab key inserts spaces not tabs
set softtabstop=4      " spaces to enter for each tab
set shiftwidth=4       " amount of spaces for indentation
set shortmess+=aAcIws  " Hide or shorten certain messages
set relativenumber
set clipboard=unnamedplus

let g:netrw_altv = 1
let g:netrw_liststyle = 3
let g:netrw_browse_split = 3

" ------ leader mapping ------
let g:mapleader = "<space>"

map <Leader> <Plug> e

" ------ command shortcuts ------
:command FZ FZF
:command EX Explore
:command PI PlugInstall
:command PC PlugClean
:command NT NERDTree

" ---- deoplete mappings -------
inoremap <expr> <C-j> pumvisible() ? "\<C-n>" : "\<C-j>"
inoremap <expr> <C-k> pumvisible() ? "\<C-p>" : "\<C-k>"map <Leader> <Plug>

" ------ nerd map ctrl + n  mapping ------
nmap <C-n> :NERDTreeToggle<CR>

" ------ enable additional features ------
let g:ycm_python_binary = 'python'

" -------- theme --------
set background=dark

" -------- limeline -------
set noshowmode
let g:lightline = { 
            \ 'colorscheme' : 'jellybeans', 
            \ 'active': {
            \   'left': [[ 'mode', 'paste'],
            \       ['gitbranch', 'readonly', 'filename', 'modified']]
            \ },
            \ 'component_function': {
            \       'gitbranch': 'fugitive#head'
            \ },
            \ }

" ------- vim plug ---------
call plug#begin('~/.local/share/nvim/plugged')

Plug 'tpope/vim-surround'
Plug 'easymotion/vim-easymotion'

Plug ''
Plug ''
Plug ''
Plug ''
Plug ''
Plug ''
Plug ''
Plug ''
Plug ''
Plug ''
Plug ''
Plug ''
Plug ''
Plug ''
Plug ''
Plug ''
Plug ''
Plug ''
Plug ''

" On-demand loading
Plug 'scrooloose/nerdtree', { 'on':  'NERDTreeToggle' }
Plug 'tpope/vim-fireplace', { 'for': 'clojure' }
Plug 'Shougo/deoplete.nvim', { 'do': ':UpdateRemotePlugins' }

call plug#end()

" move from insert to normal mode
inoremap nn <esc>

" enable mouse
set mouse=a
if has('mouse_sgr')
    " sgr mouse is better but not every term supports it
    set ttymouse=sgr

" syntax highlighting
syntax enable

set linebreak breakindent

" change cursor shape for different editing modes, neovim does this by default
if !has('nvim')
    if exists('$TMUX')
        let &t_SI = "\<Esc>Ptmux;\<Esc>\e[5 q\<Esc>\\"
        let &t_SR = "\<Esc>Ptmux;\<Esc>\e[4 q\<Esc>\\"
        let &t_EI = "\<Esc>Ptmux;\<Esc>\e[2 q\<Esc>\\"
        let &t_SI = "\e[6 q"
        let &t_SR = "\e[4 q"
        let &t_EI = "\e[2 q"

" ------ commands ------

command! D Explore
command! R call <SID>ranger()map <Leader> <Plug>
command! Q call <SID>quitbuffer()
command! -nargs=1 B :call <SID>bufferselect("<args>")
command! W execute 'silent w !sudo tee % >/dev/null' | edit!

" ------ basic maps ------

" open ranger as a file chooser using the function below
nnoremap <leader>r :call <SID>ranger()<CR>

" match string to switch buffer
nnoremap <Leader>b :let b:buf = input('Match: ')<Bar>call <SID>bufferselect(b:buf)<CR>

" change windows with ctrl+(hjkl)
nnoremap <C-J> <C-W><C-J>
nnoremap <C-K> <C-W><C-K>
nnoremap <C-L> <C-W><C-L>
nnoremap <C-H> <C-W><C-H>

" alt defaults
nnoremap 0 ^
nnoremap Y y$
nnoremap n nzzzv
nnoremap N Nzzzv
nnoremap <Tab> ==1j

" re-visual text after changing indent
vnoremap > >gv
vnoremap < <gv

" toggle line numbers, nn (no number)
nnoremap <silent> <Leader>nn :set number!

" gj/k but preserve numbered jumps ie: 12j or 45kmap <Leader> <Plug>
nmap <buffer><silent><expr>j v:count ? 'j' : 'gj'
nmap <buffer><silent><expr>k v:count ? 'k' : 'gk'

" open a terminal in $PWD
nnoremap <silent> <Leader>tt :terminal<CR>

" tab control
nnoremap <silent> <M-j> :tabmove -1<CR>
nnoremap <silent> <M-k> :tabmove +1<CR>
nnoremap <silent> <Leader>te :tabnew<CR>
nnoremap <silent> <Leader>tn :tabnext<CR>
nnoremap <silent> <Leader>tf :tabfirst<CR>
nnoremap <silent> <Leader>tp :tabprevious<CR>

" close current buffer and/or tab
nnoremap <silent> <Leader>q :B<CR>:silent tabclose<CR>gT

" open a new tab in the current directory with netrw
nnoremap <silent> <Leader>- :tabedit <C-R>=expand("%:p:h")<CR><CR>

" split the window vertically and horizontally
nnoremap _ <C-W>s<C-W><Down>
nnoremap <Bar> <C-W>v<C-W><Right>

" ------ autocmd ------

" Reload changes if file changed outside of vim requires autoread
augroup load_changed_file
    autocmd FocusGained,BufEnter * if mode() !=? 'c' | checktime | endif
    autocmd FileChangedShellPost * echo "Changes loaded from source file"
augroup END

" when quitting a file, save the cursor position
augroup save_cursor_position
    autocmd BufReadPost * call setpos(".", getpos("'\""))
augroup END

" when not running in a console or a terminal that doesn't support 256 colors
" enable cursorline in the currently active window and disable it in inactive ones
if $DISPLAY !=? '' && &t_Co == 256
    augroup cursorline
        autocmd VimEnter,WinEnter,BufWinEnter * setlocal cursorline
        autocmd WinLeave * setlocal nocursorline
    augroup END
map <Leader> <Plug>
" ------ adv maps ------

" strip trailing whitespace, ss (strip space)
nnoremap <silent> <Leader>ss
    \ :let b:_p = getpos(".") <Bar>
    \  let b:_s = (@/ != '') ? @/ : '' <Bar>
    \  %s/\s\+$//e <Bar>
    \  let @/ = b:_s <Bar>map <Leader> <Plug>
    \  nohlsearch <Bar>
    \  unlet b:_s <Bar>
    \  call setpos('.', b:_p) <Bar>
    \  unlet b:_p <CR>

" global replacemap <Leader> <Plug>
vnoremap <Leader>sw "hy
    \ :let b:sub = input('global replacement: ') <Bar>
    \ if b:sub !=? '' <Bar>
    \   let b:rep = substitute(getreg('h'), '/', '\\/', 'g') <Bar>
    \   execute '%s/'.b:rep."/".b:sub.'/g' <Bar>
    \   unlet b:sub b:rep <Bar>
    \ endif <CR>
nnoremap <Leader>sw
    \ :let b:sub = input('global replacement: ') <Bar>
    \ if b:sub !=? '' <Bar>
    \   execute "%s/<C-r><C-w>/".b:sub.'/g' <Bar>
    \   unlet b:sub <Bar>
    \ endif <CR>

" prompt before each replace
vnoremap <Leader>cw "hy
    \ :let b:sub = input('interactive replacement: ') <Bar>
    \ if b:sub !=? '' <Bar>
    \   let b:rep = substitute(getreg('h'), '/', '\\/', 'g') <Bar>
    \   execute '%s/'.b:rep.'/'.b:sub.'/gc' <Bar>
    \   unlet b:sub b:rep <Bar>
    \ endif <CR>

nnoremap <Leader>cw
    \ :let b:sub = input('interactive replacement: ') <Bar>
    \ if b:sub !=? '' <Bar>
    \   execute "%s/<C-r><C-w>/".b:sub.'/gc' <Bar>
    \   unlet b:sub <Bar>
    \ endif <CR>

" highlight long lines, ll (long lines)
let w:longlines = matchadd('ColorColumn', '\%'.&textwidth.'v', &textwidth)
nnoremap <silent> <Leader>ll
    \ :if exists('w:longlines') <Bar>
    \   silent! call matchdelete(w:longlines) <Bar>
    \   echo 'Long line highlighting disabled'
    \   <Bar> unlet w:longlines <Bar>
    \ elseif &textwidth > 0 <Bar>
    \   let w:longlines = matchadd('ColorColumn', '\%'.&textwidth.'v', &textwidth) <Bar>
    \   echo 'Long line highlighting enabled'
    \ <Bar> else <Bar>
    \   let w:longlines = matchadd('ColorColumn', '\%80v', 81) <Bar>
    \   echo 'Long line highlighting enabled'
    \ <Bar> endif <CR>

" local keyword jump
nnoremap <Leader>fw
    \ [I:let b:jump = input('Go To: ') <Bar>
    \ if b:jump !=? '' <Bar>
    \   execute "normal! ".b:jump."[\t" <Bar>
    \   unlet b:jump <Bar>
    \ endif <CR>

" quit the current buffer and switch to the next
" without this vim will leave you on an empty buffer after quiting the current
function! <SID>quitbuffer() abort
    let l:bf = bufnr('%')
    let l:pb = bufnr('#')
    if buflisted(l:pb)
        buffer #
    if bufnr('%') == l:bf
    if buflisted(l:bf)
        execute('bdelete! ' . l:bf)

" switch active buffer based on pattern matching
" if more than one match is found then list the matches to choose from
function! <SID>bufferselect(pattern) abort
    let l:bufcount = bufnr('$')
    let l:currbufnr = 1
    let l:nummatches = 0
    let l:matchingbufnr = 0
    " walk the buffer count
    while l:currbufnr <= l:bufcount
        if (bufexists(l:currbufnr))
            let l:currbufname = bufname(l:currbufnr)
            if (match(l:currbufname, a:pattern) > -1)
                echo l:currbufnr.': '.bufname(l:currbufnr)
                let l:nummatches += 1
                let l:matchingbufnr = l:currbufnr
        let l:currbufnr += 1

    " only one match
    if (l:nummatches == 1)
        execute ':buffer '.l:matchingbufnr
    elseif (l:nummatches > 1)
        " more than one match
        let l:desiredbufnr = input('Enter buffer number: ')
        if (strlen(l:desiredbufnr) != 0)
            execute ':buffer '.l:desiredbufnr
        echoerr 'No matching buffers'

" open ranger as a file chooser
function! <SID>ranger()
    let l:temp = tempname()
    execute 'silent !xterm -e ranger --choosefiles='.shellescape(l:temp).' $PWD'
    if !filereadable(temp)
    let l:names = readfile(l:temp)
    if empty(l:names)
    execute 'edit '.fnameescape(l:names[0])
    for l:name in l:names[1:]
        execute 'argadd '.fnameescape(l:name)

nnoremap <C-Left> :tabprevious<CR>                                                                            
nnoremap <C-Right> :tabnext<CR>
nnoremap <C-j> :tabprevious<CR>                                                                            
nnoremap <C-k> :tabnext<CR>

" set color needs to bee
" at the end of file to work
colorscheme iceberg

Michael has been a professional in the information technology field for over 10 years, specializing in software engineering and systems administration. He studied network security and holds a software engineering degree from Milwaukee Area Technical College with thousands of hours of self taught learning as well. He mainly writes about technology, current events, and coding. Michael also is the founder of Sof Digital, an U.S. based software development Firm. His hobbies are archery, turntablism, disc golf and rally racing.

Comments are closed.