Skip to content

vimrc tips

Pontus Leitzler edited this page Mar 19, 2021 · 32 revisions

In this wiki we cover a bit more detail on what needs to be in your .vimrc/.gvimrc, followed by a number of tips and tricks with your configuration.

What do I need in my .vimrc/.gvimrc?

If you have a .vimrc file, minimal.vimrc shows the bare bones required in order to get govim up and running.

If you only have a .gvimrc file (i.e. you do not have a .vimrc file and only use GVim), then the minimal.vimrc settings should be superfluous; gvim is more "modern" out of the box. But if you are having issues, be sure to check you aren't unsetting/overriding the required values in minimal.vimrc.

πŸ’‘ Tip: syntax highlighting

To turn on syntax highlighting for *.go files:

syntax on

πŸ’‘ Tip: triggering "hover" information via a key mapping

The GOVIMHover function triggers hover information to be shown for the identifier under the cursor as opposed to the mouse. This is very useful for people who tend to use the keyboard as much (if not more) than the mouse.

You can create a mapping to call this function, by adding something like the following:

" $HOME/.vim/ftplugin/go.vim

nmap <silent> <buffer> <Leader>h : <C-u>call GOVIMHover()<CR>

(See :help mapleader for details on the special <Leader> key)

πŸ’‘ Tip: customising key mappings

govim defines some default key mappings within Go files. To override these, create an after-directory ftplugin:

" $HOME/.vim/after/ftplugin/go.vim

nnoremap <buffer> <silent> <C-]> :hide GOVIMGoToDef<cr>

πŸ”¨ Fix: why does Vim flicker when I enter normal mode (leave exit insert mode)?

@mvdan tracked down this article which explains the reason behind the delay and also a simple fix by adding the following to your .vimrc:

" $HOME/.vimrc

set timeoutlen=1000 ttimeoutlen=0

πŸ’‘ Tip: mapping to open quickfix window without shifting focus

The following mappings map the <F2> key to open the quickfix window if there are errors but does not shift focus to the quickfix window (unlike the :cw command):

" $HOME/.vim/after/ftplugin/go.vim

nmap <silent> <buffer> <F2> :execute "GOVIMQuickfixDiagnostics" ^V| cw ^V| if len(getqflist()) > 0 && getwininfo(win_getid())[0].quickfix == 1 ^V| :wincmd p ^V| endif<CR>
imap <silent> <buffer> <F2> <C-O>:execute "GOVIMQuickfixDiagnostics" ^V| cw ^V| if len(getqflist()) > 0 && getwininfo(win_getid())[0].quickfix == 1 ^V| :wincmd p ^V| endif<CR>

Note: ^V| is achieved by typing <C-v>v|. Or in long form: hold down left control, press v, press v again, release left control, then press |

As noted in the comment, these mappings should be added to the $HOME/.vim/after/ftplugin/go.vim file, not your .vimrc (or equivalent).

#277 is tracking the ability to automatically open/close the quickfix window as and when there are errors/not.

πŸ”¨ Fix: Unknown function: govim#config#Set when using Vim8 packages to load govim

If you use Vim8 packages to load govim and have calls to govim#config#Set in your .vimrc, you will see errors like:

Error detected while processing /home/myitcv/.vimrc:
E117: Unknown function: govim#config#Set

Vim8 packages get loaded after the user's .vimrc. Hence the config autoload functions will not yet be available.

As a workaround, put all the calls to govim#config#Set at the end of your .vimrc and simply preface them as follows:

" $HOME/.vimrc

...
" force load Vim8 packages early
packadd govim
call govim#config#Set("FormatOnSave", "")

πŸ’‘ Tip: show error signs in the number column

If you already have line numbers shown, having an additional gutter to the left for error signs (markers which correspond to errors in the quickfix window) can take up valuable screen real estate. Instead, configure Vim to show signs in the number column via:

" $HOME/.vimrc

set signcolumn=number

This requires Vim >= v8.1.1564

πŸ’‘ Tip: show hover popups below-and-to-the-right of the mouse/cursor

The current default of placing hover popups above-and-to-the-right might not be to your taste; you might prefer the old Vim "balloon" default of below-and-to-the-right. If that is the case, you can configure things differently. Add something like this to your .vimrc:

" $HOME/.vimrc

call govim#config#Set("ExperimentalMouseTriggeredHoverPopupOptions", {
      \ "mousemoved": "any",
      \ "pos": "topleft",
      \ "line": +1,
      \ "col": 0,
      \ "moved": "any",
      \ "wrap": v:false,
      \ "close": "click",
      \ "padding": [0, 1, 0, 1],
      \})

See :help popup_create-arguments for details of the various options that can be set for ExperimentalMouseTriggeredHoverPopupOptions and ExperimentalCursorTriggeredHoverPopupOptions. The only difference is that "line" and "col" are interpreted as values relative to the mouse/cursor position.

πŸ’‘ Tip: auto-insert closing parentheses, braces, quotes etc

Vim does not have such a feature by default, but the excellent https://github.com/jiangmiao/auto-pairs plugin does. In the future we may look to distribute this with govim for a nice out-of-the-box experience.

πŸ’‘ Tip: govim is trampling over my quickfix window - what can I do?

