Vim: Searching

This is part 5 of a series of tutorial to Vim. You can read Part 4 here.

/

If you ever found yourself using a program which shows text in terminal, and you want to search something, the first guess you want to make is /. The key / will work, by default, for several programs1: less, more, tmux2, even Firefox. To state it explicitly, the way to search in Vim is by pressing / (obviously in normal mode), followed by keywords you want to search for and then hit return (enter).

Lesson 6

/ searches for keywords.

/ works for searching in several other programs including less, more, git status,
Firefox. When in doubt about how to search, press / in any unfamiliar program.

  • Very soon, we'll boost Vim to enable fancy features such as eager search-searching as we enter keywords, highlighting all the matched words, etcetera.
  • Press n to jump to next matched item.
    • N to go to previous matched item (adding shift reverses the action)
  • Or press <shift>-/ (?) to search backwards.

Regex

No search system is worth its salt if doesn't support regex. Vim considers its users to be power users and enables regex by default. The symbols might be a little different, from what you are used to, but you will find support for all the rules you demand. If you are unfamiliar with Regular Expressions, also called as 'regex', here's a 5 line crash course on regex:

  1. Regex are patterns of characters used to match strings which conform to certain well defined patterns or rules.
  2. * is a greedy quantifier, it will match character preceding it to 0 or more characters in text.
    • Example will illustrate this better:
      • ba* will match 'b', 'ba', 'baa', 'baaa', 'baaaaaaaaaaaa', …
    • + is same as *, except it matches 1 or more
      • ba+ will match 'ba', 'baaa' but not 'b'
  3. . will match any single character
    • b.t will match any of 'bit', 'bat', 'but', 'bot', 'b4t', 'b%t', …
  4. Characters except quantifiers as shown above (there are more than these), match to themself.
    • help will match 'help', nothing else
    • To search for characters which denote quantifiers, you have to escape them using a backslash \
    • bit\. will match just 'bit.' and nothing else.
  5. [...] will match any one of the characters inside '[' and ']'
    • [1234567890] will match any single digit
    • above rule can be shorten as [0-9] similar to [a-zA-Z] to match characters in range a-z.
    • append a + and you have regex rule to search for numbers greater than 9.

Vim supports much more regex rules than what's stated above, its language is bigger than Regular Languages-it supports positive look ahead, negative look behind etc3. It also supports capturing and back referencing, which is discussed next.

Substitution on Steriods

The way to replace foo with bar in Vim is:

:%s/foo/bar/gc

Now, one might think that's too complex for substituting since in most of other editors you can simply press <ctrl>+r, foo and bar. But, if you spend enough time with Vim, you will realize Vim has generic command pattern and substitute is just following that. Once you get hang of the generic pattern, applying any command is actually very simply.

Let's dissect the substitute command:

:%s/foo/bar/gc
  • : tells vim to accept command
  • % runs command over the whole file
    • we could have specified other range explicitly such as 12,34 (from line 12 to 34)
    • stating range is very helpful in places where you want to target the substitution.
      • it may help to eliminate the c flag, and thus the labor of pressing 'y' for each subsitution
  • s s stands for substitute, since that's what we are doing here
    • we'll also look at more powerful command g later
  • foo what we are searching for
  • bar what to substitute with
  • g and c are optional flags which mean
    • g global: substitute multiple instances of foo on same line
    • c confirm: prompt for confirmation before substituting.
    • these flags are needed most of the time, but not always.

Regex

  • Regular Expression can be very helpful for substitution too. You can 'capture' patterns and then later use them while substituting.
  • Here's an example:

    1: var fun1 = function () {
    2:               return "I am fun1";
    3:            };
    4: var fun2 = function () {
    5:               return "I am fun2";
    6:            };
    7: var fun3 = function () {
    8:               return "I am fun3";
    9:            };
    
  • suppose we want to refactor var funx = function () { to function funx() {, that is:

    function fun1 () {
      return "I am fun1";
    };
    function fun2 () {
      return "I am fun2";
    };
    function fun3 () {
      return "I am fun3";
    };
    
  • Clearly, just a small part of line is different- the function name, rest is same. We can use regex to capture the varying part and use that in substitute.
  • So, let's write regex for capturing funx: var (\w+) = function () {
    • by capturing a pattern, we can 'back reference' it later.
    • we can back reference captured patterns, using \1 for first, \2 for second and so on.
  • thus we can refactor by executing:

    :%s/var \(\w\+\) = function () {/function \1 () {/
           |←regex→|                          | |
                                               ↪ Backreference
       |       ←    old   →        ||   ← new →      |
    
    • Expression labeled as 'regex' is actually regex capturing pattern.
      • In (\w+), we have to escape (, + and ). Without escaping, Vim would try to match them with literal characters '(', '+' and ')'.
  • Just to illustrate the convenience of specifying the range, consider a slightly different problem: we want to refactor the first two functions only. There are two ways of doing it:
    1. we can append c flag to prompt us before every substitution, so that we can say 'no' when it tries to substitute the third time.
      • but what happens if there are 100 functions before fun1, we'll have to hit n 100 times just to reach fun1
    2. we can specify the exact range.
      • here, it will be 1,64. Thus the command can be re written as:

        :1,6s/var \(\w\+\) = function () {/function \1 () {/
        

Lesson 7

Substitute command
:[range]s/<old>/<new>/[optional flags]

Specifying precise range is better than c flag. It saves unintentional changes and manual work.

Remember to use regular expressing and capture the patterns which differ. Use back referencing to refer captured expression and then use them while substituting.

One More thing

If you have followed along the refactoring example by trying out in Vim, which BTW you should do, you will notice that the code indentation became very messy. Let me introduce to you a little operator =, this does an amazing job of auto aligning the code. = is composable as any other commands, you can say

  • =G to auto indent till end of buffer, thus if you are at beginning this will auto indent whole buffer.
  • == to indent current line or
  • =4<downarrow> to indent next 4 lines.

If that doesn't work as expected, you can manually, but effortlessly, indent lines using > and < operator to indent and outdent lines respectively. And of couse these guys are composable too.

Click here for part 6

Footnotes:

1

I'm not entirely sure, if other programs have Vim's binding or Vim picked up famous bindings

2

tmux is Terminal Multiplexer, it pairs very well with Vim to make a complete Development environment.

3

This site: http://www.vimregex.com/, should satisfy all your hunger for advanced regex rules.

4

1,4 would have been more precise.

Creative Commons License

Date: 2017-06-3

Author: Anurag Peshne

Emacs 25.2.2 (Org mode 9.1.14)

Validate