Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Weird indentation of C labels when a statement block starts on the same line #3229

Open
ao2 opened this issue Jul 18, 2018 · 2 comments
Open

Comments

@ao2
Copy link

ao2 commented Jul 18, 2018

Hi,

I am seeing a weird behavior (vim 8.1.89) when autoindenting C code where a goto label is followed by a block opened on the same line:

#include <stdio.h>

int main(void)
{
	int block;
	int ret;

	printf("Choose the block (1, 2, or 3) and press Enter: ");
	ret = scanf("%d", &block);
	if (ret != 1) {
		fprintf(stderr, "Invalid choice.\n");
		return -1;
	}

	switch(block) {
	case 1:
		goto blk1;
	case 2:
		goto blk2;
	case 3:
		goto blk3;
	default:
		fprintf(stderr, "Invalid block.\n");
		return -1;
	}

	/*
	 * Weird behavior when formatting labels with a following block
	 * starting on the same line, which is valid C.
	 *
	 * The behavior is particularly weird when the label is indented with:
	 *
	 *	:set cinoptions+=L0
	 *
	 * and re-indenting the whole file with:
	 *
	 *	gg=G
	 *
	 * Some padding is added which in this case is equal to the length of
	 * "blk1: " for the first time, and then accumulates for the following
	 * labels.
	 *
	 * Note that the padding is added also with the default cinoptions, but
	 * in that case it does not accumulate for the following labels.
	 *
	 * The padding can be seen more clearly with these settings:
	 *
	 *	:setlocal noexpandtab
	 *	:setlocal softtabstop=8
	 *	:setlocal shiftwidth=8
	 *	:setlocal tabstop=8
	 *
	 * and showing non-printable characters with
	 *
	 *	:setlocal list
	 *
	 * I was not able to find a cinoptions or cinkeys setting to avoid
	 * adding this padding. If possible I'd like to avoid writing an
	 * indentexpr to remove it.
	 */
	blk1: {
		      printf("block 1\n");
		      return 0;
	      }

	      blk2: {
			    printf("block 2\n");
			    return 0;
		    }

		    blk3: {
				  printf("block 3\n");
				  return 0;
			  }
}

Also on http://vpaste.net/oR1VG

I know it's an unusual syntax but it's still valid C.

I found this issue when I was trying to reuse cindent to indent Device-Tree files which have a very C-like syntax.

Any idea if there is some setting to remove the extra padding?
I read cinoptions and cinkeys documentation but couldn't find anything to solve this, +=J1 seems to help for the first label but it messes up some other indentation.

Is it a bug?

This may be somehow related to #37.

Thanks,
Antonio

@tonymec
Copy link

tonymec commented Jul 18, 2018

<offtopic>
Once upon a time, maybe 40 to 50 years ago, I used to be programming in COBOL, and if my colleagues and I did use PERFORM statements, GOTOs weren't shunned either.

Then came programming theorists who had cut their milk teeth on C, ALGOL, and the like, and they told us that GOTO statements were evil, that they necessarily begat unreadable spaghetti code, and that "structured programming" was the way to go. So we painstakingly learned to make COBOL programs with no GOTO statements, or maybe only a very few of them located exclusively inside AT END and ON SIZE ERROR clauses.

And in fact, now that the only Turing-complete language I'm still fluently writing is Vim script, it doesn't even have goto statements.

But they seem to be doing a comeback in C, which I had always thought of as belonging to the family of "structured programming" languages, where goto statements were neither necessary, nor even useful.

Strange how history turns back on itself, like the famed Ouroboros snake.
</offtopic>

@ao2
Copy link
Author

ao2 commented Jul 20, 2018

I came up with a workaround, but it's rather ugly and inefficient and it fails when there are inline comments:

" Do not force label at column 0
setlocal cinoptions+=L0
setlocal indentexpr=GetMyCIndent()

function! GetMyCIndent()

  " Fix indentation of labeled blocks,
  let m = matchstr(getline(v:lnum), '^[[:space:]]*[[:alpha:][:digit:]_]\+:')
  if !empty(m)
    " look for the indentation level of the containing block
    let [lnum,col] = searchpairpos('{', '', '}', 'bnW')
    if lnum > 0
      " and increase it by one indentation level
      return indent(prevnonblank(lnum)) + &shiftwidth
    endif
  endif

  " Fix indentation of the first line in a labeled block.
  let m = matchstr(getline(prevnonblank(v:lnum - 1)), '^[[:space:]]*[[:alpha:][:digit:]_]\+:')
  if !empty(m)
    return indent(prevnonblank(v:lnum - 1)) + &shiftwidth
  endif

  " Indent closing braces at the same level of the opening one
  let m = matchstr(getline(v:lnum), '^[[:space:]]*}')
  if !empty(m)
    let [lnum,col] = searchpairpos('{', '', '}', 'bnW')
    if lnum > 0
      return indent(lnum)
    endif
  endif

  " fall back to cindent for the other cases
  return cindent(v:lnum)
endfunction

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants