VIM - Reformatting indentation and braces - vim

When working with blocks of code in VIM, I'm able to easily re-indent blocks of code via selecting a region in visual mode (SHIFT+v), then just hit =. This re-tabs lines of code, uses the correct indentation depths, hard-tabs vs spaces, etc.
I have a large set of functions I need to re-factor, and I have several blocks of code with braces on the same line as if/else keywords, ie:
if(something) {
doFunction(something);
} else if(somethingElse) {
doFunction(somethingElse);
} else {
// default stuff to do
}
And I would like to change the brace and spacing style to:
if ( something ) {
doFunction( something);
}
else if ( somethingElse )
{
doFunction( somethingElse );
}
else
{
// default stuff to do
}
The differences include:
Having the opening/closing braces on their own dedicated line
The argument to if, else if, and functions has a space separating the beginning and end of the argument list from the surrounding round brackets.
There is a space between if/else if and the argument brackets, but not for function names and the argument brackets.
Is there a way to set this style as the default in VIM, and to also have re-indentation commands change the style to match the latter of the two I've provided? I've found tools to enforce things like line endings, tabs-vs-spaces, etc, but not style details like those shown above.
Thank you.

The indentation scripts in vim are not constructed for so complex tasks. I would advise you to use the indent command, in particular the following arguments:
-prs, --space-after-parentheses
Put a space after every '(' and before every ')'.
See STATEMENTS.
-sai, --space-after-if
Put a space after each if.
See STATEMENTS.
You should read the command's man page for more details.
Obviously, this command can be used to filter the buffer's content using:
:%!indent

Related

How can I get the whitespace at the beginning of a line and use it in a search and replace?

