Vim Tips Wiki
(reword, with minor tweaks to script)
(manual procedure and more explanation)
Line 5: Line 5:
 
|created=2004
 
|created=2004
 
|complexity=intermediate
 
|complexity=intermediate
|author=parv
+
|author=
 
|version=7.0
 
|version=7.0
 
|rating=4/1
 
|rating=4/1
Line 11: Line 11:
 
|category2=
 
|category2=
 
}}
 
}}
This snippet can be used to add newlines before or after each instance of specified strings. For instance, <tt><nowiki>:%LineBreakAt <p> </p></nowiki></tt> can be used to add a newline after <tt><nowiki><p></nowiki></tt> and <tt><nowiki></p></nowiki></tt> tags in HTML files.
+
You may need to insert a line break (newline) before or after each occurrence of a search pattern. That is useful if the newlines are needed, or as a temporary change to help understand some text. This tip shows how to insert newlines before or after specified strings, both manually and using a script to define a command so, for example, <tt><nowiki>:%LineBreakAt&nbsp;<p>&nbsp;</p></nowiki></tt> would add a newline after <tt><nowiki><p></nowiki></tt> and <tt><nowiki></p></nowiki></tt> tags in an HTML file.
   
  +
==Using search and replace==
==Usage==
 
  +
Suppose you want to insert a line break before each '<tt>(</tt>', '<tt>,</tt>' and '<tt>)</tt>' character in a line. To do that, enter these commands:
Save the following script in a file called <tt>linebreakat.vim</tt>:
 
  +
<pre>
<source lang="vim">
 
  +
/[(,)]
  +
:s//\r&/g
  +
</pre>
  +
  +
The first command searches for any occurrence of each of the three characters. You can refine this search (that is, [[Using command-line history|search again]] after correcting any problems with the [[Search patterns|search pattern]]). When the search correctly [[Highlight all search pattern matches|highlights]] the wanted hits, enter the second command to insert a newline before each hit. In the substitute command, the ''find'' pattern is empty, so the last search is used; in the ''replacement'', <tt>\r</tt> inserts a newline and <tt>&</tt> inserts the search hit (see [[search and replace]]).
  +
  +
==Using a script==
  +
The following script defines a user command to automate the insertion of line breaks. Save the script in a file called <tt>linebreakat.vim</tt>.
  +
<pre>
 
" Insert a newline after each specified string (or before if use '!').
 
" Insert a newline after each specified string (or before if use '!').
 
command! -bang -nargs=+ -range LineBreakAt <line1>,<line2>call LineBreakAt('<bang>', <f-args>)
 
command! -bang -nargs=+ -range LineBreakAt <line1>,<line2>call LineBreakAt('<bang>', <f-args>)
Line 21: Line 30:
 
let save_search = @/
 
let save_search = @/
 
let pat_list = map(deepcopy(a:000), "escape(v:val, '/\\.*$^~[')")
 
let pat_list = map(deepcopy(a:000), "escape(v:val, '/\\.*$^~[')")
let pattern = '\%(' . join(pat_list, '\|') . '\)\ze[^\n]'
+
let find = '\%(' . join(pat_list, '\|') . '\)\ze[^\n]'
 
" Example: 10,20s/\%(a:1\|a:2\|a:3\)\ze[^\n]/&\r/ge
 
" Example: 10,20s/\%(a:1\|a:2\|a:3\)\ze[^\n]/&\r/ge
let rpl = empty(a:bang) ? '&\r' : '\r&'
+
let repl = empty(a:bang) ? '&\r' : '\r&'
execute a:firstline . ',' . a:lastline . 's/'. pattern . '/' . rpl . '/ge'
+
execute a:firstline . ',' . a:lastline . 's/'. find . '/' . repl . '/ge'
 
let @/ = save_search
 
let @/ = save_search
 
endfunction
 
endfunction
</source>
+
</pre>
   
 
In Vim, enter the command <tt>:so&nbsp;linebreakat.vim</tt> to source the script. If you want the script sourced automatically whenever Vim starts, place the file in directory <tt>~/.vim/plugin</tt> (Unix) or <tt>$HOME/vimfiles/plugin</tt> (Windows). On Windows, enter the following command to see the name of the required directory (which you may need to create):
 
In Vim, enter the command <tt>:so&nbsp;linebreakat.vim</tt> to source the script. If you want the script sourced automatically whenever Vim starts, place the file in directory <tt>~/.vim/plugin</tt> (Unix) or <tt>$HOME/vimfiles/plugin</tt> (Windows). On Windows, enter the following command to see the name of the required directory (which you may need to create):
Line 34: Line 43:
 
