Development/Vim

From The Document Foundation Wiki
Jump to: navigation, search


The vim path variable

Setting of the vim path variable influences quite some things, like where vim looks for files when hitting gf for GotoFile under cursor, or the OmniCppComplete plugin (see below) looks for namespaces. A possible set in your ~/.vimrc would be

" Where to look for files in gf and ^Wf and similar commands,
" and for include files for i_^X^I and i_^X^D commands.
" The path starts with two commas ,, for searches in the current directory,
" this enables editing lists of files with relative paths without having to
" copy the list to the current directory first.
set path=,,.,./inc,./../inc,./../../inc,./../../../inc,./../../../../inc,$VIM_INC

where the shell environment variable VIM_INC before the first invocation of vim can be set to for example

export VIM_INC="$(pwd)/include,$(pwd)/workdir/UnoApiHeadersTarget/udkapi/normal,$(pwd)/workdir/UnoApiHeadersTarget/offapi/normal,$(pwd)/workdir/CustomTarget/officecfg/registry"

where $(pwd) prints the current working tree directory.

Indentation of keywords and blocks

To properly align a break keyword with the corresponding case keyword, not indent public: and private: within a class declaration and not indent inside a namespace block (if you know what hitting [[ or ]] does then you know why ;-) use these C-indent settings:

set cinoptions=b1,g0,N-s " C-indent: break aligned with case, public:/private: 0 indented, no indent in namespace
set cinkeys+=0=break     " entering break outdents

Useful macros to place in vimrc

Build the current module using Ctrl+K

To build the current file's module when hitting Ctrl+K place this snippet in your ~/.vimrc

" Call appropriate makeprg with Ctrl+K
map <C-K> :call Make()<CR>
 
if $SRC_ROOT == ""
    " Normal makeprg, not in LibreOffice environment
    function Make()
        make
    endfun
else
    " XXX NOTE: if BUILDDIR differs from SRC_ROOT (i.e. you build in
    " another directory than the source tree) this can not be used!
    " In your shell environment set SRC_ROOT to the LibreOffice source
    " tree directory, e.g. export SRC_ROOT=/build/libo/core
    " The SRC_ROOT variable can be grep'ed from config_host.mk
 
    let s:make_jobs = 6     " define how many jobs you want with make
 
    " The root of the directory where we stop going further up, excluding.
    " For example, if /libo/core is the tree's root, the root of that is /libo
    let s:dir_root = fnamemodify( $SRC_ROOT, ":h" )
    function GetMakeDirLimit( dir )
        return a:dir == s:dir_root
    endfunction
 
    function GetMakeModuleRoot()
        let l:mods = ":p:h"
        let l:dir = expand( "%" . l:mods )
        while !GetMakeDirLimit( l:dir ) && !filereadable( l:dir . "/Makefile" )
            let l:mods .= ":h"
            let l:dir = expand( "%" . l:mods )
        endwhile
        if GetMakeDirLimit( l:dir )
            let l:dir = expand( "%:p:h" )
        endif
        return l:dir
    endfunction
 
    function Make()
        let l:mymake = ""
        if has("gui_running")
            " Source some environment for detached gvim if needed.
            " It's up to you if and what you put in there or how you name
            " the file or where it is located.
            if filereadable( $SRC_ROOT . "/Env.Host.sh" )
                let l:mymake .= "source $SRC_ROOT/Env.Host.sh && "
            endif
        endif
        " cd into current file's directory before obtaining module's root
        let l:my_local_path = expand("%:h")
        if (l:my_local_path == "")
            let l:my_local_path = "."
        endif
        exec 'lcd ' . l:my_local_path
        let l:module = GetMakeModuleRoot()
        "let l:debugt = ""
        let l:debugt = " debug=true"
        if filereadable( l:module . "/Makefile" )
            " Build the entire module.
            exec 'lcd ' . l:module
            let l:mymake .= "make -rs -j" . s:make_jobs . " wall=true" . l:debugt
        else
            " Fallback to default, which likely bails out with unknown
            " target in the current file's directory.
            let l:mymake .= "make"
        endif
        let &makeprg = l:mymake
        make
    endfunction