Background
I want to convert an if statement from Fortran to C++. I like to have braces on a new line.
So I want to make
! this may be nested so indentation is unknown
if ( condition ) then
block
end if
to
if ( condition )
{
block
}
Changing end if to } is easy since the indentation is already how I want it. I just used :%s/end if/}/gc.
However, changing then is more challenging. I need to create a new line and set its the leading whitespace to the same as the previous line.
The closest I have to a solution is :%s/then/\=printf("\n%s{",indent(line('.')))/gc
However I want to use the value returned from indent(line('.') to set the number of indents.
Problem
Can I use a number I receive from a function to set the number of tabs at the beginning of line in a search and replace?
You want to substitute then with:
a newline,
followed by n spaces, as many as used for indenting the current line,
followed by an opening brace.
As is, your command does the following:
a newline,
followed by the number of spaces used for the indentation of the current line,
followed by an opening brace.
:help repeat() to the rescue:
:%s/then/\=printf("\n%s{",repeat(' ',indent(line('.'))))/gc
But there is still room for improvement…
you only want to substitute trailing thens so the g flag is useless:
:%s/then/\=printf("\n%s{",repeat(' ',indent(line('.'))))/c
the search pattern may match other thens so it should be restricted a little:
:%s/then\s*$/\=printf("\n%s{",repeat(' ',indent(line('.'))))/c
also, the pattern should include any whitespace before the then to avoid leaving annoying trailing whitespace behind:
:%s/\s*then\s*$/\=printf("\n%s{",repeat(' ',indent(line('.'))))/c
line('.') is unnecessary:
:%s/\s*then\s*$/\=printf("\n%s{",repeat(' ',indent('.')))/c
and we could use the new-ish "method" syntax to limit parenthesis nesting:
:%s/\s*then\s*$/\=repeat(' ',indent('.'))->printf("\n%s{")/c

VIM: how do I get close parenthesis aligned with block like close brace does?

in vim
when you close the brace, it automatically aligned with the block head of the opening brace. like this:
f() {
...
}
how do I get closing parenthesis automatically moved and aligned the same way:
f (
int i,
...
) {
At the moment, I can only get it aligned by use alignment command. e.g ==, without the command, it looks like this:
f (
int i,
...
) {
i.e. an extra indent from where it is supposed to be aligned.
how do I fix this problem and make it work just like a closing brace?
The answer depends on the value of 'indentexpr'.
If it's set (e.g. to GetJavaIndent()), you should look into fixing the corresponding function. No generic advice can be given here.
If 'indentexpr' is not set, you're using 'cindent', the behaviour of which can be affected with 'cinoptions'. The option you're looking for is m1. So just add this to your vimrc, and you should be set: set cinoptions=m1 (maybe wrap it into autocmd or something so that it will only affect specific filetypes.)

How can you reformat C source code '{' placements in VIM?

I want to change the following code:
if (test)
{
statements;
}
else
{
statements;
}
To the following:
if (test) {
statements;
} else {
statements;
}
Using '=' doesn't do this function (as far as I can tell).
Is there any clever VIM command to do this?
You can play with regexes. Here, it will be:
:%s/\_s*{/ {/g
:%s/}\zs\_s*\ze\(else\|while\)/ /g
The secret is in /\_s which is like /\s (which matches spaces), but which also matches newlines.
If you also want to transform things like { return foo; }, you'll have to insert newlines as well, and then reindent everything.
:%s/\_s*{\_s*/ {\r/g
gg=G
You can also have a look at vim plugins that integrates tools like AStyle or clang-format which should give better results.
PS: lh-cpp and mu-template snippets have an option (through :AddStyle) that lets the end-user specify whether a newline shall be inserted or not. Sometimes, the better approach is to produce code that conforms to project style.

How to find enclosing block in vim?

Searching backwards for { is finding sibling blocks.
The file is properly indented, possibly this can be done by moving upwards from one character before (^) until first non-whitespace character.
Is there any other way?
Eg:
if (some condition) {
for (;;) {
}
some statements;
here you're;
while (some condition) {
}
}
Is there a way to navigate from "here you're" to if?
I was thinking of placing cursor before "here you're" and call a function which will move cursor up until it finds non-whitespace character.
Press (cursor on any char of here you're line)
[{
This is exactly what you are looking for. For detailed info, :h [{, or :h motion.txt.
% is the correct way to do that.
Your example uses C code, which uses braces ({ and }) for grouping, so the following suggestion is not directly useful. For many languages that use matching patterns for grouping (such as if and fi in shell scripts, <table> and </table> in HTML, for example) my matchit.vim script, part of the standard vim distribution, extends % and defines additional motions:
g%: cycle backwards
[%: start of enclosing block (like the current question)
]%: end of enclosing block
a%: (Visual mode only) select the matching block
:help matchit-install

Apply a set of lines to a template

I want to process a set of lines and apply a template to each line. Let's say I have the following block of lines:
CASE
ESAC
IF
FI
And I would like to get the following output:
<YYINITIAL> {CASE} {
return new Symbol(sym.CASE);
}
...
<YYINITIAL> {FI} {
return new Symbol(sym.FI);
}
So, I thought of having a template with the body to apply for each line, something like this:
<YYINITIAL> {###PLACE_HOLDER###} {
return new Symbol(sym.###PLACE_HOLDER###);
}
And apply it to each line.
What I thought was of selecting the lines, putting then in a register ("a, for instance), then selecting the template putting it in a register ("b) and call an external script that generates the output. However no luck (don't know how to pass the contents of a register to an external script).
Any other approach is welcome.
this command will do that job:
:%s/.*/<YYINITIAL> {&} {\r\treturn new Symbol(sym.&);\r}/
you could remove the %, if you visual selected lines. Also you could create a mapping or user command for it, if it is used often.
Your suggested approach seems overly complex, especially because you still have to implement the template expansion logic as the external script.
Why don't you simply define your templates as :substitute commands?! You can pattern-match your PLACEHOLDER via /\({pattern}\)/ and insert it in the replacement text as \1, \2, etc. (I hope you know about the power of Vim's :substitute command; otherwise, read :help :s.
You can keep and persist your templates in several ways, depending on how often / long you need them:
Just in the command-line history (:s<Up>)
Save in named registers (:let #a = #:), execute with :#a
Save in a Vim script and :source it
Write a custom plugin with :commands

Resources