Today we’re making Code Pilot open source. It’s been an amazing ride with this product for us as a company and me personally, so I wanted to take this chance and tell you its story.
How I Learned to Stop Worrying and Love the Keyboard
I started using computers professionally around 1997. And by professionally, I mean using it for something more than just playing games and browsing the Internet. In my case it meant focusing on Linux and more programming than I was doing as a kid. So for me back then it was all about the command line which is where you can’t really do much with a mouse. I had to type in every command, every option, make mistypes, correct them and so in time, my typing skills started to depend more and more on my muscle memory than me being able to see the keyboard.
I started looking for ways to cut down on time spent typing. Every keystroke is precious, especially if you don’t touch-type. You need to glance at the keyboard, press the key, then look at the screen, figure out that you made a mistake, then press backspace and start over: an absurdly time-consuming process if I ever saw one.
There’s a reason why in the command line the programs are called
ps instead of “list all my processes” and
w rather than “dear computer, tell me who is logged on this machine”. Even for shorter commands, people quickly started creating aliases in the shell, which are essentially nicknames for commands with options. Then, the fantastic, regular-expression based command history search came in with bash’s Control-R shortcut (or whatever other shell introduced it first).
I lost my love for using the mouse right around that time. Every time I had to extend my arm to reach it felt like a waste, because I could just press some key combination and that would be it. And I could accomplish that before I even managed to take hold of the mouse, as my hands were already resting at the keyboard, ready to take immediate action.
During programming, I found it especially laborious to switch between source code files using the mouse. I had to do it much too often. And the worst part was that you had to start by locating something on the screen to act upon it with the mouse and then drag the pointer all the way towards that particular place. And then make some more movements. I hated it.
TextMate to the Rescue
This was one of the reasons why I fell in love with TextMate and its Command-T “Go to File” (and Command-Shift-T for “Go to Symbol”) shortcut to jump between files. This is how it works: you press a key combination, a little window pops up, you type some letters into it and it magically matches the symbol or a filename you might have wanted to jump to. I’ve used many IDEs before but none of their Open Quickly features worked nearly as well as that. The magic here lay in the genius yet very simple rule: whatever letters you typed in were matched (almost) independently within the filename you were looking up. What it means is that with the traditional Open Quickly feature of most IDEs, typing
would only match files whose names start with “fo,” like:
but not the ones that have all these letters scattered all over the filename:
which is exactly the point. I don’t want to type anything precise, I just want to press as few keys as possible to convey what I want. I want initials, so to speak. I want “FO” for “FlyingObject.h”, but not for a million (or none, for that matter) files starting with “FO”. And if I had a header file and an implementation file, I want to type “FOh” for “FlyingObject.h” and maybe “FOm” for “FlyingObject.m”, so I don’t have to squint into the list yielded by the initial “FO” query and then going up and down using the arrow keys to locate and then select the proper file. Type “FOm”, then immediately press enter and bam! – it’s open – without even looking at the keyboard or the results list. That was what I wanted!
And TextMate pretty much delivered this type of experience.
Trouble in Paradise
Then it turned out that I’m no longer a Ruby developer and I started doing some serious Cocoa development for Mac. I obviously tried to hook xcodebuild up to my beloved TextMate, but it didn’t end in marriage for some reason. Xcode had too much Objective-C-oriented stuff implemented quite well (code completion for starters) to just walk away from it.
And I’m talking about Xcode 3 at that time. But the Open Quickly feature of Xcode 3 was… a tad inadequate. No, it sucked, actually. Big time. It was horrible, I couldn’t use it. It pulled up files I didn’t want all the time. And it was slow. Initially, I thought that it’s just some problem on my part, a misconfiguration of sorts, but after talking the problem over with my peers it turned out that pretty much nobody uses Open Quickly. Everyone just reached for the mouse, squinted their eyes to browse the files list on the left side of the screen while muttering “where the hell is that file” under their breath. And then the clicking, folder expanding, opening the wrong file, closing it, going back to the left again… Hundreds of times a day.
As far as I’m concerned, programming is inextricably linked with the keyboard. Period. It’s just writing stuff. Take a look at today’s iA Writer, Byword, and other editors like that. They’re hiding everything that might be a distraction to focus on what’s important. Another button, another checkbox, another gizmo, like the mouse – are just not important. I know, I know. Interface builder, Instruments, and the rest of the visual stuff. But that’s not actually writing code.
We Were Somewhere Around Barstow, on the Edge of the Desert, When the Drugs Began to Take Hold
So, one evening we decided to implement a better Open Quickly for Xcode without really knowing how to go about it. It could be a hack of some sort that would work just for us, but if it’s so important, why not share it with the world? The latter required a considerably more ambitious approach. One that would allow people to actually install or update it.
We made quite a few attempts to make that work. Things like a separate application written in Ruby that was activated with a global shortcut which would look for a currently running Xcode instance, its foremost window, related documents, then dig through the project file, open and parse it… IT WAS A MESS. Scripting Bridge, Apple Script, reverse-engineering the Xcode (Project Builder) project file format… we thought we tried everything. F-Script Object Browser in one hand, irb with MacRuby in the other, trying to break through into the Xcode runtime, then cache some stuff it had inside the project file so we don’t have to parse it again for every use… it SUCKED. It was crashing, not all of the files were visible, it was horribly slow, but when it worked – it worked great. And I loved when it worked. I knew we were on to something. But it was a far cry from a real product, not something you could call a Beta version. Not even an Alpha.
So there were two fundamental problems with the “external app” approach. The first one was that it wasn’t really stable enough. The second problem revolved around the fact that we couldn’t considerably improve the app without really getting INSIDE the Xcode runtime, nor could we implement new features which inevitably would have to be based on accessing its internals. I couldn’t stop thinking about how awesome it would be to really have access to the whole environment and run your code actually within Xcode – how much cool stuff you would be able to do then. Remember, that was early 2010 and Xcode 3 could have really used some polish back then. Most full-time developers had a love-hate relationship with it to say the least.
I became possessed with the idea of hacking into Xcode cleanly.
I Thought Christmas Only Comes Once a Year
Initially, I was just using gdb to load some code into it, along with F-Script. I guess I also tried SIMBL or something like that, but it quickly started to be inconvenient enough to make me look for a more kosher way of going about it. I’m not sure how it happened exactly (but it must have happened sometime late at night) but I noticed a PBXLSPlugin class that looked fairly suspicious and managed to create a proper .plist file with magic spells like XCGCReady = YES and XCPluginHasUI = YES to finally make the damn IDE load my plugin bundle. When it worked, I was in serious shock. I spent days and weeks on end and weeks of my “free” time working on this thing, going through Xcode installation directories, files and bundles that looked like some internal plugins. Dumping all of the runtime to a file, grepping through it with some random regexps that came to my mind, rummaging around with F-Script, dtrace, class dump, strings, and whatever else came in handy. Then, finally, it turned out that Xcode had some plugin infrastructure after all. And it worked.
Okay, So, Tell Me About the Hash Bars
From that point onwards, writing the actual code for Code Pilot looked was shooting fish in a barrel. Getting inside the theme park was a problem, so to speak, but once we were in – it felt like all the lights were on and we’re the only ones around.
My main exploratory tools aside from gdb and F-Script were a couple of simple functions I wrote to dump the entirety of the Objective-C runtime into a file looking something like this:
CLASS: IDEProgressSearchField IDEProgressSearchField @property T@"NSArray",C,N,V_suggestionItems suggestionItems IDEProgressSearchField @property Tc,R suggestionsVisible IDEProgressSearchField @property T@"IDEProgressSearchSuggestionItem",& selectedSuggestionItem IDEProgressSearchField @property T@"
",&,V_commandDelegate commandDelegate IDEProgressSearchField @property T@" ",&,V_searchFieldDelegate searchFieldDelegate IDEProgressSearchField @property T@"NSArray",R allVisibleSuggestions IDEProgressSearchField @property T@"_IDEProgressSearchFieldCell",R cell IDEProgressSearchField +(class) cellClass IDEProgressSearchField -(void) setCommandDelegate: IDEProgressSearchField -(void) setSearchFieldDelegate:
It just went through the runtime and created a file with everything it could find. It kept writing down the class name next to each method thus facilitating the grepping of the resulting file, like for example:
in which I tried to explore the intricacies of Xcode’s built-in search. It matched both class name and method name, making the process very efficient. Then I went through the class hierarchy to verify my various theories and most of the time came back empty-handed.
The important thing was to dump the runtime after Xcode was fully up and running. It did load some stuff after the application actually started, so I had to wait for the right moment to have a complete runtime to dump. Obviously, I figured that out after looking for something that had to be there and wasn’t. Many, many times.
Other times, I tried to execute some code in gdb or load an experimental version of the plugin which inevitably led to Xcode freezing or crashing. Or going into some infinite loop which was the most irritating case. Had to kill it then. Often with some unsaved code.
It reminded me of the time when I was working on Linux kernel 2.0.x modules. I would write some new code, compile it, and want to test it. So I would load it live… and everything would crash. I mean the whole fucking computer would hang. Fingers crossed I haven’t lost any code, I’d wait a couple of minutes for the PC to reboot (hey, it was like Pentium 90Mhz or something) and start over. I’d open the code, fix one or two lines then compile it, load it, and… boom! Another five minutes went down the drain. It felt like torture. Remember Office Space? The Printer Scene? It felt a lot like that.
I dedicated something like 2-3 months of my life entirely to this project at that point. It worked, showed up in the center of the screen you were actually working on, it could bring up the list of files matching the query within the project or even its subprojects. You could also look for symbols globally (within whole code base) or just in the currently edited files. People loved it. It used Xcode’s internal index files, we’ve even managed to create our own preference pane inside Xcode preferences and made an amazing icon for it.
Initially it went on sale for $14.95. We were extremely excited.
Early adopters were saying good things about the product:
Since being exposed to Code Pilot, it has been added to the short list of apps that MUST be installed on any development machine I use. — Marcus S. Zarra
I’ve used Code Pilot for an hour, and I’m already a fan: Code Pilot makes me more productive. — Ruben Bakker
Your plugin is awesome. I tried it for about 5 minutes and then paid the $30 for it. I have been wanting something like this for a long time. Great job! — Christopher G.
But even though initial reactions of some people were very promising, the sales weren’t really as high as we had hoped for. All the Ferraris and Lamborghinis faded away in our dreams. Yeah, not really. It was getting obvious that users weren’t “getting it” for some reason. Some praised its value, while others didn’t understand the idea at all.
The Users Are Wrong
The most shocking news came from seasoned developers asking “why isn’t the mouse supported? I tried clicking the results I’ve gotten, but nothing happened.” Come again? The MOUSE? The whole point of Code Pilot was to avoid the mouse. It was supposed to be simple – you typed in 2 or 3 characters and pressed enter most of the time, because our algorithm sorted the results so that you should’ve gotten what you wanted at the top of the list. Sometimes you had to press the down arrow a couple of times or refine the query a bit, but that was it. So “mouse support”? Really?! What’s wrong with these people? The users, I mean. Hey, the users are wrong! But aren’t the users always right? Yep.
I remember one particular user who had problems with Code Pilot’s performance. I started debugging it and we started talking. Turned out he was working for Google on the Chrome codebase. Not surprisingly, it was a hell of a big project. Code Pilot’s performance was embarrassing with it. From then on, I started testing the performance on big open source projects. Adium’s code was pretty good for this.
Other time I was lurking through our web server’s logs and found out that there were some requests checking for Code Pilot updates coming from the 188.8.131.52/8 address class. It meant that Apple engineers were using it! Huge win!
A New Kind of Beast
So like with every project there were some ups and downs. At one NSConference I attended, I remember skipping many of the sessions because Xcode 4.x just came out and I was this close to making Code Pilot work with it. I had to rewrite most of the code from the scratch, but for some reason Xcode still didn’t load the bundle. I knew what I was looking for, more or less, but I couldn’t find it. And there were no other plugins like this for Xcode 4 at the time to compare the notes. None. Zilch. Null. Nada. So I didn’t even know whether what I was trying to do was possible. Who doesn’t like challenges like that?
Xcode 4 was a whole other story. After looking at the runtime dump (15Mb of plain text to go through), there was – what appeared to be – a fantastic, well designed plugin infrastructure similar to what Eclipse had. It looked like paradise – you could probably do everything with it. The only problem was that there was absolutely no documentation to work with. And more importantly, this design was very different from the one I knew from Xcode 3. Not that it discouraged me – I liked that. But it meant months of work to reverse-engineer it and re-write Code Pilot internals to cooperate with the new Xcode’s guts. Code Pilot sales weren’t very promising at that time and we were just a couple of guys working on apps that had to pay the bills one way or another. I had a lot of feature requests and ideas for Code Pilot. I wanted to make a sequel called Doc Pilot which would help browse the documentation very quickly, among other things. And so on and so forth.
Like I said, sales were pretty low. And every time a new Xcode came out I had to release a new version, because I wanted to make sure everything works great with each release. So, before anyone could use Code Pilot with a new Xcode version, I had to check it out, approve it, enable this version of Xcode in the new Code Pilot build and release it for people to update. Somehow I couldn’t bring myself to accept that Code Pilot could mess up someone’s code because of some change introduced in Cupertino that I didn’t know about. After many, many releases that haven’t caused a single problem, I finally gave in and enabled CP to run under “uncertified” Xcodes.
Another business issue we had was the fear that Apple could easily make this product completely worthless by either implementing a better Open Quickly themselves or just changing something within Xcode that would make it incompatible to the point where we wouldn’t be able to fix it. That’s probably true with every product that’s basically a hack on some other app. Especially if that app wasn’t meant to allow such hacks. We weren’t afraid of it all that much, because in the early stages every single day of development was like “Boy, is this really doable?” Time and again the answer turned out to be yes, even though I often came close to just throwing it all away, because I couldn’t figure out what method I should call or where to get a list of symbols. And when. And why it worked 90% of the time, but the remaining 10% was crashes all around. Pretty frustrating. And knowing we’re doing all of that in spite of unsatisfactory sales figures felt pretty stupid at times.
Initially, we were just selling Code Pilot for Xcode 3 with some educational and volume discounts (we’ve seen some folks buying 15 or more licenses). Then, when Code Pilot 2 came out for Xcode 4, we started selling them as a bundle. Two Code Pilots, two licenses, one purchase. Over time, with sales still not as high as we expected, we started feeling guilty that we couldn’t even reasonably justify spending the time that implementing new features would require. Hell, I even had problems with catching up on support e-mails. It pretty much sucked and I felt that if the users can’t be supported the way I would like to be supported myself, we won’t charge for it anymore. So that’s when Code Pilot became a free product. You just had to leave your e-mail and we’d send you a license, that was it.
Now we decided to take the next step and release it as an open source project on our GitHub. It’s good for Xcode 5 now, but we’re leaving its future (and future of anything based on it) to the community.
The code has a hackish feel to it, clearly a result of time-pressure driven reverse-engineering of a huge application that was always evolving in Cupertino behind a set of glass doors.
So have fun, hope it will be useful to someone.
Thanks to everyone who bought Code Pilot, downloaded it for free or supported us over this time in any way (keeping your fingers crossed counts too!) Doing it for you was well worth it!
Special thanks to Stefan Fürst, the first commercial Code Pilot user, Allan Odgaard, the creator of TextMate, and Jonathan ‘Wolf’ Rentzch for his great talk at the 2010 NSConference.