Search code examples
vimlatexindentation

Vimscript indent and debugging ftplugin scripts


I am trying to manually define indentation levels for .tex files using ftplugin scripts.

~/.vim/ftplugin/tex/indent.vim:

      1 setlocal indentexpr=TeXIndent(v:lnum)
      2
      3 function! TeXIndent(lnum)
      4     if a:lnum == 0
      5         return 0
      6     endif
      7
      8     let prev = getline(a:lnum - 1)
      9     let line = getline(a:lnum)
     10
     11     " sections have hardcoded indentation; environments are always subordinate to sections
     12     if prev =~ '^\s*\\section'
     13         return 1
     14     elseif prev =~ '^\s*\\subsection'
     15         return 2
     16     elseif prev =~ '^\s*\\subsubsection'
     17         return 3
     18     elseif prev =~ '^\s*\\paragraph'
     19         return 4
     20     " environment indentation
     21     elseif prev =~ '\\begin'
     22         return indent(a:lnum - 1) + 1 " increase indentation by 1 if previous line has \begin
     23     elseif line =~ '\\end'
     24         return indent(a:lnum - 1) - 1 " decrease indentation by 1 if line has \end
     25     else
     26         return indent(a:lnum - 1) " use previous indentation
     27     endif
     28 endfunction

When I type \section, I expect pressing enter to move the cursor to an indentation level of 1 on the next line (same for the other conditions, specified in TeXIndent), and this is not the case. Testing with a simpler function which returns 2 every time also lacks this behavior, so I suspect my approach is flawed. How do I achieve the expected behavior?

In general, how can ftplugin scripts be debugged?


Solution

  • All usual approaches are available when debugging a ftplugin.

    • :debug exe "normal keysequence" can be used to debug a mapping
    • :debug Command can be used to debug a command, a function (with :debug call or :debug echo)...
    • :verbose can be used to know what is actually defined and where it has been defined -- see this dedicated Q/A on vi.SE: https://vi.stackexchange.com/q/7722/626
    • logs can be used as well -- see my framework
    • we can even add some asserts to fail fast on programming errors -- see my other framework (from the same library plugin)

    Now your problem is not with ftplugins but with indent expressions. If I'm not mistaken, it faces the same issues than the ones we can have with fold expression: the indenting/folding function is automatically called once per line by Vim. Messages will be silenced, internal uses of :debug will end in nightmares.

    What can we do:

    • log to a file, or elsewhere like the qf window

    • don't tie the function to v:lnum in order to be able to call that function independently. That's perfect, it's what you already did. We can test response from all calls with

        :echo map(range(1, line('$')), 'the_expr_function(v:val)')
      
    • play with balloons (requires gvim instead of vim)

    • play with signs

    You'll find these methods used in a folding plugin I'm maintaining.

    (See also this Q/A on vi.SE: https://vi.stackexchange.com/a/19916/626)

    PS: indent plugins shall go into {rtp}/indent/, not {rtp}/ftplugin/, and we usually analyse the last non empty line. You should find examples in $VIMRUNTIME/indent, and I have few TeX indent plugins written by Johannes Zellner circa 2002.