A custom function in Vim - vim

I'm quite new to Vim/Vi, and I need to write a custom function/macro.
Is it possible to define a command (ex. :mycommand) that would format the lines in the current file like so:
Initial lines:
This is line 1
This is line 2
This is line 3
This is line 4
This is line 5
This is line 6
This is line 7
This is line 8
Result:
This is line 1\nThis is line 2
This is line 3\nThis is line 4
This is line 5
This is line 6\nThis is line 7\nThis is line 8
How do I go about creating such a script? And where do I place it?

Marco
You can record a macro, to join two lines by \n separator, for example in your vimrc:
let #x='gJi\n^['
(the ^[ above, you press ctrl-v Esc)
Assume your cursor is on the first line, you can do #x in normal mode, then )J## for the 2nd block, then job is done.
You can wrap those operations in function or create them as mapping.
Join plugin
I have written a Join script: https://github.com/sk1418/Join , it supports to join lines with separator and other features, in your case, you can execute command: :J '\n', it will do what above macro (#x) does. You can put it in your function too, like:
function Foo()
Join '\n'
join!
Join '\n'
endfunction
Update for the Question modification:
The modification you made, turned the question into another one... However it could be solved, I listed two possibilities below, one is vim way, the other is with external awk tool, if you have awk available on your system.
with vim :s cmd
This command should do it for you:
%s/\n\n/∢/g|%s/\n\ze./\\n/g|%s/∢/\r/g
the ∢ is done by pressing ctrl-v u2222, it is just for a special char, which not exists in your text, you can use other uni-code chars too.
with external awk
%!awk -v RS='\n\n' '{gsub(ORS, "\\n")}7'
This will do the transformation for you, however it leaves an extra \n at the end of the file, just remove it.

Related

How to replace current string line with another line in vim?

I'd like to replace current string line with another (for example the another line is placed in 5 lines above current line). I can do it with a pair of commands
dd
:-5t-1
Is there the shorter way to obtain same goal?
dd
:-5t-1
is already pretty short if you ask me. But you can squeeze everything into a one-liner:
:d|-5t-1
and remove the 1 because it's implied by -:
:d|-5t-
Barring making a custom command or mapping I don't see how you could make it shorter.
:-5y<CR>Vp
is it shorter?
if you need do that really often, add this into your vimrc:
command! -range R d|<line1>,<line2>t-
then you can just do :-5R replace current line with -5 line
or 2,4R to cp line 2-4 (3 lines) to current line, and replace current line.
If you don't mind a plugin, my LineJuggler plugin offers a ]r command (and many more):
]r Fetch the line [count] visible lines above the current line and replace the current line with it.
With it, your example would be the short and easy 5]r
In addition, the companion LineJugglerCommands plugin now offers a similar :Replace Ex command. Again, your example would be
:Replace -5

Search and replace in vim in specific lines

I can use
:5,12s/foo/bar/g
to search for foo and replace it by bar between lines 5 and 12. How can I do that only in line 5 and 12 (and not in the lines in between)?
Vim has special regular expression atoms that match in certain lines, columns, etc.; you can use them (possibly in addition to the range) to limit the matches:
:5,12s/\(\%5l\|\%12l\)foo/bar/g
See :help /\%l
You can do the substitution on line 5 and repeat it with minimal effort on line 12:
:5s/foo/bar
:12&
As pointed out by Ingo, :& forgets your flags. Since you are using /g, the correct command would be :&&:
:5s/foo/bar/g
:12&&
See :help :& and friends.
You could always add a c to the end. This will ask for confirmation for each and every match.
:5,12s/foo/bar/gc
Interesting question. Seems like there's only range selection and no multiple line selection:
http://vim.wikia.com/wiki/Ranges
However, if you have something special on line 5 and 12, you could use the :g operator. If your file looks like this (numbers only for reference):
1 line one
2 line one
3 line one
4 line one
5 enil one
6 line one
7 line one
8 line one
9 line one
10 line one
11 line one
12 enil one
And you want to replace one by eno on the lines where there's enil instead of line:
:g/enil/s/one/eno/
You could use ed - a line oriented text editor with similar commands to vi and vim. It probably predates vi and vim.
In a script (using a here document which processes input till the EndCommand marker) it would look like:
ed file <<EndCommands
5
s/foo/bar/g
7
s/foo/bar/g
wq
EndCommands
Obviously, the ed commands can be used on the command line also.

How to let Vim process my command backforward?

Suppose I have 5 lines of text, if I input some commands to let vim process each line, Vim will process the text one by one, first line, second line, ... the last line. what I want is to let Vim process my text in reverse order. that is the last line first, then the 4th line, and at last the first line.
Why I need this?
I have the following text
1234567890
abc
123
def
1234567890
123456789
I want to remove the newline symbol(\n) from lines which contains 3 characters. so after processing,I will get the following text
1234567890
abc123def1234567890
123456789
It seems a piece of cake, I use the following command
:%s/\v(^\w{3})\n/\1/
But what i got is
1234567890
abc123
def1234567890
1234567890
Why? I guess vim first remove \n from the second line, then this line has text abc123, now vim will not remove \n after 123, since it's not 3 characters now, so vim process the next line def, and remove \n from it, that's the result i got.
If vim can process from back to front, this will not happen and I can got the result I want.
BTW, I can get the expected result in other ways, I just want to know whether this is possible.
Explicitly loop over the range of lines (e.g. the visually selected ones) backwards, then execute the command on each line. I've used :join here instead of the :substitute with a newline:
:for i in range(line("'>"), line("'<"), -1)| silent execute i . 'g/^\w\{3}$/join!' | endfor
Can be achieved using perl:
while (<>) {
chomp if (/^...$/);
print;
}
In this case it is easier to use the :global command to join the lines.
:g/^\w\{3}$/normal! gJ
The command gJ joins the current line with the following line without inserting any spaces. The global command above calls gJ on each line containing only three characters. It works by marking all the matches first, before performing the operation, so the problem of looping is avoided.
this line should do what you want:
:%s/\v(\_^\w{3}\n)+/\=substitute(submatch(0),"\n","","g")/
if you want to do it simpler with external command, e.g. awk, you could:
%!awk '{printf "\%s", length($0)==3? $0:$0"\n"}'