</pre>
 
</pre>
   
  +
;Usage examples
Examples:
 
 
{| class="cleartable"
 
{| class="cleartable"
 
| <tt>:LineBreakAt ( )</tt> || Insert newline after each '(' and ')' in current line.
 
| <tt>:LineBreakAt ( )</tt> || Insert newline after each '(' and ')' in current line.
Line 45: Line 54:
 
|}
 
|}
   
  +
You do not need to type the entire command; depending on your system, you may find that typing <tt>:L</tt> is sufficient (press the Tab key to expand <tt>L</tt> to see other commands that start with that text {{help|'wildchar'}}).
==Explanation==
 
{{todo}}
 
*Expand explanation.
 
   
  +
The arguments to <tt>LineBreakAt</tt> are the strings that you want to find; they are not search patterns. For example, <tt>:%LineBreakAt!&nbsp;*</tt> will insert a newline before each asterisk in the whole buffer (the command escapes the <tt>*</tt> by preceding it with a backslash, so the asterisk has no special meaning).
It defines a <tt>:command</tt> <tt>LineBreakAt</tt> which takes a range and a list of arguments separated by whitespace. It then creates a pattern from this list of arguments, taking care to escape any characters that are ''special'' for Vim's default setting of <tt>'magic'</tt>. It then proceeds to add a newline after each instance of the arguments which is not already immediately followed by a newline.
 
  +
  +
If you want to include a space in the text, type a backslash before the space. For example, <tt>:%LineBreakAt!&nbsp;The\ rain</tt> will insert a newline before each occurrence of "The rain" in the whole buffer.
  +
 
===Explanation===
  +
The <tt>:command</tt> line defines a user command (<tt>LineBreakAt</tt>) that calls a function with the same name. The first argument to the function (<tt>bang</tt>) will be '<tt>!</tt>' if an exclamation mark was typed after the command, and will be an empty string otherwise. The arguments typed after the command (one or more strings separated by whitespace) are passed as the <tt>...</tt> in the function. {{help|<f-args>|&lt;f-args&gt;}}
  +
  +
The function converts the <tt>...</tt> arguments (accessed as the list <tt>a:000</tt>) by escaping the characters that have a special meaning when searching, for example, <tt>/</tt> is replaced with <tt>\/</tt> because the substitute command uses slash as a delimiter. The list of arguments is copied with <tt>deepcopy()</tt> (because arguments to a Vim function cannot be changed), then <tt>map()</tt> processes each argument (<tt>v:val</tt>) with the <tt>escape()</tt> function; the result is copied into <tt>pat_list</tt> (a list of the individual escaped search patterns).
  +
  +
A search pattern is then formed in the <tt>find</tt> variable. If there are three arguments, <tt>arg1 arg2 arg3</tt>, the pattern will be <tt>\%(arg1\|arg2\|arg3\)\ze[^\n]</tt>. Each argument is separated with <tt>\|</tt> (''or''), and the result is grouped (<tt>\%(...\)</tt>) so that what follows applies to the whole group, rather than to <tt>arg3</tt>. The <tt>[^\n]</tt> requires that a character that is not a newline follows the pattern (so another newline will not be inserted). The <tt>\ze</tt> marks the end of the search hit, so the newline will be inserted before the character that follows the pattern.
  +
  +
The replacement text <tt>(repl)</tt> is <tt>&\r</tt> (the search hit then newline) if no exclamation mark is used in the command, or is <tt>\r&</tt> (a newline then the search hit), otherwise.
  +
  +
