Saturday, February 8, 2014

VimL List Gymnastics


Some common flexing between lists and dicts in VimL.

let a_list = range(1,10)
echo a_list
"=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

" from list -> dict
let a_dict = {}
call map(copy(a_list), 'extend(a_dict, {v:val : (v:val * v:val)})')
echo a_dict
"=> {'1': 1, '2': 4, '3': 9, '4': 16, '5': 25, '6': 36, '7': 49, '8': 64, '9': 81, '10': 100}

" from dict -> list
echo keys(a_dict)
"=> ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10']

echo map(keys(a_dict), 'str2nr(v:val)')
"=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

echo values(a_dict)
"=> [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

" collecting a single dictionary from multiple dictionaries
let b_text = "one thing\ntwo things\nthree more things"
let b_dict = {}
call map(split(b_text, '\n'), 'extend(b_dict, {split(v:val)[0] : v:val})')
echo b_dict
"=> {'one': 'one thing', 'two': 'two things', 'three': 'three more things'}

Monday, January 13, 2014

Anonymous Functions in VimL

Admit it, you’ve wanted this for a long time now.

Update: These two functions are now available in VimaholicsAnonymous.

VimL’s list sort() method allows the caller to provide a custom comparison function with which to sort by. Unfortunately, VimL doesn’t support anonymous functions (sometimes called lambdas), so the caller is forced to pre-write their comparator as a full-fledged function and provide its funcref to sort(). Well, that ends today. Now, VimL has anonymous function love! Check it:

This humble little snippet of code lets vimmers declare anonymous functions on the fly:


let fn_idx = 0
function! Fn(fn_form)
  let [fn_args, fn_body] = split(a:fn_form, '\s*=>\s*')
  let g:fn_idx = g:fn_idx + 1
  let fname = 'AnonFn_' . g:fn_idx
  let b_elems = split(fn_body, '|')
  let b_elems[-1] = 'return ' . b_elems[-1]
  exe 'func! ' . fname . fn_args . "\n"
        \. join(b_elems, "\n") . "\n"
        \. 'endfunc'
  return function(fname)
endfunction

Like this:


let x = ["one","two","three","four","five","six","seven","eight","nine","ten"]
echo sort(x, Fn('(a, b) => len(a:a) > len(a:b)'))

The magic is in the Fn() call. It takes a string argument of the form:
(arguments) => function-body statements separated by | (pipe)

The last statement will be implicitly returned by the anonymous function, so no need to explicitly add a return statement.

Unicorns, or what?!

What? You want more. Certainly, sir. Behold:


function! Fx(fn, ...)
  return call(a:fn, a:000)
endfunction

That lets you execute an anonymous function, like:


echo '2 ^ 6 = ' . string(Fx(Fn('(a, b) => pow(a:a, a:b)'), 2, 6))

Any cooler and you’d need your jumper! :-D Oh, but there’s more… I’m just getting warmed up!

Just because I like you, here’s a little extra gift:


let Mul = Fn('(a,b)=>let x = a:a | let y=a:b | x*y')
echo '5 * 6 = ' . Fx(Mul, 5, 6)

That lets you call your funcref'd anonymous function by name. Did you see what I did there? Yes, I cheated God! I named an annonymous function. Cool, eh?

So, apart from the obvious, is this really that amazing? My vote is: YES! I have plans for this technology. Here’s a sneaky hint of where I’m looking to take this:


CompileMacros

Mul = ((a,b) => let x = a:a | let y=a:b | x*y)

echo '5 * 6 = ' . #(Mul, 5, 6)
echo '2 * 6 = ' . #(((a, b) => a:a * a:b), 2, 6)

That’s right. I want hygenic macros in VimL. And that code works in my current experiments. The notation is stolen from… clojure I believe, where #(…) executes a lambda.

The call to CompileMacros will process any macros in the surrounding expressions before sourcing the result. Just for completeness, defining macros currently looks like this:


call Macro('(\+\((.\{-})\s*=>.\{-}\))', "Fn('\\1')")

call Macro('\%(\n\|^\)\@<=\s*\(\w\+\)\s*=\s*\(Fn(.*=>.\{-})\)\s*\n',
      \ "let \\1 = \\2\n" )

call Macro('#(\(.*\))\n', "Fx(\\1)\n")

call Macro('#Fn(\(.\{-}\))\n', "Fx(Fn(\\1)\n")

But that will change in the next version. Using regex to parse is an abomination; I was merely proof-of-concepting here. I’ll use VimPEG instead.

Stay tuned, if bending the spoon is what you’re in to.

Sunday, January 12, 2014

Help Us Help You

I know: you want to be a good #vim citizen; you want to be a good vimmer; you want to learn and grow and solve your problems in a timely manner and do so in a safe and supportive environment filled with smart, caring and funny peers.

I know; we all do. And we all can. Here’s how:

Help Yourself

Vim has an awesome built-in manual filled with more answers than you have questions. Almost every problem you have is addressed somewhere within its many chapters. Unfortunately, newcomers often can’t find what they’re looking for in the manual, or don’t understand the answer because of a lack of jargon or fundamental Vim-specific knowledge. This feeling of inaccessibility passes over time as you level up in Vim, but can be debilitating to the beginner. But despair not! When you don’t understand something in the manual, then that is the time for asking on #vim, but more on that later. First, here are some tips for helping yourself to some Vim love:

Note I use the loose term manual here to treat the reference-manual and user-guide as a whole, just as the built in tools for navigating them do.
  • :help topic will jump to topic within the manual.
  • :helpgrep pattern will search for all occurrences of pattern throughout the manual. Use :cope to open the quickfix window of search results.
  • There is a decent FAQ: http://vimhelp.appspot.com/vim_faq.txt.html
  • The #vim channel has a fact bot called vimgor filled with answers to common problems. These facts are often keyed on weird terms that must be supplied to vimgor precisely to elicit their associated gems of wisdom, which is practically useless to the uninitiated. Thankfully, vimgor supports a listfacts interface that will return all known facts containing the given term, e.g:
    /msg vimgor listfacts file
    will list all known keys containing file.
Note You should explore vimgor using either /msg or /query to limit interference on the #vim channel.
Feel free to call upon vimgor directly from within #vim if you are using it to answer someone else’s question. Here are some additional tools in that vein:
  • ;help topic ⇐ will provide a URL to Vim’s manual for the given topic.
  • ;man topic ⇐ will provide a URL to the appropriate unix man page.

Ask For Help

So you’ve looked in the manual and can’t find an answer, or don’t understand the answer; you’ve looked at the FAQs and checked in with vimgor to no avail. Now is the time to ask for help on #vim. Alternatively, ask reddit's vim channel. The guidelines for asking on #vim apply equally well for reddit's forum, of course.

Both for reasons of courtesy to others and self-edification, it’s important to try to find the answers to your own questions yourself before asking others on #vim. But just as important is how you ask:

For simple, easy and short examples, asking inline is acceptable and preferred, but if your problem is dense or requires more than a couple lines of data, using a pastebin service is preferred. Github’s gist is popular, but any decent ad-free pastebin will be happily tolerated. The format of your pastebin is also important. Here is an excellent example: https://gist.github.com/2759774

Etiquette

It’s obvious to most people that asking politely will yield better responses; some people need to be told explicitly. The good folk of #vim freely donate their time and energy to helping others discover the joy of being a vimmer. They delight in sharing their arcane knowledge with interested learners and inquisitive minds. In case you’re having trouble reading between the lines here, don’t approach #vim with these attitudes:
  • impatience or unwillingness to learn: You don’t care to read the :help topic provided, you just want the solution to your exact problem RIGHT NOW. Nothing upsets a vimmer more. The :help is extremely well written if not a bit obtuse for newcomers, but we’ll help you understand those intricacies — very rarely is someone told to rtfm and then left to drown in their own incomprehension.
  • rudeness or ungratefulness: diagnosing your problem might take some time and several steps requiring your participation. If you get frustrated at this or interact poorly then your chances of continued support diminish quickly. Be nice, get nice; say please and thank you.