endif

Show the issue in a browser

Of course this function would need to be hacked if firefox isn't the browser to be used, and new prefixes can be added. To use this function, simply place the cursor on a bug reference in the code like i#12345 and hit Ctrl+i.

" Get the issue number under the cursor. An issue number is containing
" digits, lating letters and #. The # characters are removed from the result.
" 
" Code heavily inspired from the words_tools.vim of Luc Hermitte
" http://hermitte.free.fr/vim/ressources/dollar_VIM/plugin/words_tools_vim.html
function! GetCurrentIssueText()
  let c = col ('.')-1
  let l = line('.')
  let ll = getline(l)
  let ll1 = strpart(ll,0,c)
  let ll1 = matchstr(ll1,'[0-9#a-zA-Z]*$')
  if strlen(ll1) == 0
    return ll1
  else
    let ll2 = strpart(ll,c,strlen(ll)-c+1)
    let ll2 = strpart(ll2,0,match(ll2,'$\|[^0-9#a-zA-Z]'))
    let text = ll1.ll2
 
    let text = substitute( text, "#", "", "g" )
    return text
  endif
endfunction
 
 
" Open IssueZilla / Bugzilla for the selected issue
function! OpenIssue( )
 
    let s:browser = "firefox"
 
    let s:issueText = GetCurrentIssueText( )
    let s:urlTemplate = ""
    let s:pattern = "\\(\\a\\+\\)\\(\\d\\+\\)"
 
    let s:prefix = substitute( s:issueText, s:pattern, "\\1", "" )
    let s:id = substitute( s:issueText, s:pattern, "\\2", "" )
 
    if s:prefix == "i"
        let s:urlTemplate = "https://issues.apache.org/ooo/show_bug.cgi?id=%"
    elseif s:prefix == "n"
        let s:urlTemplate = "https://bugzilla.novell.com/show_bug.cgi?id=%"
    elseif s:prefix == "fdo"
        let s:urlTemplate = "https://bugs.documentfoundation.org/show_bug.cgi?id=%"
    endif
 
    if s:urlTemplate != ""
        let s:url = substitute( s:urlTemplate, "%", s:id, "g" )
        let s:cmd = "silent !" . s:browser . " " . s:url . "&"
        execute s:cmd
    endif
endfunction
 
map <silent> <C-i> :call OpenIssue()<CR><C-l>

Show the current function name

To use this function, simply hit f: the line containing the function name will be displayed.

function! ShowFuncName()
  let lnum = line(".")
  let col = col(".")
  echohl ModeMsg
  echo getline(search("^[^ \t#/]\\{2}.*[^:]\s*$", 'bW'))
  echohl None
  call search("\\%" . lnum . "l" . "\\%" . col . "c")
endfunction
map f :call ShowFuncName() <CR>

Remove trailing whitespace when saving

Put this into your .vimrc to remove all trailing whitespace when saving.

" Remove all trailing whitespace when saving
" See http://vim.wikia.com/wiki/Remove_unwanted_spaces
autocmd BufWritePre * :%s/\s\+$//e

Editing zipped XML files

If you debug some import problem in a zipped XML format, put this to your .vimrc:

au BufReadCmd *.odt,*.ott,*.ods,*.ots,*.odp,*.otp,*.odg,*.otg,*.odb,*.odf,*.odm,*.odc call zip#Browse(expand("<amatch>"))
au BufReadCmd *.sxw,*.stw,*.sxc,*.stc,*.sxi,*.sti,*.sxd,*.std,*.odb,*.sxm,*.sxg,*.sxs call zip#Browse(expand("<amatch>"))
au BufReadCmd *.bau call zip#Browse(expand("<amatch>"))
au BufReadCmd *.oxt call zip#Browse(expand("<amatch>"))
au BufReadCmd *.docx,*.dotx,*.dotm,*.docm,*.xlsx,*.xltx,*.xlsm,*.xsltm,*.pptx,*.potx,*.ppsx,*.pptm,*.ppsm,*.potm call zip#Browse(expand("<amatch>"))

