Development/Vim

    From The Document Foundation Wiki


    Recommended Vim plug-ins

    Use Pathogen or Vundle to install any of these mentioned plug-ins.

    localvimrc

    The localvimrc plug-in allows you to have a .lvimrc file to overwrite/extended your main ~/.vimrc file without cluttering it.

    " Works best with vimrc entries:
    "   let g:localvimrc_sandbox = 0
    "   let g:localvimrc_whitelist = "/path/to/postgres/tree/.*"
    "
    if g:localvimrc_sourced_once_for_file
        finish
    endif
    
    " Place here the specific setting for your LibreOffice environment.

    You should add an entry .lvimrc to your global .gitignore file to avoid listing the file when using git.

    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 ;-)

    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.

    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.

    In case you notice problems ctags parsing C++11 code, give Universal Ctags a try.

    YouCompleteMe

    YouCompleteMe uses clangd 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]

    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.

    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 in your .vimrc or .lvimrc:

    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 (or lvimrc)

    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)

    Jumping to a random line

    Jumping to a random line is useful in testsuite files where a framework is local to a file, so adding a new test in a particular file is useful, but always appending new tests at the end would cause too much conflicts.

    Vim Tips Wiki covers to to some extent, a Python script is also available. Here is a macro to use the later:

    command GotoRandom pyf /path/to/goto-random.py

    Then executing

    :GotoRandom
    

    will jump to a random line inside the file

    Avoiding UUIDs in SmartArt files

    nnoremap <silent><leader>ls :% !pptx-kill-uuid.py<cr>

    in your vimrc, then put pptx-kill-uuid.py in PATH.

    Finally open `ppt/diagrams/data1.xml`in a PPTX file in vim, press `,ls` and it makes all those UUIDs human-readable.

    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.