By default, govim automatically populates the quickfix window with diagnostic errors reported by gopls. But it's easy to turn this off and only have it populate the quickfix window on demand. Simply add the following to your .vimrc (or equivalent):

call govim#config#Set("QuickfixAutoDiagnostics", 0)

Then use :GOVIMQuickfixDiagnostics to populate the quickfix window with diagnostics.

πŸ’‘ Tip: change gutter sign appearence

There are four different highlights for signs defined by govim:

  • GOVIMSignErr
  • GOVIMSignWarn
  • GOVIMSignInfo
  • GOVIMSignHint

The default values can be overridden in .vimrc to get custom colors that matches your theme, e.g.:

highlight GOVIMSignErr ctermfg=15 ctermbg=1 guisp=Blue guifg=Blue

If you would like to change the what is shown in the gutter from the default >> it is possible to override the sign definition in .vimrc, e.g. where warnings are changed to !!:

call sign_define("GOVIMSignWarn", {"text":"!!","texthl":"GOVIMSignWarn"})

πŸ’‘ Tip: how do I make autocompletion work with govim?

There are a number of plugins that enable autocompletion. With thanks to @mcesar, here is one such way:

function! Omni()
    call asyncomplete#register_source(asyncomplete#sources#omni#get_source_options({
                    \ 'name': 'omni',
                    \ 'whitelist': ['go'],
                    \ 'completor': function('asyncomplete#sources#omni#completor')
                    \  }))
endfunction

au VimEnter * :call Omni()

inoremap <expr> <Tab>   pumvisible() ? "\<C-n>" : "\<Tab>"
inoremap <expr> <S-Tab> pumvisible() ? "\<C-p>" : "\<S-Tab>"
inoremap <expr> <cr>    pumvisible() ? "\<C-y>" : "\<cr>"

Then you should find that autocompletion works as you type.

πŸ’‘ Tip: wrapping to the next error in the current buffer

Vim has a helpful command :cbelow which will go to the next error in the current buffer below the cursor location, but it will not wrap around to the top of the file. If you would like to have a shortcut which goes to the next error in the file and wraps around to the top, you can define a command and bind it to a keystroke with something like this in your .vimrc:

command! Cnext try | cbelow | catch | cabove 9999 | catch | endtry
nnoremap <leader>m :Cnext<CR>

πŸ’‘ Tip: use fzf with workspace symbol search

fzf is a popular fuzzy finding implementation and interface for Vim. fzf can be configured with various sources: file hierarchies, process lists, git grep results... the list can (and does) go on.

Workspace symbol search however is slightly different, because the search results can (and do) change with each refinement of the search query, especially with fuzzy matching. Hence a slightly different approach is required when it comes to using gopls' workspace symbol method as an fzf source.

The following configuration, placed in your .vimrc, will map \s (or whatever <Leader>s is configured as) to start an fzf workspace symbol search. When you have found and selected the result you want:

  • Enter will jump to that result in the current window
  • <Ctrl-s> will open open a split and jump to the result
  • <Ctrl-v> will open a vertical split and jump to the result
  • <Ctrl-t> will open a new tab and jump to the result
" GovimFZFSymbol is a user-defined function that can be called to start fzf in
" a mode whereby it uses govim's new child-parent capabilities to query the
" parent govim instance for gopls Symbol method results that then are used to
" drive fzf.
function GovimFZFSymbol(queryAddition)
  let l:expect_keys = join(keys(s:symbolActions), ',')
  let l:source = join(GOVIMParentCommand(), " ").' gopls Symbol -quickfix'
  let l:reload = l:source." {q}"
  call fzf#run(fzf#wrap({
        \ 'source': l:source,
        \ 'sink*': function('s:handleSymbol'),
        \ 'options': [
        \       '--with-nth', '2..',
        \       '--expect='.l:expect_keys,
        \       '--phony',
        \       '--bind', 'change:reload:'.l:reload
        \ ]}))
endfunction

" Map \s to start a symbol search
"
" Once you have found the symbol you want:
"
" * Enter will open that result in the current window
" * Ctrl-s will open that result in a split
" * Ctrl-v will open that result in a vertical split
" * Ctrl-t will open that result in a new tab
"
nmap <Leader>s :call GovimFZFSymbol('')<CR>

" s:symbolActions are the actions that, in addition to plain <Enter>,
" we want to be able to fire from fzf. Here we map them to the corresponding
" command in VimScript.
let s:symbolActions = {
  \ 'ctrl-t': 'tab split',
  \ 'ctrl-s': 'split',
  \ 'ctrl-v': 'vsplit',
  \ }

" With thanks and reference to github.com/junegunn/fzf.vim/issues/948 which
" inspired the following
function! s:handleSymbol(sym) abort
  " a:sym is a [2]string array where the first element is the
  " key pressed (or empty if simply Enter), and the second element
  " is the entry selected in fzf, i.e. the match.
  "
  " The match will be of the form:
  "
  "   $filename:$line:$col: $match
  "
  if len(a:sym) == 0
    return
  endif
  let l:cmd = get(s:symbolActions, a:sym[0], "")
  let l:match = a:sym[1]
  let l:parts = split(l:match, ":")
  execute 'silent' l:cmd
  execute 'buffer' bufnr(l:parts[0], 1)
  set buflisted
  call cursor(l:parts[1], l:parts[2])
  normal! zz
endfunction