And then you can open the file in vim directly, editing the relevant XML substream in-place.

Pretty-printing

Pretty-printing is really needed for XML files, as LO typically writes no whitespace around XML tags by default, so it's hard to read or write the XML for a human.

The easy way:

map <F11> :% !xmllint --format --recover - 2>/dev/null<CR>

Then hitting F11 will pretty-print the XML. There are two problems with this approach: it doesn't handle non-XML (for example RTF) files, and it also doesn't handle invalid XML.

A more complex solution to add to .vimrc:

map <F11> :% !prettyprint<CR>

(prettyprint script, XML formatter, RTF formatter)

GNU ID-utils

As mentioned in GNU ID-utils, the eid command makes use of some environment variables when invoking the editor:

export VISUAL='vim'
export EIDARG='+/%s/'
export EIDLDEL='\<'
export EIDRDEL='\>'

To be able to invoke the lid utility from within Vim we need a key mapping and function call, you may place this section into your .vimrc file:

" GNU id-utils, taken from :h ident-search
" Generate the ID file in the current directory or in .. or ../.. or ../../..
" To use it, place the cursor on a word, type "_u" and vim will load the file
" that contains the word.  Search for the next occurrence of the word in the
" same file with "n".  Go to the next file with "_n".
map _u :call ID_search()<Bar>execute "/\\<" . g:word . "\\>"<CR>
map _n :n<Bar>execute "/\\<" . g:word . "\\>"<CR>
function ID_search()
    let g:word = expand("<cword>")
    let x = system("lid --key=none ". g:word)
    let x = substitute(x, "\n", " ", "g")
    execute "next " . x
endfun

lid.vim plugin

lid.vim is a plugin to integrate GNU ID-utils with Vim, providing a lid -g output in the quickfix window that can be travelled using the :cnext and :cprev commands respectively their keyboard mappings.

ctags

ctags is the other local indexer tool supported by the build system. To use it in vim:

  • always run 'make tags' after a toplevel build completed
  • add this to your ~/.vimrc:
" Tags files are searched first relative to the current file, then relative to
" the current working directory, and last in the $HOME directory.
set tags=./tags,./../tags,./../../tags,./../../../tags,./../../../../tags,./../../../../../tags,tags,../tags,.
  • then you can use :ts ClassName or :ts ClassName::FunctionName in vim to jump to the definition or declaration of a classname, Ctrl+T jumps back
  • you can also use Ctrl+] to search for the class or function or variable name at the cursor position and jump to the first hit.

See the vim help :help tags for more details.

OmniCppComplete

If you install the Vim OmniCppComplete plugin you also get auto completion of function names and class members when using ctags as the tags file is built with the necessary OmniCpp options. The vim path variable is evaluated if OmniCpp_NamespaceSearch is set to 2 to determine in which files to look for namespaces, it may help to not have /usr/include in there.. See also above for the vim path variable setting.

YouCompleteMe

YouCompleteMe uses a Clang compiler plugin to generate useful autocomplete information.

You can generate a clang compilation database file that can be used by YCM through make vim-ide-integration. For more details see [1]

Fugitive interface with Git

A great plugin to use Git from within Vim is fugitive. It offers diff, blame, jumping around in the commit tree and more all on a few key strokes. Its self-description (A Git wrapper so awesome, it should be illegal) is a little bit exaggerated, but just a little ;-)

Vimagit

Emacs has magit, Vim has vimagit, basically a frontend to the git add --interactive or git add --patch (respectively git commit --interactive or git commit --patch) commands. Great stuff to create a commit of only portions of your edits from within your favorite editor. Available as vimagit from the vim online script repository, install as a pathogen.vim bundle. See the feature set on the vimagit GitHub page.