Sometimes an array initialization in C extends over several lines, especially if the array is multidimensional. In Emacs the result of auto-indentation looks like this:
int a[N][N] = {{0, 0, 6, 7, 0, 4, 0, 2, 0},
{0, 5, 0, 6, 0, 0, 0, 0, 1},
{2, 0, 0, 0, 0, 8, 0, 0, 4},
{4, 0, 9, 5, 0, 7, 0, 0, 3},
{0, 0, 0, 0, 0, 0, 0, 0, 0},
{8, 0, 0, 2, 0, 1, 9, 0, 6},
{6, 0, 0, 1, 0, 0, 0, 0, 7},
{3, 0, 0, 0, 0, 5, 0, 6, 0},
{0, 2, 0, 3, 0, 6, 1, 0, 0}};
In Vim the auto-indentation feature enabled by :filetype indent on
merely indents the lines by the constant shiftwidth
which leads to the following:
int a[N][N] = {{0, 0, 6, 7, 0, 4, 0, 2, 0},
{0, 5, 0, 6, 0, 0, 0, 0, 1},
{2, 0, 0, 0, 0, 8, 0, 0, 4},
{4, 0, 9, 5, 0, 7, 0, 0, 3},
{0, 0, 0, 0, 0, 0, 0, 0, 0},
{8, 0, 0, 2, 0, 1, 9, 0, 6},
{6, 0, 0, 1, 0, 0, 0, 0, 7},
{3, 0, 0, 0, 0, 5, 0, 6, 0},
{0, 2, 0, 3, 0, 6, 1, 0, 0}};
Is there a way to make Vim behave like Emacs in this particular situation?
Herbert Sitz's answer was indeed very helpful (thanks!). I have slightly modified his code to look like this:
setlocal indentexpr=GetMyCIndent()
function! GetMyCIndent()
let theIndent = cindent(v:lnum)
let m = matchstr(getline(v:lnum - 1),
\ '^\s*\w\+\s\+\S\+.*=\s*{\ze[^;]*$')
if !empty(m)
let theIndent = len(m)
endif
return theIndent
endfunction
Saving this to the file ~/.vim/after/ftplugin/c.vim
solves the problem, i.e. it makes Vim align the array declaration the same way Emacs does.
matchstr()
instead of matchlist()
to make the code easier to understand (len(m)
in place of len(m[0])
).static
declarations.{
) so that the expression also matches one-dimensional arrays (or structures).;
) because this indicates that the declaration holds in one line (i.e. the next line should not be aligned under the opening bracket). I think this is a more general approach than looking for closing brackets or commas at the end of the line.Please let me know if I have missed something important.
To tab or add the indentation at multiple lines, try “shift+dot” i.e., “.” Shortcut once. You will see it will add an indentation of one character at each selected line from the start. If you want to add indentation without stopping, then you have to try the “.” Key from the keyword after using “shift+.”.
To mark a block of lines and indent it, V j j > to indent three lines (Vim only). To indent a curly-braces block, put your cursor on one of the curly braces and use > % or from anywhere inside block use > i B .
autoindent essentially tells vim to apply the indentation of the current line to the next (created by pressing enter in insert mode or with O or o in normal mode. smartindent reacts to the syntax/style of the code you are editing (especially for C). When having it on you also should have autoindent on.
Someone may know better than I do, but here's a first stab: Yes, the indenting can be customized. If your file is a recognized language "filetype" then it is being indented using rules/code in the corresponding *.vim file found in the /indent directory (e..g, vim/vim72/indent).
You would need to modify the code that's providing an indent on your multiline array, which might involve adding a new if block to the section that makes the indents, with an if expression that matches all and only the first lines of your multiline arrays. You should be able to get an idea of how things work by examining the files in the /indent directory.
UPDATE: Here's a mod to the c.vim indent file that should be close to what you want, seems to work fine for me. This is the entire file:
" Last Change: 2005 Mar 27
" Only load this indent file when no other was loaded.
if exists("b:did_indent")
finish
endif
let b:did_indent = 1
" C indenting is built-in, thus this is very simple
setlocal cindent
setlocal indentexpr=GetMyCIndent()
function! GetMyCIndent()
let theIndent = cindent(v:lnum)
let m = matchlist(getline(v:lnum - 1),'^\S\+\s\+\S\+\s*=\s*{\s*{\ze.*}[^}*]')
let m2 = matchlist(getline(v:lnum - 1),'}.*}')
if (!empty(m)) && (empty(m2))
let theIndent = len(m[0]) - 1
endif
return theIndent
endfunction
let b:undo_indent = "setl cin<"
The only problem (I think) with this code is that it will give same indent to an array of arrays intialization that is completed on one line. To avoid that the pattern needs to be modified to match only when there is one closing bracket on the line, not two. (Alternatively, you could just do a separate test.) That will take a little finagling, but shouldn't be too hard. (Also, if you do extend current pattern, you'll want to use the \ze marker in pattern to mark the end of the match that you want stored in m[0], which will be after second opening bracket that is last character in current pattern.) I REVISED CODE ABOVE TO DO SEPARATE TEST (using variable m2) THAT I THINK SOLVES THE PROBLEM. Not sure what other little details need to get taken care of.
One alternative would be to say that you want this indenting behavior whenever there are at least two opening brackets on the line and the last line char is a comma. This might actually be the best way, since it lets you have pairs, triplets, etc. of elements on a line:
let m = matchlist(getline(v:lnum - 1),'^\S\+\s\+\S\+\s*=\s*{\s*{\ze.*,\s*$')
if !empty(m)
let theIndent = len(m[0]) - 1
endif
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With