The <tt>:execute</tt> command performs a substitute (<tt>:s///ge</tt>) on the given range of lines (by default, the current line, or as specified). The <tt>g</tt> flag (global) replaces all occurrences on each specified line, and the <tt>e</tt> flag prevents an error message being displayed if no matches are found.
   
 
==References==
 
==References==
Line 59: Line 79:
   
 
==Comments==
 
==Comments==
 
{{todo}}
  +
*Need to tweak the script if bang is used: instead of <tt>\ze[^\n]</tt>, need something similar at the beginning of the pattern.

Revision as of 05:10, 9 September 2010

Tip 671 Printable Monobook Previous Next

created 2004 · complexity intermediate · version 7.0


You may need to insert a line break (newline) before or after each occurrence of a search pattern. That is useful if the newlines are needed, or as a temporary change to help understand some text. This tip shows how to insert newlines before or after specified strings, both manually and using a script to define a command so, for example, :%LineBreakAt <p> </p> would add a newline after <p> and </p> tags in an HTML file.

Using search and replace

Suppose you want to insert a line break before each '(', ',' and ')' character in a line. To do that, enter these commands:

/[(,)]
:s//\r&/g

The first command searches for any occurrence of each of the three characters. You can refine this search (that is, search again after correcting any problems with the search pattern). When the search correctly highlights the wanted hits, enter the second command to insert a newline before each hit. In the substitute command, the find pattern is empty, so the last search is used; in the replacement, \r inserts a newline and & inserts the search hit (see search and replace).

Using a script

The following script defines a user command to automate the insertion of line breaks. Save the script in a file called linebreakat.vim.

" Insert a newline after each specified string (or before if use '!').
command! -bang -nargs=+ -range LineBreakAt <line1>,<line2>call LineBreakAt('<bang>', <f-args>)
function! LineBreakAt(bang, ...) range
  let save_search = @/
  let pat_list = map(deepcopy(a:000), "escape(v:val, '/\\.*$^~[')")
  let find = '\%(' . join(pat_list, '\|') . '\)\ze[^\n]'
  " Example: 10,20s/\%(a:1\|a:2\|a:3\)\ze[^\n]/&\r/ge
  let repl = empty(a:bang) ? '&\r' : '\r&'
  execute a:firstline . ',' . a:lastline . 's/'. find . '/' . repl . '/ge'
  let @/ = save_search
endfunction

In Vim, enter the command :so linebreakat.vim to source the script. If you want the script sourced automatically whenever Vim starts, place the file in directory ~/.vim/plugin (Unix) or $HOME/vimfiles/plugin (Windows). On Windows, enter the following command to see the name of the required directory (which you may need to create):

:echo expand('$HOME/vimfiles/plugin')
Usage examples
:LineBreakAt ( ) Insert newline after each '(' and ')' in current line.
:10,20LineBreakAt ( ) Same, in lines 10 to 20 inclusive.
:%LineBreakAt ( ) Same, whole buffer.
:LineBreakAt! ( ) Insert newline before each '(' and ')' in current line.

You do not need to type the entire command; depending on your system, you may find that typing :L is sufficient (press the Tab key to expand L to see other commands that start with that text :help 'wildchar').

The arguments to LineBreakAt are the strings that you want to find; they are not search patterns. For example, :%LineBreakAt! * will insert a newline before each asterisk in the whole buffer (the command escapes the * by preceding it with a backslash, so the asterisk has no special meaning).

If you want to include a space in the text, type a backslash before the space. For example, :%LineBreakAt! The\ rain will insert a newline before each occurrence of "The rain" in the whole buffer.

Explanation

The :command line defines a user command (LineBreakAt) that calls a function with the same name. The first argument to the function (bang) will be '!' if an exclamation mark was typed after the command, and will be an empty string otherwise. The arguments typed after the command (one or more strings separated by whitespace) are passed as the ... in the function. :help <f-args>

The function converts the ... arguments (accessed as the list a:000) by escaping the characters that have a special meaning when searching, for example, / is replaced with \/ because the substitute command uses slash as a delimiter. The list of arguments is copied with deepcopy() (because arguments to a Vim function cannot be changed), then map() processes each argument (v:val) with the escape() function; the result is copied into pat_list (a list of the individual escaped search patterns).

A search pattern is then formed in the find variable. If there are three arguments, arg1 arg2 arg3, the pattern will be \%(arg1\|arg2\|arg3\)\ze[^\n]. Each argument is separated with \| (or), and the result is grouped (\%(...\)) so that what follows applies to the whole group, rather than to arg3. The [^\n] requires that a character that is not a newline follows the pattern (so another newline will not be inserted). The \ze marks the end of the search hit, so the newline will be inserted before the character that follows the pattern.

The replacement text (repl) is &\r (the search hit then newline) if no exclamation mark is used in the command, or is \r& (a newline then the search hit), otherwise.

The :execute command performs a substitute (:s///ge) on the given range of lines (by default, the current line, or as specified). The g flag (global) replaces all occurrences on each specified line, and the e flag prevents an error message being displayed if no matches are found.

References

Comments

 TO DO 

  • Need to tweak the script if bang is used: instead of \ze[^\n], need something similar at the beginning of the pattern.