One of my greatest teaching mentors gave me this gem once:
Teach as if your mentor is watching from the corner.
The analogy here is: ask as if you weren’t sitting behind a terminal; ask as if your mum or boss or teacher or doctor or someone you respect were listening.

In fairness, I have rarely seen such bad behaviour on #vim; it’s generally one of the most polite chat rooms I’ve ever seen. Let’s keep it that way! :-)

Tuesday, August 27, 2013

Not Classy, VimLPOO!

Rise, fellow VimLers and cease sobbing onto your consoles about the stink of VimLPOO (VimL Programming Object Orientedly). Vim keeps an open mind and so should you.

tl;dr : VimL OOP can haz class-reopening like Ruby

Okay, maybe not exactly like Ruby, but check it:

VimL’s OOP is more like javascript’s than Ruby’s. It doesn’t have explicit classes. It uses dictionaries to store data and methods that operate on it.

Here is one way in VimL to create an object factory:

function! Kid(name)                  <1>
  let k = {}                         <2>
  let k.name = a:name                <3>
  func k.say(blah) dict              <4>
    echo self.name . ': ' . a:blah
  endfunc
  return k                           <5>
endfunction

let boy = Kid('Jack')                <6>
let girl = Kid('Jill')
call boy.say('wassup?')              <7>
call girl.say('chillin'' at the hill. u?')
echo boy                             <8>
Jack: wassup?
Jill: chillin' at the hill. u?
{'name': 'Jack', 'say': function('69')}
  1. I like to use the full command form function when creating object factories (classes?).
  2. The object container is a dictionary (hash).
  3. You can explicitly set attributes outside of methods if desired.
  4. I like to use the short command form func for methods. The dict argument tells Vim that this is an instance method, providing us the self. accessor.
    Note You don’t need the ! on method declarations as you do for the outer-level.
  5. The factory must return the newly created object.
  6. Create an instance using the factory.
  7. Call methods using dot notation.
  8. The object in its native format is just a dictionary (hash).
Typically, after creating the object factory, the VimLPOO developer can’t re-open it to augment its behaviour although you can derive a new factory type from an existing one (inheritance without paternity):

function! RudeKid(name)
  let rk = Kid(a:name)               <1>
  func! rk.say(blah) dict            <2>
    echo self.name . ': Yo, biatch! ' . a:blah
  endfunc
  return rk
endfunction

let boy = RudeKid('Jack')
let girl = Kid('Jill')
call boy.say('wassup?')
call girl.say('wtf?')
echo boy
Jack: Yo, biatch! wassup?
Jill: wtf?
{'name': 'Jack', 'say': function('72')}
  1. Base this object on the parent factory.
  2. Override methods as desired.
    Note The use of ! is now required because the method already exists in the base object.
But I’m not here today to talk about weak inheritance. I wanna play with class re-opening, Vim style.

As a quick recap, a Vim object is a dictionary with data and methods that can use the self. modifier internally to refer to its data and other methods. It turns out that Vim is not too particular about who gets to claim dict access on your objects. You’re free to create external functions, adorned with the dict modifier, and have them manipulate your objects as if they were created with the class originally:

function! s:slapped() dict
  echo self.name . " just got slapped!"
endfunction

This happens to be a script-local (s:) function; global scope would work too, but why pollute unnecessarily? Now, if you tried to do a naive direct call of this, you’d be sorely disappointed:

call boy.slapped()
Error detected while processing jack_and_jill.vim:
Line   42:
E716: Key not present in Dictionary: slapped

That makes sense… We created slapped() as a script-local function, not a method on the boy instance.
Note Adding the method to the Kid() factory after having created the boy instance would be just as useless.

Happiness is just a call away:

call call('s:slapped', [], boy)

" Jack just got slapped!

:-D How cool is that?!

I have a little project in the works that uses this to allow clients of the engine to inject their own solutions to various parts of the workflow. It’s almost done, so I should be able to show something a bit more real-worldy soon. For now, what mischief can you concoct with this shiny new toy? I look forward to finding out. :-)

Vim on!

Monday, April 22, 2013

Sort Me A Column

