Sunday, May 13, 2012

I guess, deep down, there are probably feelings of apprehension about releasing something so personal and close to one's heart; that insensitive malcontents might throw stones through the windows and graffiti the walls.

A bigger and more real concern is that of the endemic affliction known as Cargo Culting. Vim is like a martial art. Mastery comes from years of rigorous, dedicated practice, lots of trials and accidents and occasional brilliant successes, study at the feet of other masters and plain old Time at the coal-face. There is no short-cutting this process. You can speed it along with the right approach, but you can not just dump a master's vimrc file at $HOME and think you're playing with the big boys now. That will only lead to tear-stained keyboards and hosed code.

Heh... I'm distorting the truth only slightly here, but I was just told a funny story that allegorises this tragedy: A good friend and fellow vimmer had his Vim set up with :cursorcolumn=80 to remind him where to constrain his code lines. A colleague passing by gasped, "Oh no! Your monitor is broken!". Upon explaining that everything was ok, that the monitor was indeed working fine and that Ti... er, I mean, my friend wanted the line there, the enquirer left mollified and quietly envious of this awesome feature that his Ec, er, I mean, editor didn't have. A week later, my friend discovered his colleague's computer had a dark red line too; only, his was drawn on a transparent plastic strip sticky-taped to the monitor.

:-)

In fairness... the colleague was mocking my friend, so this is not really a story of Cargo Culting, but it shows the phenomenon well, I think. Someone sees something awesome in another person's setup and blindly copies it in the vain hopes it will bring them equal fortune too.

While not a Vim Master, I do have the occasional sharp implement that is best kept away from curious beginners. To that end, I am not just pushing my vimrc up for you to copy blindly. I give you here a snapshot in time. I have annotated it in the hope that it shows you why I chose a certain option or implemented a particular solution. It is my intention that you learn how to wield this cutting edge tool with ample guidance and clear models.

So much for the What and Why. Now a little bit about the How. An expert is not born expert. Years of training go into the growth of his skills, techniques, tools and knowledge. My vimrc has grown over the years in the same way, from humble beginnings through proud days of development and embarrassing times of foolishness all the while with unremitting hubris.

You can see examples of that growth in the snapshot below. In various places I note pieces that might eventually grow to the point of becoming their own plugin. When they do, they will be cut out of my vimrc and put into a standalone plugin of their own. Doing this helps keep the core vimrc down to a manageable size. It also allows me to share it more easily with others.

Finally, the process of cleaning up my vimrc for public release was quite a long and intellectually grueling one. It generated the philosophical challenge: what is my Vim configuration? By this I mean, it occurred to me that my vimrc was not the entirety of my Vim configuration. I have extracted and encapsulated within plugins many times over productive growths from my vimrc throughout the years. They serve me well still, sitting mostly quietly at the sides, doing their jobs unnoticeably well. To forget these pieces and not include them in my configuration would be disingenuous. They provide many of the commands that my muscle memory relies on within Vim today. They are my Vim configuration; certainly as much as my vimrc can claim to be, anyway.

Wednesday, May 9, 2012

One thing I frequently find limiting in Vim is the disturbingly all
too frequent read-only commands that display data in a pager-like dump
with the only options being: space, d, j, b, u, k and q for moving
down and up and quitting the pager. This is lame. Sometimes we want
to be able to interact with that information, or see a filtered subset
of it, or have at it with even more freedom and flexibility to do with
as we choose.

Take for example, the :scriptnames command that shows all of the
scripts Vim has loaded since the session started, in load order. We
most commonly use this list to check for the presence of a new plugin
we've installed, to confirm that it's loading correctly. Indeed, this
is one of the diagnostics #vim denizens will demand of you when
presenting with plugin failures.

If all you have is a small handful of scripts, then perusing the
output of :scriptnames manually in a dumb pager is tolerable, if not
enjoyable. If you have a lot of plugins then this quickly becomes
painful. Wouldn't it be nice to be able to filter your :scriptnames
list so that it only showed scripts matching the pattern you're
looking for? What we crave is something like:

:scriptnames /\cnerd/

to select only the loaded scripts matching the case-insensitive
pattern 'nerd' (presumably to catch one of either NERDCommenter or
NERDTree.)

That would be nice. That is what we desire. But we are left wanting.
In fact, we're left with the unsavoury message:

E488: Trailing characters

Are we doomed to a baleful life of blind bumbling through screenfuls
of awkwardly navigated, 1970's style non-interactive data dumps?

No. We don't have to suffer this injustice. We are vimmers. We have
recourse. We can solve this problem with Vim's :redir command, in
various ways, such as:

:redir > somefile

:redir => somevar

