How can I separate pages with Vim? - vim

I have the requirement of separating an ASCII document into pages of max length 58 lines per page. At the bottom of each page there is a 3 line footer. I'm not aware of any pagination abilities within Vim that would accomplish this.
Is there a good way to do this with Vim? Perhaps highlighting every 58th line or something of the sort.
N.B. I'm seeing answers involving using a separate tool to do this; which I have thought of. What I'm interested in is a Vim solution.
Thanks.

The proper tool you're looking for is very likely a2ps.
a2ps --lines-per-page 58 --footer=footer_text document.txt

It's possible in vim as a script. Put the following in a file and :source it while the file to change is open. The s:footer list are the lines to insert after each run of 58 lines.
let s:footer = ["","Footer",""]
let s:line = 0
while s:line <= line("$") - 58
let s:line = s:line + 58
call append(s:line, s:footer)
let s:line = s:line + len(s:footer)
endwhile

Why is it important to use vim? You could use split and cat more efficiently.
Assuming your original file is called file and you have a file, footer, created that includes your footer text.
$ split -l 58 file file_parts
$ for i in file_parts*; do cat $i footer > $i.footered; done
$ cat file_parts*.footered > file.footered
file.footered would have your original file with the contents of footer inserted at every 58th line.
This is assuming you want it all back in the original file. If you don't, then the resulting file_parts*.footered files would be the already separated pages so you could skip the last step.