Need to sort a column within your table without messing with the other columns?
Vim has your back with blockwise visual selections and a bit of gymnastics.

Sample table:
1 this 1 apple    is 1
2 this 4 durian   is 2
3 this 3 carrot   is 3
4 this 2 banana   is 4
5 this 5 eggplant is 5

Desired result:
1 this 1 apple    is 1
2 this 2 banana   is 2
3 this 3 carrot   is 3
4 this 4 durian   is 4
5 this 5 eggplant is 5

[wrong] Result of naive :sort command:
1 this 1 apple    is 1
2 this 4 durian   is 2
3 this 3 carrot   is 3
4 this 2 banana   is 4
5 this 5 eggplant is 5

[wrong] Result of :sort /^. / command (to skip leading numbers):
1 this 1 apple    is 1
4 this 2 banana   is 4
3 this 3 carrot   is 3
2 this 4 durian   is 2
5 this 5 eggplant is 5

A Solution

  1. Visually select the column of interest with ctrl-v
  2. Cut it with x
  3. Open a temporary scratch buffer with :enew
  4. Paste with p
  5. Sort with :sort
  6. If you need to, ensure your cursor is line 1, col 1 with gg0
  7. Visually select and yank everything, blockwisely with ctrl-vG$y
  8. Switch back to where you came from with ctrl-6 (ctrl-^)
  9. Jump to the start of your original cut with `[
  10. Paste with P

Saturday, April 20, 2013

Partial Explosion

Vim has a powerful regex spell, :help /\%[] . While it might look like the emoticon of a lunging crocodile, this little piece of regex lets vimmers match words in either their full form or any partial reduction thereof. That’s a messy explanation. A showing will help:

The pattern:

/r\%[ead]

Will match: r, re, rea, and read

Unicorns, I know!

This actually gets a lot of love in Vim’s syntax highlighting file for its own language, VimL. I wish it didn’t to be honest. I actually deplore the availability of partial keyword forms like fu instead of the full form function. But that’s not why we’re here today, so let’s move on.

Not all regex engines support this awesome atom. In fact… I don’t know of any other that does.

I am in the process of creating a vim.lang file for source-highlighter. As you would expect, it has a regex based DSL for specifying syntax items. One such item is for keywords. Easy!, I thought, I’ll just grab the keywords from Vim’s syntax files and… oh, crap… It’s infested with \%[]

So… Let’s explode them.

With a quick regex, I got the Vim keywords split out onto separate lines, like this:

a
arga[dd]
ar[gs]
bar
bn[ext]
breaka[dd]

And then we can explode out the partial variations with this little regex:

:%s/^\(.\{-}\)\[\(.\{-}\)\]/\=join(map(range(len(submatch(2))+1),  'submatch(1).strpart(submatch(2), 0, v:val)'), ' ')/

Producing:

a
arga argad argadd
ar arg args
bar
bn bne bnex bnext
breaka breakad breakadd

And just in time for dinner, too.

I'd Tap That

The Ruby world and many others have a tap() method on a class way up in the hierarchy near God that lets the bug hunting developer peek inside of objects during execution to see what the gnomes are getting up to under the covers. I needed just such a tool for my latest dabblings in Vim, so I built one and thought I’d share it here:


function! Tap(thing)
  echom string(a:thing)
  return a:thing
endfunction

It’s not very intimidating, I know, but thats a little gem of a function. I used it to debug the setting of the includeexpr option in Vim, as shown here:


set includeexpr=Tap(substitute(Tap(v:fname),'\\${\\?\\(\\w\\+\\)}\\?','\\=expand(\"$\".submatch(1))','g'))

I was suspicious that the v:fname variable was not being set properly before includeexpr was being evaluated by Vim. Tap() proved that to be the case which allowed me to focus my debug efforts on the real cause, instead of continuing to waste time fretting over the search and replace patterns in the substitute and whether I’d escaped them correctly or not. I only wish the Tap() inspiration had come to me sooner than it really did. Oh well… with Tap() as a permanent fixture in my ~/.vimrc, hopefully it won’t take me as long to think of it the next time I need its services.