7

I have the following code:

namespace
{

template<typename T> 
    struct One // This is the odd line
{
     // impl details
}

template<typename T> 
struct Two 
{
     // impl details
}

}

I would want to change the indent rules to make it look like this:

namespace
{

template<typename T> 
struct One 
{
     // impl details
}

template<typename T> 
struct Two 
{
     // impl details
}

}

Primary question But I can't find what rule is causing the strange indentation. Is there a way to query vim for this information? (I know there is something like that in emacs)

Secondary question If there is no such option, does someone know how I can make it look right? Note that it only happens immediately after a namespace.

My current cinoptions is cinoptions=:0,g0,=1s,(1s,N-s

  • Interesting question! This seems to be related to cinoptions=N-s, since I seem to be able to reproduce it with that line alone (on a *.cpp file, ft=cpp, 'cindent' enabled.) (See :help cino-N for help on that option.) I don't think there's a way to get Vim to tell why it's indenting a line the way it is. I'd still like to have an explanation for this one, maybe someone here will be able to explain it? – filbranden May 8 at 16:58
4
+50
" tell which cinoption take effect for current line
com! WhichCinoption call s:which_cinoption()

function s:which_cinoption() abort

  " test if cindent take effect
  if !empty(&indentexpr)
    echo "'indentexpr' exists, it overrides 'cindent'."
    return
  endif

  if !&cindent
    echo "'cindent' is currently disabled."
    return
  endif

  " test cinoption one by one

  " copied from :h cino- <ctrl-a> in vim8.2 1-677
  let opts = ['#','(',')','+','/',':','=','>','^','{','}','C','E','J','L','M',
        \ 'N','U','W','b','c','e','f','g','h','i','j','k','l','m','n','p','t',
        \ 'u','w','*']

  let indent = cindent(line('.'))
  let results = []
  let cinopt = &l:cinoptions

  for opt in opts
    try

      " test it with 8s, it should be big enough to make difference
      exe printf('setlocal cinoptions+=%s8s', opt)
      if indent != cindent(line('.'))
        let results += [opt]
      endif

    catch /.*/
      echom 'failed to test ' . opt
      echom 'internal error : ' . v:exception
    finally
      let &l:cinoptions = cinopt
    endtry
  endfor

  echohl Macro
  echom join(results)
  echohl None
endfunction

Test struct one:

WhichCinoption
--------
+
| improve this answer | |
  • 1
    Thanks @dedowsdi! This was extremely useful! I built on your answer, adapted the script to calculate the effect on indentation of each line and generated them for the C++ snippet both with cindent= (empty) and cindent=N-s which is the one triggering the odd effect. My results are here. I still don't understand exactly what is triggering that odd behavior, but at least now it's very visible which cinoptions are affecting which line, which is what was asked! Very clever approach. Thanks once again! – filbranden May 15 at 0:21
2

This is :h cino-+.

It looks like get_c_indent() (from src/cindent.c) doesn't know about template and treats it like an ordinary expression split over several lines (here namespace serves as "a function" from :h cino-+).

The relevant piece of code from src/cindent.c is

2831 else if (lookfor == LOOKFOR_UNTERM)
2832 {
2833     if (cont_amount > 0)
2834         amount = cont_amount;
2835     else
2836         amount += ind_continuation;
2837 }

However, as far as I can tell, it's not really a bug but rather "a missing functionality".

| improve this answer | |
  • Yes, continuation lines which defaults to shiftwidth likely explain why struct will be indented after template, but how come this only affects struct One and doesn't affect struct Two? If you have empty cinoptions= then both will be indented, but the whole block inside the namespace will be indented too, which I guess is the part N-s is trying to correct... – filbranden May 8 at 19:55
  • Yeah, so maybe also adding cinoptions=+0 is a way to work around this and get exactly the result as expected. That would have a side effect inside functions though, not sure that's really desirable... In any case, I still can't explain why struct One and struct Two are different here, that's the part that's been irking me the most. – filbranden May 8 at 19:58
  • @filbranden Complete guess, but I reckon it’s because the first struct comes after the initial opening {, but the second one comes after the closing } from the first struct, which resets the “current” indentation level. – Rich May 8 at 20:04
  • @filbranden This is because of the closing brace in the first column: Vim believes that struct Two is on the global level, not inside "function". – Matt May 8 at 20:04
2

@dedoswdi's answer was extremely helpful!

I adapted it somewhat to produce the result for each line on the buffer. My adapted script can be found in this Gist.

After applying it to the original C++ code, I got the results with both cinoptions= (none of them set) and cinoptions=N-s (which triggers the odd behavior with the second struct.)

Here are the results with cinoptions= (empty):

       | namespace
f      | {
> ^ N  | 
> ^ N  |         template 
+      |                 struct One // This is the odd line
{ N    |                 {
/ > {  |                         // impl details
}      |                 };
N      | 
N      |         template 
+      |                 struct Two
{      |                 {
/ > {  |                         // impl details
}      |                 }
       | 
}      | }

And with cinoptions=N-s:

       | namespace
f      | {
> ^ N  | 
> ^ N  | template 
+      |         struct One // This is the odd line
{ N    | {
/ > ^  |         // impl details
}      | };
       | 
       | template 
       | struct Two
f      | {
/ > ^  |         // impl details
}      | }
       | 
       | }

So it's still unclear to me why the N is missing from the second template and why the + is missing from the second struct, but at least now it's very visible!

I guess this definitely answers the "Is it possible to get the rule (cinoptions) that govern the indentation for a specific line in vim?" question!

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.