Vim plugin-writer and occasional web developer
For me, Vim is just the default go-to for any sort of text editing. Coding, notekeeping, occasional blog-posting. A while back, I used to write my emails with it, but gmail doesn’t use a proper textarea anymore, so it’s been harder. If I could use a Vim-like interface everywhere, I probably would.
My Vim config is big. Big enough to warrant a README. I realize a lot of people pride themselves on minimalistic vimrcs, and I respect that, but it’s definitely not for me. If I come up with something useful, I code it up and try it out. (That, or forget all about it and delete it years later.)
You wouldn’t guess that if you were to look at my vimrc, mind you. At time of writing, it’s a mere 40 lines, but if you look a bit more closely, you might notice a few lines that include other files into it. At some point, I realized I need separate places for settings, plugin variables, custom mappings, etc. It helps a lot when I’m wondering where to add configuration and new stuff, and, to an extent, when looking for their implementations. Though, for the latter, I often just use ctags. Did you know that ctags understands Vim mappings and commands? It’s priceless.
startup directory is a pretty small piece of it, though. I use pathogen for plugins, and maintain them in git as submodules, but I still have a
plugin directory with old, one-file plugins that don’t really need to be in their own module. I have a ton of things in
ftplugin for particular filetypes (just look at the ruby one), but whenever I can, I try to generalize any fancy logic and just leave a few buffer-local variables. I have a “utility” file in autoload I just call lib.vim for various tricks I’d like to reuse. I put everything between not-quite-a-plugin and not-just-one-command either in the
personal directory or the
wip directory, depending on how complete I feel it is. The naming is not ideal.
And yes, I do have quite a few things around I don’t use, but hey, my Vim still boots in around 170ms, so I’m happy enough. I’ll do a cleanup one of these days. Probably.
My learning process started off with me trying out vimtutor and then using Vim for a while and then… giving up. I couldn’t understand why anybody would use something this weird. I had to actually witness an expert using it with rails.vim to “get” it. It was a university course in ruby and rails, and for my final project, I used Vim. Two weeks later, and forevermore, this was my editor.
So I guess the first “useful resource” must have been the rails.vim documentation. From that point on, I started my first real job, and realized how incredibly inadequate I was. I was made fun of a lot for my “archaic” text editor, so, with the enthusiasm of a 20-year-old, I resolved to prove them wrong. I googled around for tips and tricks, I searched vim.org for useful PHP and SVN plugins, and I went deep into the help files. That last part was an amazing boost. There was a lot of the “wikipedia effect” of looking at an article, and following a bunch of links for the next half hour, learning cool new stuff. Like, say, hitting
:help * and realizing there’s an
'iskeyword' setting. Or even just scrolling down a bit and discovering
gd. But my favorite help topic has to be
:help magic. I spent a lot of time there.
Later on, I started picking bits and pieces from a lot of places. For instance, Steve Losh had a great twitter account with snippets from his own vimrc. There were blog posts with recommended settings and plugins, and at some point, I just started making new stuff up, so the learning and the creating have bled into each other a bit. As it should be, probably. No better way to learn than to try creating something new, right?
Well, I maintain the VimLinks account, so I try to keep an eye open for new things. Most of the time it’s plugins or blog posts, but following the mailing list lets me learn about new features pretty quickly.
The last thing I’ve learned that’s strictly about Vim core is probably the new
jsondecode() methods. Not a particularly fancy feature for users, but might help plugin authors. Tim Pope had to implement his own json parser for projectionist, for example. It also seems to be the beginning of async jobs! Exciting stuff, but too early to say how it’ll turn out.
The second-most-recent thing I learned is the
:smile command :). Though you need a pretty recent Vim version if you want to try it out.
I mentioned my first job and my effort to fight for the glory of my editor. Well, I didn’t start my first plugin then, but I wrote a lot of vimscript that sort of looked like a plugin. It was a PHP shop, and I worked mostly with Symfony. So, missing rails.vim, I figured I’d write a clone of that for symfony.
Believe it or not, I did a pretty good job of it. It was really, really useful to me while working there, but it wasn’t really plugin material. There were a lot of assumptions that worked for the company, but wouldn’t work for a generic project. I wanted to extract it at some point, but I moved on, started working with rails, and realized I’m probably never going to use Symfony again anyway. No point in maintaining the plugin, then. I officially deleted it, with a heavy heart, and it’s deep inside the git history of my Vimfiles, somewhere in this sea of red.
So, I grit my teeth and started on the long road towards making an official plugin. I copied a lot of the documentation structure from Tim Pope, along with quite a few ideas. I also took some inspiration from the NERDTree, which to this day I believe is the most meticulously organized Vim plugin I’ve seen.
I even wrote tests, but that came a bit later. In fact, I wrote my Vimrunner project, which I use for testing, specifically because I was tired of testing splitjoin manually. This was a plugin that had a lot of different use cases for different filetypes, and quite a few edge cases. If I hadn’t automated, development would have ground to a halt. Not that it’s moving too fast right now, but I’ve made a lot of test-backed changes.
Well, I guess I said most of that in my previous answer. Though, I really, really like its description as “a natural addition to Vim’s editing language”. It’s actually something I’ve been told by a few people, and I’m very proud of it.
A few of my other plugins try to achieve a similar thing, I think, switch, sideways, whitespaste, and maybe undoquit. I’m not sure how well they succeed, but I’m pretty happy with how comfortable they are to me at least. I rarely have to think about them at all.
It’s never been an explicit design goal, or at least, I’ve never thought about it much. I feel like it’s more a matter of the problem it tries to solve. There’s a whole category of plugins that implements a very specific action, like a small change to a few characters, or a tiny adjustment to the built-in way that Vim works. Like, say surround, or smartword. Just a tiny boost, but it tends to add up and change your workflow a lot.
I lived in Berlin for a few years, and I went to Vimberlin all the time. That was really nice, but I’m based in Sofia, Bulgaria now, and we don’t really have anything like it. I’ve considered starting a VimSofia (or should it be Sofia.vim?), but I’m not sure where I’d even start. Not to mention I have no idea if there’d be enough people in attendance. Maybe some day in the future.
I do give quite a few Vim talks, so much so that it’s become a running gag for everybody that knows me. Every time I do a talk for a conference, it’s sort of automatically considered to be a Vim talk. Which is why I’ve been doing different talks for the last few years, just for the heck of it :). Mind you, most of the talk usually consists of showing code in Vim… What can I say, it’s a lot easier than copying it in slides.
I have a lot to pick from, and the big problem is, I tend to be quite verbose. Which means it’s difficult to find one that’s short enough to be readable.
One thing I wanted to do at some point was “delete a surrounding function call”. I’ve got a gist of it here, but let me try to explain in more detail.
The first iteration of this was something like:
nmap dsf F(bdt(ds(
So, breaking it down, if I type
F(goes backwards to an opening bracket
bcontinues backwards to the beginning of the closest word
dt(deletes everything up to the next bracket (so, the word we just went over)
ds(finishes the deed by using surround.vim do “delete surrounding (“
This worked great for simple cases. If you have a
function_call(something) and you hit
dsf while between the brackets, you’ll be left with
something. This can be useful if the invocation of the
function_call moved elsewhere, so you don’t need it anymore, or something like that.
Unfortunately, this doesn’t work for method calls, or really, any invocation that involves a few words and connecting symbols. So, I had to delete this elegant mapping and expand it into this monstrocity:
" First, we do the mapping again, but this time we invoke a special function created for the task nnoremap <silent> dsf :call <SID>DeleteSurroundingFunctionCall()<cr> " (The s: of this function is the <SID> upstairs) function! s:DeleteSurroundingFunctionCall() " most of the real work is done by the s:FindFunctionCallStart function. let [success, opening_bracket] = s:FindFunctionCallStart('b') if !success return endif " if it succeeded, it moved us to the right place to just delete everything to the opening bracket... exe 'normal! dt'.opening_bracket " ...and then delete the brackets themselves exe 'normal ds'.opening_bracket " and let's get repeat.vim support, so we can do this again with a simple `.` silent! call repeat#set('dsf') endfunction function! s:FindFunctionCallStart(flags) " first, look for something that looks like a keyword, followed by a ( or a [ if search('\k\+\zs[(', a:flags, line('.')) <= 0 return [0, ''] endif " what's the opening bracket? let opener = getline('.')[col('.') - 1] " go back one word to get to the beginning of the function call normal! b " now we're on the function's name, see if we should move back some more let prefix = strpart(getline('.'), 0, col('.') - 1) while prefix =~ '\k\(\.\|::\|:\|#\)$' if search('\k\+', 'b', line('.')) <= 0 break endif let prefix = strpart(getline('.'), 0, col('.') - 1) endwhile return [1, opener] endfunction
I’ve put some comments in between the code, but if you’re not familiar with vimscript, I’m sure there’s a lot of it that looks like black magic. My advice would be to hit the
:help if you want to learn more :).
The end result? Type
dsf within the brackets in
Module::Foo.new("bar") and you’re left with just the
"bar". It’s not something I use every day, but when I do, it saves me from a lot of unnecessary thinking.
Well, I’ve been working with ember.js recently, which made me think of editor optimizations, as usual. For now, I have a set of projections for projectionist and a small project file, but I’m definitely thinking of building some rails.vim-like tools. Maybe an override of
gf? We’ll see.
As for Vim plugins, I recently had an idea for a plugin that I’m pretty excited about. The basic idea is that, after searching for something, say
Foo, I’d be able to call
:Modsearch word to modify the search by wrapping the pattern in word-limits – turn it into
\<Foo\>. Which isn’t particularly impressive, but led me to think about other modifications. Like, say, searching for a variable and then excluding strings and/or comments from the search. Now this is something I could use.
I’ve put modsearch.vim up on github, and it actually works, but there’s still some work to do on documentation. And configuration, like creating your own “modifiers” to searches. And searching for camelcased/underscored versions of the word? And maybe an
:Unmodsearch command to reset it? Though there’s a question of, should it reset it or keep a history, and how do I even tell when the modifications begin…
Well, there’s some work to do, but I’ll be sure to tweet about it when I feel it’s ready :).