:redir @{a-zA-Z*+"}> (to a register)

The Standard Redir Recipe

Solving the :scriptnames problem above with the file redirection solution:

This works, and isn't too painful to do manually when you need it,
so long as you don't need it too often. The :scriptnames command is a
prime example of one you wouldn't need to call very often. Having to
choreograph the five line dance above once in a blue moon is no great
ordeal, provided you can easily remember the right steps in the right
order.

Is this as good as it gets?

Well, actually, no. This is just the first step, showing you the crux
of the problem and the simplest of Vim's built-in solutions. Coming up
next, I will show you how to take it to the next level with a splash
of split, filter, join love.

Slow Motion Replay
If that makes perfect sense to you and you're even wondering why I was so verbose in some places, then I have nothing left to teach you here. Otherwise, let's break this down to show what's going on:

Note:

Vim uses the | character as a command separator, like the ; in C.

Vim's :ex mode has a notion of the current line which many commands use as their default source or target address.

1.:g// is Vim's global command. It finds all lines in the buffer matching the specified pattern, in this case: /weight:/. On each match, it sets the current line to the line of the match and runs the series of | separated commands given to it.

Note: Some of the internal commands within this sequence can alter the current line, affecting the subsequent command's notion of where the current line is.

2. copy . duplicates the given address range to below the current line. With no explicit address given then the implied source address is the current line. This effectively duplicates the weight: found by the :g// command below itself. It also sets the current line to the destination address, so the next command in our chain will implicitly operate on the duplicated line as its current line.

3.silent! is used in this pattern in case the user has :set nowrapscan (which would cause the ?weight:??? to fail on the first matching line in the file, prematurely terminating our :g// command. If the user has :set wrapscan enabled, then the last weight: in the file will be found here. Either way, cleaning up the first line is a manual exercise for our hapless user in this tutorial.

4.?weight:? searches back from the current line to the prior match of weight:. Because our current line was reset by the copy command to be the duplicated line, then a single search backwards will merely find the line we copied from. That's not good enough. We want the next one back again from that one. However, the ?? command, when chained, sets the current line again, so a subsequent ?? immediately after will find the next prior weight: line. When a search (either forward with // or backward with ??) is used without an explicit search term, Vim uses the prior search term implicitly, so ?? means search backwards for... weight: (because that's what we last searched for, in the ?weight:? command to start with).

Question for the attentive: Why did I give the explicit pattern ?weight:? in that command and not rely on the prior implicit search pattern?

5. Remember that copy . duplicates the given address range to below the current line. The given address in this case was explicitly provided by the search in #4, which points at the actual prior weight: . The current line is the duplicated weight line from step #2, so this command will copy the second prior weight line to below the duplicated weight line. Also remember that the copy command resets the current line to that of the destination address. It might be instructive to see what a sample of the file would look like if we halted our command here. That is, if we were to run the command:

Note: That rogue 80kg comes from the fact that the first match of the :g// command is at the top of the file so the subsequent ?weight??? wrapped backwards around the file, finding the bottom-most entry instead — which is 80kg in our sample data file.

6.- join moves back a line (from the current line!) and joins the following line to the current line. The result of step #6 from the three weight: lines shown in step #5 is:

weight: 87.5kgweight: 87.5kg weight: 90kg <-- current line

It's really instructive for you to run this partial command up to this point yourself to see the goblins I'm ignoring by deliberately choosing the second weight: match within the file. It's only a white lie that will all be cleared up later anyway, so I don't feel too bad about it.

The remaining 4 commands in the chain are substitutions which strip unwanted non-numeric pieces from the line, shape it into an arithmetic subtraction expression and evaluate it to produce the arithmetic difference between the two numbers. The following lines show the result of each command in turn applied to the result of step #6.

We want the differences at the end of the preceding weight: lines, so we'll use another :g// command for that:

:g/weight:/ join

Which leaves us almost done. The only bugbear remaining is the duplicated first weight in the file:

weight: 90kg weight: 90kg

Clean that up manually.

Reflection

I could have approached this in a number of different ways, but to my mind, this seemed to be the quickest and easiest approach. Other approaches might include using a macro instead of a global command, or writing a full-blown VimL script.

I tend to build these things up piecemeal, testing as I go. I follow the same methodology when constructing SQL commands. Run a partial to prove to yourself that it's good so far. Press the u key to undo and add your next chunk. Repeat until you're done.

Writing up this article to explain the solution took twenty times longer than scratching the solution out for the requester in the first place.

Oh, the answer to my earlier question? Did you figure it out? The explicit pattern given in s/kg//g forces me to be explicit again at the start of the chained commands within the :g// command.

Saturday, May 5, 2012

If you can write your code when all about youAre wasting time and blaming it on tools;If you can :findyour &path when work you do,And not get caught in GUIs like the other fools;If you can move without thought to motion,Or address your :ex with the love afforded :normal;Or ask for :help and consider your :options,And yet don't act on #vim too formal;

If you can think in regex and yet know when to pass;If you can record your steps and play them back on wont;If you can meet with :registers and :marksAnd pull from them the things you want;If you can cast your charms without the mouse debased,Poised atop the keyboard with a graceful whirl;Or yank and put betwixt the 'paste;And carve your objects without a purl;

If you can :map your keys to do your biddingAnd :command that actions be takenAnd write a script when it is fittingAnd release it as a plugin unshaken;If you can :set your favourite optionsTo change :au the event that has comeAnd so code on with new &filetype adoptionsAllowing not an end to the awesome fun.

If you can look upon your :buffers listAnd :b not bound by impartial mates;If neither quickfix nor preview you missAnd with your :compiler your projects :make;If you can :split the window beneath youHoused in :tabs each their own glimmer -Yours is the Edit and everything u,And - which is more - you'll be a Vimmer!p.s. everything bolded is a Vim :help topic.