How to append every third line in Vim?

I'm not at all familiar with Vim but I'm working with large text files (~1G) and my standard text editors weren't cutting it.
My files are currently in this format:
Arbitrary_title_of_sequenceA
SEQ1SEQ1SEQ1SEQ1
SEQ2SEQ2SEQ2SEQ2
Arbitrary_title_of_sequenceB
SEQ1SEQ1SEQ1SEQ1
SEQ2SEQ2SEQ2SEQ2
I need a convenient way of appending the "SEQ2" line to the "SEQ1" line like so:
Arbitrary_title_of_sequenceA
SEQ1SEQ1SEQ1SEQ1SEQ2SEQ2SEQ2SEQ2
Arbitrary_title_of_sequenceB
SEQ1SEQ1SEQ1SEQ1SEQ2SEQ2SEQ2SEQ2
Considering the size of these files, doing each line separately isn't really an option. Any help would be much appreciated!
What about providing a correct sample to begin with?
:g/SEQ1/norm Jx
does what I think you want.
:g/SEQ1 is the :global command which allows you to act on each line containing the pattern SEQ1. See :help :global.
norm is the :normal command that you use to perform a normal mode command, here on every line matched by :g/SEQ1. See :help :normal.
After that comes the normal command in question:
J is used to join the current line with the line below.
x is used to remove the <Space> automatically added by Vim.
:1,$s/\(.*\n\)\(.*\)\n\(.*\n\)/\1\2\3/
1,$ -> range is all file
s/PAT1/PAT2/ -> substitute PAT1 with PAT2
.* -> match any character except new line
\n -> match new line
\(PAT1\) -> capture/remember the string that matched PAT1
\1,\2,\3 -> refers to the captured string for captures in order
Also using sed instead of vim should be faster:
sed -i 'n;N;s/\n/ /' input_file
This can be summarized as:
Read a line
Read another line and print previous line (n)
Read another line and append it to the previous line (N)
find the first newline and change it to space (s/\n/ /)
print the line (or merged lines)
I think romainl's solution is the best if you have a reliable "SEQ1" pattern you can grab onto. If not and you want to literally join every third line, you could easily do this with a macro:
qqjJxjq
Hit G to see how many lines are in the file and just repeat the macro that many times (it doesn't matter that it's higher than you need). So if the file was 1000 lines you could do 1000#q. This kind of solution is easy to remember and integrate into your normal workflow.

How do I join two lines in vi?

I have two lines in a text file like below:
S<Switch_ID>_F<File type>
_ID<ID number>_T<date+time>_O<Original File name>.DAT
I want to append the two lines in vi like below:
S<Switch_ID>_F<File type>_ID<ID number>_T<date+time>_O<Original File name>.DAT
The second line got deleted and the contents of the second line was appended to the first line.
How could I do it using command mode in vi?
Shift+J removes the line change character from the current line, so by pressing "J" at any place in the line you can combine the current line and the next line in the way you want.
Vi or Vim?
Anyway, the following command works for Vim in 'nocompatible' mode. That is, I suppose, almost pure vi.
:join!
If you want to do it from normal command use
gJ
With 'gJ' you join lines as is -- without adding or removing whitespaces:
S<Switch_ID>_F<File type>
_ID<ID number>_T<date+time>_O<Original File name>.DAT
Result:
S<Switch_ID>_F<File type>_ID<ID number>_T<date+time>_O<Original File name>.DAT
With 'J' command you will have:
S<Switch_ID>_F<File type> _ID<ID number>_T<date+time>_O<Original File name>.DAT
Note space between type> and _ID.
This should do it:
J
In vi, J (that's Shift + J) or :join should do what you want, for the most part. Note that they adjust whitespace. In particular, you'll end up with a space between the two joined lines in many cases, and if the second line is indented that indentation will be removed prior to joining.
In Vim you can also use gJ (G, then Shift + J) or :join!. These will join lines without doing any whitespace adjustments.
In Vim, see :help J for more information.
Just replace the "\n" with "".
In vi/Vim for every line in the document:
%s/>\n_/>_/g
If you want to confirm every replacement:
%s/>\n_/>_/gc
If you want to join the selected lines (you are in visual mode), then just press gJ to join your lines with no spaces whatsoever.
This is described in greater detail on the vi/Vim Stack Exchange site.
Press Shift + 4 ("$") on the first line, then Shift + j ("J").
And if you want help, go into vi, and then press F1.
In Vim you can also use gJ.
ََ
Another way of joining two lines without placing cursor to that line is:
:6,6s#\n##
Here 6 is the line number to which another line will be join. To display the line number, use :set nu.
If we are on the cursor where the next line should be joined, then:
:s#\n##
In both cases we don't need g like :s#\n##g, because on one line only one \n exist.

Resources