Change wrap width in a text file using Vim - text

I want to format srt subtitle text files to avoid wrapping problems on my media player.
I need to set a line wrap width to a number of characters e.g. 43
I can do this with Editplus, its a built in function and works well. The reason I want to do it in Vim, firstly Editplus is only available on the PC and the secondly Vim is badass.
I have found the following solution on the net..
:set tw=43
gggqG
It does work, but not exactly how I want it.
E.g.
I have this text:
557
00:47:39,487 --> 00:47:42,453
I will have to complete some procedures,
and I asked you to check out what they are for me
after I format it, I get:
557 00:47:39,487 --> 00:47:42,453 I will
have to complete some procedures, and I
asked you to check out what they are for
me
It seems to ignore line breaks/CRs. As you can see the "I will" has been added to the first line.
How do I get it to not ignore line breaks?
EDIT: apoligies about the formatting, first time using stackoverflow!

You could use the whitespace option of formatoptions and make the lines you want to wrap end in whitespace.
:set tw=43
:set fo+=w
:g/^\a/s/$/ /
gggqG
The third line adds a space on the end of any line starting with a letter and fo+=w stops gq from joining lines that don't end in spaces.
See:
:help fo-table
:help 'formatoptions'
:help gq
:help :g
Edit in response to comments
:g/^\a/s/$/ /
This translates to:
:g/ " Search the file
^\a " For lines starting (^) with an alphabetic character (\a - equivalent to [A-Za-z])
/ " Then on each line that the regexp matches (i.e. each line starting with an alphabetic character)
s/ " Substitute...
$ " The end of line (zero-width match at the end of the line)
/ / " With a space (slashes are delimiters)
The global (:g) command will only operate on the current file, but the textwidth and formatoptions lines will last for the whole session. If you want those options to only be used on the current buffer, use :setlocal instead:
:setlocal tw=43
:setlocal fo+=w
:help :setlocal

If the only lines you want to wrap are the ones that start with text (ignore the ones that start with numbers) you can use the following.
:g/^[a-zA-Z]/normal gqq
This will run p00ya's command on each text line in the file.

With the following text (i.e. only 3 lines):
557
00:47:39,487 --> 00:47:42,453
I will have to complete some procedures, and I asked you to check out what they are for me
Use gqq (format current line) on the 3rd line after setting tw. Formatting by paragraph will not work since vim will treat all 3 lines as part of the same paragraph (paragraphs only end with a blank line).

While not exactly robust (or elegant), if you can assume that the subtitle lines do not begin with numbers, you can configure Vim to treat them as comments.
:set comments=:0,:1,:2,:3,:4,:5,:6,:7,:8,:9
gggqG
Lines starting with 0-9 will be treated as comments and won't be merged together with the text.

I can't see exactly what the issue is due to the formatting of your post, but you might want to checkout the 'formatoptions' setting in vim's help, specifically :he fo-table which contains a bunch of customization flags.
This link might be useful.

Related

Remove single line breaks but keep empty lines with Vim

Is it possible to remove single line breaks but to keep empty lines (double line breaks I guess?) in Vim? I'm using Vim's HardPencil plugin to do some word processing, and I like the hard line breaks because it makes it easier to bounce around the file. However, I need to share this information in LibreOffice Writer or Word, and hard line breaks won't work well if others edit the file. To clarify, this is what I have:
Line1.
Line2.
Line3.
Line4.
Line5.
And this is what I would like:
Line1. Line2. Line3.
Line4. Line5.
Essentially hard line breaks should be replaced with a space, and double line breaks should be preserved.
I've tried variations of :%s/\n but can't get the double line breaks to be preserved. I've found similar questions here and here, but these are not Vim specific.
You can replace line+line break with just line, and use negative look ahead \(\n\)\#! to make sure it's not followed by an empty line:
:%s/\(.\+\)\n\(\n\)\#!/\1 /
#Peter_Rincker's cleaner solution:
:%s/.\+\zs\n\ze./ /
Use gw (or gq) with a motion (like G).
gwG
gw is Vim's paragraph formatting operator which "reflows" text to the desired 'textwidth'. You may want to temporarily set 'textwidth' to 0 for unlimited text width (:set tw=0).
There some other ways as well do this as well:
Use :%!fmt- same idea as above but use fmt external tool and a filter.
:v/^$/,//-j - squeezes non-empty lines together. Fails if the end of the range does not have a blank line
:g/\v(.+\zs\n)+/,-//j - Also squeezes non-empty lines together, but avoids the range error of the previous method.
A substitution - See #Fabricator solution for an example
For more help see:
:h gw
:h operator
:h 'tw'
:h filter
:h :range!
:h :range
:h :v
:h :j
:h :g
:h magic
:h /\zs