The two most effective ways for doing that in Vim are a script (like
#Geoff has already suggested) and a substitution command, like
:%s#\%(.*\n\)\{58}#\0---\rfooter\r---\r#
A macro (as suggested in a comment to the question) is the slowest,
a script is the fastest. A substitution command is slower than script,
but much faster than a macro.
So probably substitution is the best Vim-only solution unless its
performance is unacceptable. Only in that case, I think, it is worth
writing a script.

You're probably trying to use the wrong tool for this. You could do it much easier programmatically, for example with this simple Perl oneliner:
perl -pe'print "your\nfooter\nhere\n" unless $. % 58' inputfilename > outputfilename

A recursive macro might work. Experiment with the following (position the cursor on the first character of the first line and switch to normal mode):
qqq
qq
57j
:read footer.txt
3j
#q
q
Note that the register to which you record the macro must be cleared (qqq) and that you must not use tab-completion when reading the footer-file (:read footer.txt).
You can then use the macro (normal mode):
#q

Related

vim: Run multiple commands based off of one :global command

Apologies if this has been posted already, for I cannot find an answer, even on the vim wiki.
Is there a way I can run multiple commands in vim command-line mode off of a single :g search?
For example,
:%g/foo/ s/bar/\=#a/g | exe "norm /cat\<enter>\"ayiw"
Which (for what I intend it to do) should, on every line matching foo, replace bar with the contents of register a, and then find the next iteration of cat (even if it is many lines ahead), and put the surrounding word into register a.
Instead, this specific syntax completes the subsitution command using the current contents of the initial a register, and then executes the normal mode command on a single line (after the substitution has been completed).
This example is not my specific use-case but shows one instance where this functionality is useful. I realize I could put it all into a single exe, i.e., %g/foo/exe "norm :s/bar/\\=#a/g\<enter>/cat\<enter>\"ayiw", but I would like to do it the first way, as I feel it is more flexible.
I would prefer to do this using vanilla vim, but if a plugin exists for this, that is an okay alternative. Does anybody know if there is syntax to do such a thing?
Okay a "little bit" dirty, but does this work for you?
:let list = split(execute('g/cat/p'), '\n') | g/foo/ s/bar/\=matchstr(remove(list, 0), '\s\d\+\s\zs.*')/g
It first reads all occurences of cat save them in a list.
Then replace the first bar with the first cat... and so on.
The dirty part ist the matchstr command. the g//p also returns a number for the result so the list looks like this:
1 cat
2 cat
3 cat
...
that's why we have to remove a bit from the front. I would love to hear if someone knows a clean solution for that (I am also interested in a clean vimscript solution, does not have to be a oneliner).
You can do this (at least for multiple :s commands applied to a single :g). Example:
" SHORT STORY TITLES to single word of CapitalizedWords within <h3>s
.,$g/^\L\+$/s/[^A-Z0-9 ]\+//ge|s/\u\+/\L&/ge|s/\<\l\+\>/\u&/ge|s/ \+//ge|s/.*/<h3>&<\/h3>/

vim Jump to the next non-identical line

I am looking at files that may have several consecutive identical lines.
Is there a easy way of jumping to the next non-identical line?
Alternatively I would like to be able to fold all the lines that are equal to the initial one showing just the number of linees that are folded.
You could define your own fold-expr:
first set fdm:
:set fdm=expr
then
:set foldexpr=getline(v:lnum)==#getline(v:lnum-1)?1:0
now you can test by typing zM, to close all fold, if you are lucky ^_^ all duplicated lines are folded.
you could type zR to open all folds.
if it works and you open those kind of file very often, you could put the above lines in your .vimrc.(au with ft) if only one time job, you can write mode line into that file.
Try this:
:nmap <F2> "1y$<CR>/^\(<C-R>1$\)\#!<CR>
It maps F2 to:
copy the current line into register 1
search for (and move to) the first line that does not match the contents of register 1
This seems to work well, unless the text of your copied line has escaped characters that will confuse the search regexp. This is because register 1 is just dropped into the search expression without escaping. This would be tricky to fix reliably, but for normal log files, it shouldn't be much of a problem.
Also: if you're not married to vim and just need to read the non-consecutively-duplicated lines of a file, the canonical UNIX way is:
uniq filename
If you want to be in vim but won't need to make changes to the file, try:
:%!uniq
(If you try the latter, be sure to exit without saving)

How to repeat the same search and replace command over disjunct line ranges in Vim?

I had a situation where I wanted to replace FOO with BAR through out a file. However, I only want to do it in certain places, say, between lines 68–104, 500–537, and 1044–1195. In practice, I dropped markers at the lines of interest (via ma, mb, mc, etc.) and ran the following:
:'a,'b s/FOO/BAR/g | 'c,'d s/FOO/BAR/g | 'e,'f s/FOO/BAR/g
I had to repeat this dozens of times with different search and replace terms s/CAT/DOG, etc., and it became a pain to have to rewrite the command line each time. I was lucky in that I had only three places that I needed to confine my search to (imagine how messy the command line would get if there were 30 or 40).
Short of writing a function, is there any neater way of doing this?
On a related note. I copied FOO to the s (search) register, and BAR to the r (replace) and tried running
:'a,'b s/\=#s/\=#r/ | 'c,'d s/\=#s/\=#r/ | 'e,'f s/\=#s/\=#r/
This would have saved me having to rewrite the command line each time, but, alas, it didn’t work. The replace bit \=#r was fine, but the \=#s bit in the search pattern gave me an error.
Any tips would be appreciated.
If you need to perform a set of line-wise operations (like substitutions) on a bunch of different ranges of lines, one trick you can use is to make those lines look different by first adding a prefix (that isn't shared by any of the other lines).
The way I usually do this is to indent the entire file with something like >G performed on the first line, and then use either :s/^ /X/ commands or block-visual to replace the leading spaces with X on the lines I want.
Then use :g in conjunction with :s. eg:
:%g/^X/s/FOO/BAR/g
:%g/^X/s/BAZ/QUUX/g
Finally, remove the temporary prefixes.
In order to get rid of the necessity to retype the same search
pattern, substitution string and flags, one can simply use the
:& command with the & flag:
:'a,'bs/pat/str/g | 'c,'d&& | 'e,'f&&
(See :help :& for details.)
Instead of using marker use this one :
:68,104s/FOO/BAR/g << substitue from line 68 to 104
This should make your job a little bit easier and clearer.
inspired by #Vdt's answer:
I am not sure but you could write all the substitutions down in a file and source that file i think.
substitutions.vim:
68,104s/FOO/BAR/g
168,204s/FOO/BAR/g
618,644s/FOO/BAR/g
681,1014s/FOO/BAR/g
.
.
.
68,104s/BAZ/BOOO/g
168,204s/BAZ/BOOO/g
and then :so substitutions.vim maybe you can also use this for multiple files of same structure. you can add an e to add an ignore error message, if it is not clear that the substitutions are found on the corresponding line blocks.
With q:, you can recall previous command lines and edit them as a normal Vim buffer, so you can quickly replace FOO and BAR with something else, then re-execute the line with Enter.
The s/\=#s/\=#r/ doesn't work; as you said, this only works in the replacement part. But for the pattern, you can use Ctrl + R Ctrl + R s to insert the contents of register s, instead of \=#s. Preferably use the default register, then it's a simple s//, but you probably know that already.
When performed over a closed fold, substitutions are limited to that fold.
fold each region
put the cursor on one closed fold
perform the substitution: :s/foo/bar<CR>
move to the next closed fold with zj or zk
use the command-line history: :<C-p><CR> or :<Up><CR> to perform the same substitution
repeat…
You can also add the c flag at the end of your substitution so that Vim asks you for a confirmation before actually performing it. This can be tedious if you have lot of matches.
Here's the simplest way to do it
:5,10s/old/new/g
5,10 : startlinenum,endlinenum

vim command to restructure/force text to 80 columns

I know there are ways to automatically set the width of text in vim using set textwidth (like Vim 80 column layout concerns). What I am looking for is something similar to = (the indent line command) but to wrap to 80. The use case is sometimes you edit text with textwidth and after joining lines or deleting/adding text it comes out poorly wrapped.
Ideally, this command would completely reorganize the lines I select and chop off long lines while adding to short ones. An example:
long line is long!
short
After running the command (assuming the wrap was 13 cols):
long line is
long! short
If this isn't possible with a true vim command, perhaps there is a command-line program which does this that I can pipe the input to?
After searching I found this reference which has some more options: http://www.cs.swarthmore.edu/help/vim/reformatting.html
Set textwidth to 80 (:set textwidth=80), move to the start of the file (can be done with Ctrl-Home or gg), and type gqG.
gqG formats the text starting from the current position and to the end of the file. It will automatically join consecutive lines when possible. You can place a blank line between two lines if you don't want those two to be joined together.
Michael's solution is the key, but I most often find I want to reformat the rest of the
current paragraph; for this behavior, use gq}.
You can use gq with any movement operators. For example, if you only want to reformat to the end of the current line (i.e. to wrap the line that your cursor is on) you can use gq$
You can also reformat by selecting text in visual mode (using `v and moving) and then typing gq.
There are other options for forcing lines to wrap too.
If you want vim to wrap your lines while you're inserting text in them instead of having to wait till the end to restructure the text, you will find these options useful:
:set textwidth=80
:set wrapmargin=2
(Don't get side-tracked by wrap and linebreak, which only reformat the text displayed on screen, and don't change the text in the buffer)
Thanks to a comment from DonaldSmith I found this, as the textwidth option didn't reformat my long line of text (I was converting playing with hex-to-byte conversions):
:%!fold -w 60
That reformated the whole file (which was one line for me) into lines of length 60.
If you're looking for a non-Vim way, there's always the UNIX commands fmt and par.
Notes:
I can't comment on Unicode, it may or may not behave differently.
#nelstrom has already mentioned using par in his webcast.
Here's how we would use both for your example.
$ echo -e 'long line is long!\nshort' > 3033423.txt
$ cat 3033423.txt
long line is long!
short
$ fmt -w 13 3033423.txt
long line is
long! short
$ par 13gr 3033423.txt
long line is
long! short
To use from inside Vim:
:%! fmt -w 13
:%! par 13gr
You can also set :formatprg to par or fmt and override gq. For more info, call :help formatprg inside Vim.
Almost always I use gq in visual mode. I tell my students it stands for "Gentlemens' Quarterly," a magazine for fastidious people.

How to remove quotes surrounding the first two columns in Vim?

Say I have the following style of lines in a text file:
"12" "34" "some text "
"56" "78" "some more text"
.
.
.
etc.
I want to be able to remove the quotes surrounding the first two columns. What is the best way to do this with Vim (I'm currently using gVim)?
I figured out how to at least delete the beginning quote of each line by using visual mode and then enter the command '<,'>s!^"!!
I'm wondering if there is a way to select an entire column of text (one character going straight down the file... or more than 1, but in this case I would only want one). If it is possible, then would you be able to apply the x command (delete the character) to the entire column.
There could be better ways to do it. I'm looking for any suggestions.
Update
Just and FYI, I combined a couple of the suggestions. My _vimrc file now has the following line in it:
let #q=':%s/"\([0-9]*\)"/\1/g^M'
(Note: THE ^M is CTRLQ + Enter to emulate pressing the Enter key after running the command)
Now I can use a macro via #q to remove all of the quotes from both number columns in the file.
use visual block commands:
start mode with Ctrl-v
specify a motion, e.g. G (to the end of the file),
or use up / down keys
for the selected block specify an action, e.g. 'd' for delete
For more see
:h visual-mode
Control-V is used for block select. That would let you select things in the same character column.
It seems like you want to remove the quotes around the numbers. For that use,
:%s/"\([0-9]*\)"/\1/g
Here is a list of what patterns you can do with vim.
There is one more (sort of ugly) form that will restrict to 4 replacements per line.
:%s/^\( *\)"\([ 0-9]*\)"\([ 0-9]*\)"\([ 0-9]*\)"/\1\2\3\4/g
And, if you have sed handy, you can try these from the shell too.
head -4 filename.txt | sed 's/pattern/replacement/g'
that will try your command on the first 4 lines of the file.
Say if you want to delete all columns but the first one, the simple and easy way is to input this in Vim:
:%!awk '{print $1}'
Or you want all columns but the first one, you can also do this:
:%!awk '{$1="";$0=$0;$1=$1;print}'
Indeed it requires external tool to accomplish the quest, but awk is installed in Linux and Mac by default, and I think folks with no UNIX-like system experience rarely use Vim in Windows, otherwise you probably known how to get a Windows version of awk.
Although this case was pretty simple to fix with a regex, if you want to do something even a bit more advanced I also recommend recording a macro like Bryan Ward. Also macros come easier to me than remembering which characters need to be escaped in vim's regexes. And macros are nice because you can see your changes take place immediately and work on your line transformation in smaller bits at a time.
So in your case you would have pressed qw to start recording a macro in register w (you can of course use any letter you want). I usually start my macros with a ^ to move to the start of the line so the macro doesn't rely on the location of the cursor. Then you could do a f" to jump to the first ", x to delete it, f" to jump to the next " and x to delete that too. Then q to finish recording.
Instead of making your macro end on the next line I actually as late as today figured out you can just V (visually line select) all lines you want to apply your macro to and execute :normal #w which applies your macro in register w to each visually selected line.
See column editing in vim. It describes column insert, but basically it should work in the same way for removing.
You could also create a macro (q) that deletes the quotes and then drops down to the next line. Then you can run it a bunch of times by telling vi how many times to execute it. So if you store the macro to say the letter m, then you can run 100#m and it will delete the quotes for 100 lines. For some more information on macros:
http://vim.wikia.com/wiki/Macros
The other solutions are good. You can also try...
:1,$s/^"\(\w\+\)"/\1/gc
For more Vim regex help also see http://vim.wikia.com/wiki/Search_patterns.
Start visual-block by Ctrl+v.
Jump at the end and select first two columns by pressing: G, EE.
Type: :s/\%V"//g which would result in the following command:
:'<,'>s/\%V"//g
Press Enter and this will remove all " occurrences in the selected block.
See: Applying substitutes to a visual block at Vim Wikia

Resources