Learn Vimscript the Hard Way

Comparisons

We've gone over conditionals, but if statements aren't very useful if we can't compare things. Of course Vim lets us compare values, but it's not as straightforward as it may seem.

Run the following commands:

:if 10 > 1
:    echom "foo"
:endif

Vim will, of course, display foo. Now run these commands:

:if 10 > 2001
:    echom "bar"
:endif

Vim displays nothing, because 10 is not greater than 2001. So far everything works as expected. Run these commands:

:if 10 == 11
:    echom "first"
:elseif 10 == 10
:    echom "second"
:endif

Vim displays second. Nothing surprising here. Let's try comparing strings. Run these commands:

:if "foo" == "bar"
:    echom "one"
:elseif "foo" == "foo"
:    echom "two"
:endif

Vim echoes two. There's still nothing surprising, so what was I going on about at the beginning of the chapter?

Case Sensitivity

Run the following commands:

:set noignorecase
:if "foo" == "FOO"
:    echom "vim is case insensitive"
:elseif "foo" == "foo"
:    echom "vim is case sensitive"
:endif

Vim evaluates the elseif, so apparently Vimscript is case sensitive. Good to know, but nothing earth-shattering. Now run these commands:

:set ignorecase
:if "foo" == "FOO"
:    echom "no, it couldn't be"
:elseif "foo" == "foo"
:    echom "this must be the one"
:endif

Whoa. Stop right there. Yes, you saw that right.

The behavior of == depends on a user's settings.

I promise I'm not messing with you. Try it again and see. I'm not kidding, I can't make this stuff up.

Code Defensively

What does this mean? It means that you can never trust the == comparison when writing a plugin for other people to use. A bare == should never appear in your plugins' code.

This idea is the same as the "nmap versus nnoremap" one. Never trust your users' settings. Vim is old, vast, and complicated. When writing a plugin you have to assume that users will have every variation of every setting.

So how can you get around this ridiculousness? It turns out that Vim has two extra sets of comparison operators to deal with this.

Run the following commands:

:set noignorecase
:if "foo" ==? "FOO"
:    echom "first"
:elseif "foo" ==? "foo"
:    echom "second"
:endif

Vim displays first because ==? is the "case-insensitive no matter what the user has set" comparison operator. Now run the following commands:

:set ignorecase
:if "foo" ==# "FOO"
:    echom "one"
:elseif "foo" ==# "foo"
:    echom "two"
:endif

Vim displays two because ==# is the "case-sensitive no matter what the user has set" comparison operator.

The moral of this story is that you should always use explicit case sensitive or insensitive comparisons. Using the normal forms is wrong and it will break at some point. Save yourself the trouble and type the extra character.

When you're comparing integers this distinction obviously doesn't matter. Still, I feel that it's better to use the case-sensitive comparisons everywhere (even where they're not strictly needed), than to forget them in a place that they are needed.

Using ==# and ==? with integers will work just fine, and if you change them to strings in the future it will work correctly. If you'd rather use == for integers that's fine, just remember that you'll need to change the comparison if you change them to strings in the future.

Exercises

Play around with :set ignorecase and :set noignorecase and see how various comparisons act.

Read :help ignorecase to see why someone might set that option.

Read :help expr4 to see all the available comparison operators.