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
Related
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.
In Vim you can search/replace text in the following way.
:%s/old/new
What does the %s mean?
% is the range over which the :s command (short for :substitute) will be run. % itself is short for the range :1,$, which means Line 1 to the last line in the buffer.
The Vim help has a couple topics (user manual - :help 10.3, reference manual - :help cmdline-ranges) describing the forms that ranges can take.
The syntax for :s (which is short for :substitute) is:
:[range]s[ubstitute]/{pattern}/{string}/[flags] [count]
The % range means "the whole file".
This is very powerful; if you would want to do substitutions on just line 1, you would use:
:1s/a/b/
Or, for just lines 1 to 3:
:1,3s/a/b/
A very useful (related) trick, is to highlight lines with visual mode (V), and then use :s to substitute just on the lines you highlighted.
See: :help [range]
:%s/old/new/
This will search the entire document for "old" and replace the first instance on each line with "new". You can use :%s/old/new/g to replace all instances of "old" with "new".
(Updated based answer on jamessan's comment).
%s stands for the whole document. See here:
http://vim.wikia.com/wiki/Ranges
I've found substantial use for commands that will find differences in two lists of words, and the best (and really only) solution to this I've found is this command:
g/^/kl |if search('^'.escape(getline('.'),'\.*[]^$/').'$','bW') |'ld
I want to make a mapping in my _vimrc file that will execute this line whenever I hit F2 for example, but I haven't been able to make it work.
If someone could explain character by character what this line actually does, I think it would make all the difference in the world. I've seen a dozen or so articles on vim mapping and none of them explain what things like / or ^ or \.*{}^$/' do in contexts like this one.
When are : needed in mappings? I've seen some examples with and most without.
When is <CR> needed?
Thanks in advance
This sounds like a job for Awk
:%!awk '!a[$0]{a[$0]=1;print}'
However you asking a two questions:
What does :g/^/kl |if search('^'.escape(getline('.'),'\.*[]^$/').'$','bW') |'ld do?
How can I make a mapping to this?
The Mapping
Let's start with "How can I make a mapping to this?":
:nnoremap <f2> :g/^/kl<bar>if search('^'.escape(getline('.'),'\.*[]^$/').'$','bW')<bar>'ld<cr>
The trick is to use <bar> instead of | and to actually execute the command with <cr>. See :h keycodes.
What does this do?
It goes over every line of the buffer with :g/^/ and deletes lines that are the same as a line from above, if search('^'.escape(getline('.'),'\.*[]^$/').'$','bW') and d. The confusing parts to me are the following:
Using marks needlessly i.e. :k and range with the :d command.
A complicated building of the regex for the search() function. By using the \V (very nomagic) we can reduce the line noise in the regex: '\V\^'.escape(getline('.'),'\').'\$'
Why are you doing an O(N^2) operation when you can do an O(N)?
Simplify the command
g/^/if search('\V\^'.escape(getline('.'),'\').'\$','bWn') | d | endif
We remove the needless marks and simplify the escaping. I also added the endif to show the end of the if statement (this can be optionally left off because it will be assumed).
:g/{pat}/{cmd} The :global command runs {cmd} on ever line matching {pat}
:g/^/ is a common idiom to run a command on every line, since all lins have a beginning, ^.
if {expr} | {cmds} | endif. Execute {cmds} if the expression, {expr}, evaluates to true
search({pat}, {flags}) search for {pat} in the buffer. {flag} alter the search behavior
search() returns the line number of a match or 0 for no match found
b flag for search() means search backwards
W flag means do not wrap around the buffer when searching
n do not move the cursor
escape({str}, {chars}) escape {chars} with \
\V pattern uses very no magic meaning all regex meta characters need to be escaped
\^ and \$ are escaped for start and end of line because of \V option
:delete or :d for short delete the current line
I suggest you use the awk solution at the start of this answer.
:nnoremap <f2> :%!awk '!a[$0]{a[$0]=1;print}'<cr>
For more help see the following:
:h :range!
:h :g
:h :d
:h :l
:h :if
:h search(
:h escape(
:h /\V
Is there an easy way using a macro or ~10 line function (no plugin!) to center some text between the first and last word (=sequence of non-blank characters) on a line? E.g. to turn
>>> No user serviceable parts below. <<<
into
>>> No user serviceable parts below. <<<
by balancing the spaces +/-1? You can assume no tabs and the result should not contain tabs, but note that the first word may not start in column 1. (EDIT: ... in fact, both delimiter words as well as the start and end of the text to center may be on arbitrary columns.)
source this function:
fun! CenterInSpaces()
let l = getline('.')
let lre = '\v^\s*\S+\zs\s*\ze'
let rre = '\v\zs\s*\ze\S+\s*$'
let sp = matchstr(l,lre)
let sp = sp.matchstr(l,rre)
let ln = len(sp)
let l = substitute(l,lre,sp[:ln/2-1],'')
let l = substitute(l,rre,sp[ln/2:],'')
call setline('.',l)
endf
note
this function might NOT work in all cases. I just wrote it quick for usual case. this is not a plugin after all
the codes lines could be reduced by combining function calls. but i think it is clear in this way, so I just leave it like this.
if it worked for you, you could create a map
it works like this: (last two lines I typed #: to repeat cmd call)
You can use the :s command with the \= aka sub-replace-expression.
:s#\v^\s*\S+\zs(\s+)(.{-})(\s+)\ze\S+\s*$#\=substitute(submatch(1).submatch(3),'\v^(\s*)(\1\s=)$','\1'.escape(submatch(2),'~&\').'\2','')#
Overview
Capture the text (including white-space) between the >>> and <<< marks. Divide up the white-space on both sides of the text in half and substitute in the non-white-space text in between. This white-space balancing act is done via the regex engine's backtracking because math is hard. Lets go shopping!
Notes:
using \v or very magic mode to reduce escaping as this command is long enough
already
use # as an alternative separator instead of the usual / for :s/pat/sub/ in hopes to make it slightly more readable
Matching Pattern
:s#\v^\s*\S+\zs(\s+)(.{-})(\s+)\ze\S+\s*$#...
:s with no range supplied only do the substitution on the current line.
^\s*\S+ match the starting white-space followed by non-white-space. >>> in this case.
(\s+)(.{-})(\s+) match white-space followed by the "text" followed by white-space
3 capture groups: 1) leading white-space, 2) the "text", and 3) trailing white-space. These will be later referenced by submatch(1), submatch(2), and submatch(3) respectively
.{-} is vim-speak for non-greedy matching or .*? in perl-speak
without the non-greedy matching the second capture group would include too much white-space at its end
\S+\s*$ match the non-white-space (i.e. <<<) and any trailing white-space
Use \zs and ze to designate the start and end of the match to be replaced
Replacement
\=substitute(submatch(1).submatch(3),'\v^(\s*)(\1\s=)$','\1'.escape(submatch(2),'~&\').'\2','')
\= tells vim that replacement will be a vim expression. Also allows the use of submatch() functions
substitute({str}, {pat}, {sub}, {flags}) Our expression will be a nested substitution
substitute(submatch(1).submatch(3), ...) do a substitute over the concatenation of leading and trailing white-spacing captured in submatch(1) and submatch(3)
The {pat} is ^(\s*)(\1\s=)$. Match some white-space followed by white-space of the same length as the first or 1 character longer. Capture both halves.
escape(submatch(2),'~&\') escape submatch(2) for any special characters. e.g. ~,&,\1, ...
The {sub} is '\1'.escape(submatch(2),'~&\').'\2'. Replace with the the escaped submatch(2) (i.e. the "text" we want to center) in between the halves of white-space, \1 and \2 from the {pat}
No {flag}'s are needed so ''
Usage
If you use this often I would suggest creating a command and putting it in ~/.vimrc.
command! -range -bar -nargs=0 CenterBetween <line1>,<line2>s#\v^\s*\S+\zs(\s+)(.{-})(\s+)\ze\S+\s*$#\=substitute(submatch(1).submatch(3),'\v^(\s*)(\1\s=)$','\1'.submatch(2).'\2','')#`
Otherwise use this once and then repeat the last substitution via & on each needed line.
For more help see
:h :s/
:h :s/\=
:h sub-replace-\=
:h submatch(
:h substitute(
:h escape(
:h /\v
:h /\S
:h /\{-
:h /\zs
:h &
EDIT by Kent
Don't be jealous, your answer has it too. ^_^
I didn't change the command, just cp/paste to my vim. only add |noh at the end to disable highlighting.
If execute this command, it looks like:
I don't know of any good way. I usually do it in a semi-automatic way, by using :center on a line of text that only contains the parts that are to be centered and then move the result into the line containing the surrounding parts.
If nobody else has a better answer, perhaps boxes can help if you need to do this kind of thing a lot.
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.