2016-08-15
My ambivalent view on Vim superintelligence, contrasted with GNU Emacs
I have historically had an extremely ambivalent attitude on vim as a superintelligent editor; my standard remark is that if I want such an editor, that's what I have GNU Emacs for. Part of this is just that I often want vim to not get in the way, and part of this is that I'm used to vi being vi. But a good part of this comes down to what I see as a fundamental philosophical difference between the editors.
To put it simply, vi has a powerful conceptual model at its heart, while GNU Emacs already starts out being basically a big random ball of stuff. Sure, GNU Emacs has a certain amount of concepts, but at its core it's a mechanism for wiring keystrokes to Lisp functions. It's nice when the collection of keystrokes have some conceptual unity behind them (it makes them easier to remember), but there is no fundamental model of 'how GNU Emacs edits text' that makes that necessary in the way that it does for vi. You can have commands in vi(m) that really feel like they are breaking the rules; in GNU Emacs, the rules are just conventions and may be freely violated without pain.
(However much I love it, magit clearly breaks the 'rules' of GNU Emacs in a flagrant way by binding ordinary alphabetical characters to all sorts of special actions in its status buffer. In GNU Emacs, of course, this is perfectly fine and has a long history.)
The consequence of this is that because GNU Emacs is fundamentally relatively arbitrary, it's much easier to make it do random superintelligent things without damaging what conceptual integrity it has. And you have a clear hook in the core of the editor for doing those superintelligent things, because 'running Lisp code in response to keystrokes' is what GNU Emacs is all about.
The other advantage that GNU Emacs has is that, to put it one way, it's Lisp all the way down (even if some of the Lisp is in C). At a conceptual level and often at a practical level, GNU Emacs is written in the same language as your superintelligent extensions to it, and your extensions do the same kind of things as everything except the very lowest level code (often through exactly the same interfaces). There is no privileged (language) core to GNU Emacs.
Neither of these are true for Vim. Instead, vim has a hard core of
both a conceptual model and an implementation environment, and then
it has a plugin model that is attached on the side instead of being
a fundamental component. In Emacs, auto-indentation is conceptually
simple; you rebind newline to run your Lisp code that analyzes the
state of the text buffer and then inserts the right number of spaces
(and maybe tabs) as well as that newline. The only difference from
plain newline processing is that you're running a lot more Lisp
code and you're not inserting just a single character. In vim, well,
you need an entirely new conceptual model of hooking into keystrokes,
and then what happens is a completely different path from if you
didn't have smart auto-indentation active. And this change raises
other relatively deep questions; for example, you can normally
repeat insertions with .
. What happens if you .
an insertion
with a newline in an environment with auto-indent? Does it insert
the actual end result text, or does it basically re-run the user's
typing and thus re-trigger auto-indentation in the new context?
You can answer these questions (and you have to), and presumably vim has. But that these questions get raised when you start adding various forms of superintelligence to vim is why I feel ambivalent about the whole endeavour. On the one hand, I see the appeal and the necessity. On the other hand, it feels messy in a way that base vi(m) doesn't.
(I sort of touched on this in Where vi
runs into its limits, but the whole issue is on my mind again for reasons
beyond the scope of this entry.)
(I optimistically think it might be possible to create a vi-like 'composable operations' editor with a strong conceptual model that gracefully allowed for extensions, auto-indentation intelligence, and so on. But I have no idea what the result would look like, and it might not look entirely like vi. Perhaps it shouldn't permit arbitrary extensions to its functionality and instead have a clear model for, say, parsing buffer contents and expressing auto-indentation rules based on the parse state.)
Some options for reindenting (some of) my existing Python code
In my entry on how I'm probably going to change my style of indenting Python code, I said that it was at least a bit tempting to reformat existing code to be PEP-8 compatible (probably using the 'if I have to touch it at all, it gets reindented' approach). This leaves me with the question of how to do that.
In an ideal world I could simply load code into GNU Emacs and say 'okay, my indent level is going from 8 spaces to 4 spaces, lay out everything again with this in mind'. Unfortunately I don't think Emacs's Python mode offers this feature (and it's actually slightly harder than it looks). The one time I tried to sort of fake this with Emacs features that I could find, it blew up in my face in a painful way.
(I'm not familiar with vim's Python smarts, but maybe it has something to do this.)
The great big hammer is yapf.
This will do the reindentation, but with the side effect of
reformatting all of my code according to its idea of proper style.
Having looked at its output, I don't entirely agree with it and I
feel that it shuffles code and comments around more than I'm happy
with. I could give in (for the same reason I gave in with gofmt
),
but I'm also not convinced that yapf is going to become the one
true style that people write to.
Yapf compares itself to autopep8 and pep8ify. It looks like at least pep8ify can be told to only change indentation and not touch anything else, so perhaps that is the easiest approach. On the other hand, I might find that one or the other (or both) of these make some additional formatting changes that I'm okay with. I'd have to try them.
There's also a reindent Python package, and a version of this appears to be part of the tools that are packaged with Python 3. This says it's specifically about reindenting code and almost nothing more, which is just what I want. If the PyPi version still works in Python 2, this is probably a good thing to check out first.
Finally, there is the yak shaving option of writing my own version of this. On the one hand, it doesn't seem too complicated and in theory it would do exactly what I want. On the other hand, there are all sorts of corner cases with continued lines and so on, and if there are existing tools that do it right (or right enough) I should probably just use one of them instead of indulging my vague instinct to write some code.
(If I was all fired up to write this sort of code it might be different, but I'm not. 'Write my own' is just a vaguely grumpy reflex.)
(As usual, writing this entry has caused me to do enough research that I now know that I have a lot more options than I thought I did.)