Vim: How to delete the same block of text over the whole file

I'm reviewing some logs with Java exception spam. The spam is getting is making it hard to see the other errors.
Is is possible in vim to select a block of text, using visual mode. Delete that block every place it occurs in the file.
If vim can't do it, I know silly question, vim can do everything. What other Unix tools might do it?
Sounds like you are looking for the :global command
:g/pattern/d
The :global command takes the form :g/{pat}/{cmd}. Read it as: run command, {cmd}, on every line matching pattern, {pat}.
You can even supply a range to the :delete (:d for short) command. examples:
:,+3d
:,/end_pattern/d
Put this togehter with the :global command and you can accomplish a bunch. e.g. :g/pat/,/end_pat/d
For more help see:
:h :g
:h :d
:h :range
Vim
To delete all matching lines:
:g/regex/d
To only delete the matches themselves:
:%s/regex//g
In either case, you can copy the visual selection to the command line by yanking it and then inserting it with <C-r>". For example, if your cursor (|) is positioned as follows:
hello wo|rld
Then you can select world with viw, yank the selection with y, and then :g/<C-r>"/d.
sed
To delete all matching lines:
$ sed '/regex/d' file
To only delete the matches themselves:
$ sed 's/regex//g' file
grep
To delete all matching lines:
$ grep -v 'regex' file
grep only operates line-wise, so it's not possible to only delete matches within lines.
you can try this in vim
:g/yourText/ d
Based on our discussion in the comments, I guess a "block" means several complete lines. If the first and last lines are distinctive, then the method you gave in the comments should work. (By "distinctive" I mean that there is no danger that these lines occur anywhere else in your log file.)
For simplifications, I would use "ay$ to yank the first line into register a and "by$ to yank the last line into register b instead of using Visual mode. (I was going to suggest "ayy and "byy, but that wold capture the newlines)
To be on the safe side, I would anchor the patterns: /^{text}$/ just in case the log file contains a line like "Note that {text} marks the start of the Java exception." On the command line, I would use <C-R>a and <C-R>b to paste in the contents of the two registers, as you suggested.
:g/^<C-R>a$/,/^<C-R>b$/d
What if the yanked text includes characters with special meaning for search patterns? To be on the really safe side, I would use the \V (very non-magic) modifier and escape any slashes and backslashes:
:g/\V\^<C-R>=escape(#a, '/\')<CR>\$/,/\V\^<C-R>=escape(#b, '/\')<CR>\$/d
Note that <C-R>= puts you on a fresh command line, and you return to the main one with <CR>.
It is too bad that \V was not available when matchit was written. It has to deal with text from the buffer in a search pattern, much like this.

Vim - yank from current cursor position till beginning of line

in VIM, I understand that we can do a yank till the end of line using y$ but if e.g. my text is abcdefg and my cursor is at 'g' and I enter y^ the line will be copy without the g. My idea is to copy the whole line without the line break, any similar action will do.
0y$$ - copy the line without line break and move cursor back to the end
Making it a visual selection and then yanking that includes the character under the cursor:
v0y
If all the characters are indeed together and conform to a "vim sentence", you could use visual selection for the sentence object. A sentence in this case would match abcdefg even if that is not starting at the beginning of a line and it will not include the line ending:
visy
If you want to include trailing whitespace you would use a instead of i (mnemonic for "inside"):
vasy
The only problem with this approach (which may be what you do not want) is that it will not include leading whitespace. So if you have something like:
abcdefg
The selection will not include the leading chunk of whitespace, just abcdefg.
Turns out yanking till end of line is a thing you'll find yourself doing quite often. As such, the following mapping is quite popular.
noremap Y y$
It's so popular, it's even listed under :h Y!
If you use this mapping, the answer to your question would be 0Y
0yg_
is another option.
But visual mode is better:
v0y
v^y

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 add line numbers to range of lines in Vim?

How can I add line numbers to a range of lines in a file opened in Vim? Not as in :set nu—this just displays line numbers—but actually have them be prepended to each line in the file?
With
:%s/^/\=line('.')/
EDIT: to sum up the comments.
This command can be tweaked as much as you want.
Let's say you want to add numbers in front of lines from a visual selection (V + move), and you want the numbering to start at 42.
:'<,'>s/^/\=(line('.')-line("'<")+42)/
If you want to add a string between the number and the old text from the line, just concatenate (with . in VimL) it to the number-expression:
:'<,'>s/^/\=(line('.')-line("'<")+42).' --> '/
If you need this to sort as text, you may want to zero pad the results, which can be done using printf for 0001, 0002 ... instead of 1, 2... eg:
:%s/^/\=printf('%04d', line('.'))/
Anyway, if you want more information, just open vim help: :h :s and follow the links (|subreplace-special|, ..., |submatch()|)
cat -n adds line numbers to its input. You can pipe the current file to cat -n and replace the current buffer with what it prints to stdout. Fortunately this convoluted solution is less than 10 characters in vim:
:%!cat -n
Or, if you want just a subselection, visually select the area, and type this:
:!cat -n
That will automatically put the visual selection markers in, and will look like this after you've typed it:
:'<,'>!cat -n
In order to erase the line numbers, I recommend using control-v, which will allow you to visually select a rectangle, you can then delete that rectangle with x.
On a GNU system: with the external nl binary:
:%!nl
With Unix-like environment, you can use cat or awk to generate a line number easily, because vim has a friendly interface with shell, so everything work in vim as well as it does in shell.
From Vim Tip28:
:%!cat -n
or
:%!awk '{print NR,$0}'
But, if you use vim in MS-DOS, of win9x, win2000, you loss these toolkit.
here is a very simple way to archive this only by vim:
fu! LineIt()
exe ":s/^/".line(".")."/"
endf
Or, a sequence composed with alphabet is as easy as above:
exe "s/^/".nr2char(line("."))."/"
You can also use a subst:
:g/^/exe ":s/^/".line(".")."^I/"
You can also only want to print the lines without adding them to the file:
"Sometimes it could be useful especially be editing large source files to print the line numbers out on paper.
To do so you can use the option :set printoptions=number:y to activate and :set printoptions=number:n to deactivate this feature.
If the line number should be printed always, place the line set printoptions=number:y in the vimrc."
First, you can remove the existing line numbers if you need to:
:%s/^[0-9]*//
Then, you can add line numbers. NR refers to the current line number starting at one, so you can do some math on it to get the numbering you want. The following command gives you four digit line numbers:
:%!awk '{print 1000+NR*10,$0}'
The "VisIncr" plugin is good for inserting columns of incrementing numbers in general (or letters, dates, roman numerals etc.). You can control the number format, padding, and so on. So insert a "1" in front of every line (via :s or :g or visual-block insert), highlight that column in visual-block mode, and run one of the commands from the plugin.
If someone wants to put a tab (or some spaces) after inserting the line numbers using the this excellent answer, here's a way. After going into the escape mode, do:
:%s/^/\=line('.').' '/
^ means beginning of a line and %s is the directive for substitution. So, we say that put a line number at the beginning of each line and add 4 spaces to it and then put whatever was the contents of the line before the substitution, and do this for all lines in the file.
This will automatically substitute it. Alternatively, if you want the command to ask for confirmation from you, then do:
:%s/^/\=line('.').' '/igc
P.S: power of vim :)
The best reply is done in a duplicate question.
In summary:
with CTRL-V then G I 0 You can insert a column of zero.
Then select the whole column and increment:
CTRL-V g CTRL-A
See also: https://vim.fandom.com/wiki/Making_a_list_of_numbers#Incrementing_selected_numbers

Resources