There, I fixed Xcode—You’re Welcome.
Update: Xcode 4 ships with a brand new completion engine and UI that makes my hackjob plugin look like a toy. Go use that instead!
Download xcode-auto-assistant now
In my last installment, I discussed Xcode’s frustrating implementation of code completion.
I’ve now taken matters into my own hands.
It turns out Xcode has an (undocumented) plugin system. And it
also turns out that
this guy created a
really fantastic plugin
for Xcode that automatically inserts a matching opening bracket
‘[
' when typing the closing bracket
']
’.
Now, it turns out Apple added this bracket-insertion feature on their own, possibly being inspired by this, so the plugin is now just reference material.
Really useful reference material for doing what I want to do.
I’ll save the nitty-gritty details of how the plugin works for a later post (or just browse the source). Let’s just say that we have access to keystrokes as you type them in Xcode. How would we use that to achieve automatic completion list display?
Take 1: Popup all the time!
The most useful behavior, it seems, is for the popup list to
appear as soon as you type a reasonable character that can be
completed, such as an alphabetical character. Here we’ll
type ‘u
' because we want
UIViewController
.
So that’s the first thing I implemented. Listen for alphabetical character, popup the list.
The problem turns out to be that the list takes “a
while” to appear, because we’re in Objective-C which
is still C and the number of legal symbols you
can type in a particular location is immense. I mean look
at that tiny scrollbar! There’s all manner of detritus in
there like u_quad_t
and
UIKIT_STATIC_INLINE
.
You might think, no problem, I’ll just ignore the popup and
keep typing because the list will narrow itself down as I go. But,
in practice, it turns out to be extremely annoying because you
can’t type small words like “if
" and
"for
" without the popup list slowing you down.
Examine our options
I imagine that Apple encountered this same problem, and it seems the way they addressed it was by analyzing the number of items that would match and only giving you a suggested completion if the number of possible matches is small.
This is why, with Apple’s completion, you must type
“nss
" before it will automatically suggest
something like "NSString
”. If you just type
“ns
" you can sit there forever and it’ll
never suggest anything. But if you type "cl
" it might
suggest "CLLocation
" because there aren’t a lot
of other things starting with "cl
”.
My beef with this approach is that it’s
unpredictable. How do I know how many letters I’m supposed to type
before I can press enter
and move on?
Take 2: Smart delay
The solution turned out to be surprisingly simple. Display the list after typing any alphanumeric character, but with a delay proportional to the number of characters in the word entered thus far. On the 4th character and beyond, the list always appears immediately.
This way you can type “uivi[enter]
"
as fast as possible and you get
UIViewController
(depending on your typing history,
but this is intuitive).
By the time you get to the 4th character, the list is almost always reasonably small and therefore much more helpful.
If you just type “u
" and forget what you were
doing, we’ll wait about 0.4 seconds before presenting the
list to help you.
More importantly, you can type “for (int i = 0…
" at normal typing speed without the popup appearing at all, and
once you get stuck on something you’ll pause naturally and
only then will the list appear.
Extras
Of course, you don’t want the list appearing on just any alphanumeric character: If you’re writing a string or a comment, the list isn’t going to help you. So the plugin does some extra work to detect those situations. It’s not a full-on lexer but it seems to work 99% of the time.
Additionally, if you type a period ‘.
' to
access a struct member or ObjC property, the list always appears
immediately.
One last neat feature: When you type a semicolon, it will be automatically moved to the end of the line as necessary. I don’t know about you, but I often find myself in this kind of situation:
…having just typed a method name and needing to cap off the line
with a semicolon. Normally I’d have to go find the right
arrow key first, but with the plugin you just type
‘;
' and it puts it in the right place.
Pretty cool!