How to take curly brace to end of previous line - vim

I see many codes like following on the net:
public static void read()
{
using (StreamReader m_StreamReader = new StreamReader("C:\\myCsv.Csv"))
{
while (m_StreamReader.Peek >= 0)
{
string m_Str = m_StreamReader.ReadLine;
string[] m_Splitted = m_Str.Split(new char[] { "," });
Console.WriteLine(string.Format("{0}={1}", m_Splitted[0], m_Splitted[1]));
}
}
}
However, I want to convert above to following:
public static void read() {
using (StreamReader m_StreamReader = new StreamReader("C:\\myCsv.Csv")) {
while (m_StreamReader.Peek >= 0) {
string m_Str = m_StreamReader.ReadLine;
string[] m_Splitted = m_Str.Split(new char[] { "," });
Console.WriteLine(string.Format("{0}={1}", m_Splitted[0], m_Splitted[1]));
}
}
}
Hence starting curly brace is taken to the end of previous line. How can this be done programmatically in Vim? It tried but though I can pick up starting curly brace but could not manage to take it to end of previous line.
Note: above code is from here.

Joining the next line to the current line is done with :help J in normal mode or :help :join in command-line mode.
Joining the current line to the previous line is done in normal mode by moving the cursor to the previous line with - and then joining with J. In command-line mode, you would use -, short for .-1 ("current line number minus one"), as :h address for :join: :-j, which mirrors the normal mode method quite well.
To do this on the whole buffer, you need a way to execute a given command on every isolated opening brace. This is done with :help :g:
:g/^\s*{\s*$/-j
Breakdown:
:g/<pattern>/<command> executes <command> on every line matching <pattern>,
^\s*{\s*$ matches lines with a single opening braces and optional leading and trailing whitespace,
-j joins the current line with the line above.
But the result is not correctly indented anymore so you will need something like the following command to fix the mess:
gg=G
Breakdown:
gg moves the cursor to line 1,
=G re-indents every line from the cursor to the last line, see :help =.
That said, switching from one coding style to another seems like something that should be done with a dedicated tool rather than with general text editing.

The desired output looks strikingly similar to the ratliff style. If that's correct, astyle is one formatter that supports this particular style and this solution will be more robust than making changes manually.
You can wrap it in your own command as follows:
command! -buffer Fmt let winsaved = winsaveview() | execute '%! astyle --style=ratliff' | if v:shell_error > 0 | silent undo | endif | call winrestview(winsaved)

Related

Vim Multi-Line If-Statements into Single-Line If-Statements

Is it possible to have a vim binding that turns multi-line if-statements into single-line if-statements and vice-versa?
turn this
if () {
statement_1;
statement_2;
} else if () {
statement_3; statement_4;
} else if () {
statement_5;
} else {
}
into this
if () { statement_1; statement_2 }
else if () { statement_3; statement_4; }
else if () { statement_5; }
else { }
or anything close to the above behavior? I was thinking of having the command execute upon visually selecting the block to convert and then use searches for else if and entering new lines, etc. But my problem was determining how many else if were in the code
Join all the lines to a single line by selecting them in visual mode V and pressing J; then add a newline before every else with :s/else/\relse/. You'll end up with:
if () { statement_1; statement_2; }
else if () { statement_3; statement_4; }
else if () { statement_5; }
else { }
The \r in the replacement pattern is a newline (you need to use \n and search and \r in replace; don't ask me why).
Next step is to put all the start braces in the same column. I'd use the tabular plugin for this, which makes that very easy:
:%Tabularize /{/
With % we operate on the entire buffer, in a "real" file you'll probably want to use a more restrictive range or visual mode. There are some other plugins which do something similar as well.
You should now have the output you want.
If you don't want to use a plugin, you can use the column command:
:%!column -ts{ -o{
If you want a "Vim-only" solution, then it's a bit more complex:
:let b:column = 10
:%s/^\(.\{-}\) {/\=submatch(1) . repeat(' ', b:column - len(submatch(1))) . ' {'/
Breaking that down:
I used the b:column variable to specify the column to align to. You don't need this but it make it a bit easier to edit this number later on.
^\(.\{-}\) { puts everything before { in a subgroup.
In the replace we used an expression (as indicated with \=). See :help sub-replace-\=.
First we put the if ... back with submatch(1)
Then we insert as many spaces as we need with repeat(' ', b:column - len(submatch(1)))
Finally we insert the literal { back.
I told you it was a bit more complex ;-) If you don't want tabular. Personally, I'd just start insert mode to insert spaces, which will be a lot faster than writing & debugging this (relevant xkcd).
Note that I didn't make some "magic" command which re-arranges all the text with just a stroke of a key. I don't think such a command would be a good idea. In practice there will be a lot of edge cases that such a command won't handle. Fully "parsing" a programming language with ad-hoc editing commands and/or regexps doesn't really work all that great.
Where Vim really shines is giving the user powerful text editing commands, which can be applied and combined with minimal effort, which is exactly what I did above. There are several other ways one can use to get the same effect.
But if you really want to, you can of course combine all of the above in a command:
fun! s:reformat(line1, line2)
" Remember number of lines for later
let l:before = line('$')
" Join the lines
execute 'normal! ' . (a:line2 - a:line1 + 1) . 'J'
" Put newline before else
:s/else/\relse/
" Run tabular; since the number of lines change we need to calculate the range.
" You could also use one of the other methods here, if you want.
let l:line2 = a:line2 - (l:before - line('$'))
execute a:line1 . ',' . l:line2 . 'Tabularize /{/'
endfun
command! -range Reformat :call s:reformat(<line1>, <line2>)

Why does ci` jump into the backticks but ci( does not jump into braces? [duplicate]

This question already has answers here:
Why ci" and ci(, ci{.... behave differently?
(2 answers)
Closed 6 years ago.
Suppose I have a textfile like this:
this is some test `more test`
this is some test (more test)
If I am at the beginning of the first line and type ci` the cursor will jump into the backticks, replaces the content inside and let's me edit.
However, if I am at the beginning of the second line and I type ci( nothing will happen.
What's the reason for this behavior? Is there a setting that might change it?
Concerning the internal behavior of vim 7.4:
For blocks (),{},[],<>:
Vim searches backwards and then forwards starting from current position to match the opening and closing character.
For quotes "", '', `` :
Vim spans the whole line, where the cursor is, till it finds the quotes.
Here is the last case where there is no quotechar under the cursor:
/* Search backward for a starting quote. */
col_start = find_prev_quote(line, col_start, quotechar, curbuf->b_p_qe);
if (line[col_start] != quotechar)
{
/* No quote before the cursor, look after the cursor. */
col_start = find_next_quote(line, col_start, quotechar, NULL);
if (col_start < 0)
return FALSE;
}
/* Find close quote character. */
col_end = find_next_quote(line, col_start + 1, quotechar,curbuf->b_p_qe);

Way to remove two non-consecutive lines in vim

Is there a way to remove two non-consecutive lines with a single vim command (which could be undone in a single step).
I have file like this:
if (enabled) {
const a = 1;
const b = 2;
console.log(a + b);
}
And would like to end up with this:
const a = 1;
const b = 2;
console.log(a + b);
Use visual mode and paste over the whole block.
yiBvaBVp
Vimcasts episode: Pasting from Visual mode
For more help see:
:h iB
:h aB
:h v_p
If you select your example (with v$% for instance), then
:'<,'>g/{\|}/d
will give you what you want (if there are no nested { or }).
Another way would be to record a macro, something like
# qa to start recording, the final q to stop. ^O is <Ctrl-o>
qaf{%dd^Oddq
then use it with #a.
Edit: more general solution (still using visual mode though):
You could add a mapping:
:xnoremap <leader>d <esc>:'<d \| '>d<cr>
to delete the first and last line of the last visual selection.

variable search and replace in vim

I have a list of items that I want to add a method call to. An example is easiest. Here's what I have now:
assists: 12,
level: 14,
deaths: 5,
...
I want to change that list to look like this:
assists: build_average(:assists),
level: build_average(:level),
deaths: build_average(:deaths),
...
Is it possible to add that method call to the end of every line with the name of the key as the argument with a neat Vim expression?
More of a regular expression:
:%s/\(\w\+\):.\+/\1: build_average(:\1),/
Note that this applies to all lines in your file. To only replace in a region, select the region (using V) and then use :s (which results in :<,>s/...).
Using more complex regular expressions in VIM can be tricky, because metacharacters are different from "normal" regular expression syntax (you need to write \+ instead of +, but can use . without escaping it, for example). I found this guide very handy to refer to the special VIM-syntax of regular expressions: http://vimregex.com/#pattern
Alternatively you can record a macro:
q // record macro
q // assign it to letter 'q'
0 // go to start of line
/:<ENTER> // search for ':'
l // move cursor 1 position to the right
d$ // delete to end of line (line is now 'assists:')
yyp // duplicate current line (cursor moves 1 line down)
k // move cursor up
A build_average( // append " build_average("
<ESC> // exit edit mode
J // join next line
A ), // append " ),"
<ESC> // exit edit mode
j // move 1 line down
q // stop recording macro
2#q // execute macro 'q' 2 times
More regexp gymnastics:
:%s/\v(\w+):\s+\zs.*\ze,/build_average(:\1)/
Decrypting it:
:help \v
:help \w
:help \s
:help \zs
:help \ze
:help \1

Vim keyboard shortcut: Deleting code without deleting the if statement

This is probably a basic vim questions to all those vim gurus out there.
If I want to delete a particular if statement and its closing bracket, but without deleting the code inside the expression, how can I achieve this through some keyboard shortcuts?
eg:
if (a == 2) {
// do not delete this part.
for (int i = 1; i < 10; i++) {
// code
}
}
The result will be:
// do not delete this part.
for (int i = 1; i < 10; i++) {
// code
}
Thanks for any help.
Alternatively to Zachs solution, you could use this:
di{Vk]p
Explanation:
You start inside the { } block, and with di{ you delete inside the { } block.
Then, you select the the if statement, which you want to remove, which now is two lines. Vk selects both, and you paste the block you just deleted over it with p. The ] makes the pasted block be correctly indented (thanks, Kache)
There could be a more elegant solution to the pasting part, buth this was the first one that came to mind.
The anvantage over Zachs answer is that you don't have to move the cursor to the line with the if, you just need to be inside it (although you may run into trouble with nested {} blocks, which there usually are). In addition, you don't need to fix indentation.
Move your cursor anywhere on the line with the if statement. Here is the sequence of commands (will be explained after)
$%dd''.
Explanation:
$ goes to the end of the line (cursor is now on {).
% goes to the matching curly brace (or parentheses etc...). Cursor is now on } of the if statement
dd deletes the line the cursor is on (} is now deleted)
'' goes to the line of the last jump. The last jump was when we used %. (cursor is back on if statement line)
. Repeats the last command (dd). This deletes the line (if statement line is now deleted)
Result:
// do not delete this part.
for (int i = 1; i < 10; i++) {
// code
}
The indentation will be off so you can use gg=G (which correctly indents the file) to fix it.
If you do this alot you can make map it to a key in your .vimrc or you can make it into a macro
Making it into a mapping:
Example of a mapping in your .vimrc:
nnoremap <C-d> $%dd''.gg=G
Whenever you press control-d (you can choose any key, just replace <C-d>) Vim will run those commands, doing what I explained before followed by an entire file indentation this time. Make sure your cursor is on the if statement line when using this command otherwise there could be unintended results.

Resources