Making Sublime Text 2 Jump to the Correct Line with Unity on OS X

posted by Allan Lavell on July 31, 2013

When you click on an error or warning in Unity, it has a line number associated with it that the editor you launch is supposed to jump to. Sublime Text 2 simply wasn’t doing that – and people have been asking for a solution for about a year now. One way to solve the problem without having access to the Sublime’s source code is to create a proxy application that interprets the information sent from Unity and launches Sublime with custom arguments. This is possible because sublime’s command line launch tool, subl, is able to open a file to a particular line and column using the syntax:

subl file:line:column

Creating a proxy had apparently already been done on Windows, but no one had made it work on OS X. Tim Keating got started on a proxy application, and posted a link to his Xcode project in a Sublime text support forum thread. He was unable to go any further than intercepting the Apple Event with it, because it looked like the “kpos” parameter that was supposed to hold the line number was empty. He mentioned that he was pretty sure the information was there, but he couldn’t figure out how to extract it.

I decided I’d give it a shot. I pulled his project and ran it. Same deal – empty kpos parameter string. Like Keating, I found it hard to believe that the information wasn’t there – it seemed more like a mishap on the part of the code logging the information. I decided to dig into the event object on a lower level and find out for myself what was there.

The object was an NSAppleEventDescriptor, which has a method called data that returns the object in its raw NSData format. I wrote code to write the NSData of the event to a file when SublimeProxy was run. I then ran SublimeProxy twice on the same file, that had errors on two different lines. This produced two output files containing the hex representing the event objects. I opened up both files in Hex Fiend, and ran a diff on them.

hexdiff2

I found that at offset 0x01A6 (422 in base 10) there were 2 bytes that differed – and they seemed to match up to the number representing the line in question, minus one! I verified this by running it on a few more files, and indeed the 2 bytes at offset 0x01A6 in the hex of the event represent the line number with a little-endian 16-bit unsigned integer. It was a simple matter to pull those bytes out and invoke sublime text using that line number.

SubimeProxy Release

I have forked Keating’s original project and posted the source code and build of the working proxy on Github. If you want to use Sublime Text 2 with Unity, I recommend checking it out.

Download the SublimeProxy Xcode project and a pre-compiled build on my Github page.

There’s still an issue with the implementation, where the first time you try to open a file it gives the error:

The document “x” could not be opened. SublimeProxy cannot open files in the “All files” format.

If that comes up just click OK and then click on the file again; it should open. I’m not sure why it happens – seems to be something to do with Document Types in the info.plist. I haven’t got the time to sort that out at the moment – if anyone knows how, let me know, or fix it yourself and send a pull request on Github.

Discuss on Reddit

Twitter: @allanlavell

P.S. It was looking like the Sublime Text team weren’t planning to fix the issue, hence my interest in solving the problem – but according to a recent post on this thread, the issue is solved in the new beta of Sublime Text 3. I’m posting the proxy anyways for people still using Sublime Text 2.

  • Tom Johnstone

    Thanks for your post. If you have spaces in your file path then you need to remove %20 escape characters. I added the following line to fix this issue: filepathWithLine = [filepathWithLine stringByReplacingOccurrencesOfString:@"%20" withString:@" "];

  • Brian Hall

    Hey! Thanks for your post, most helpful. I’m in the process of making a shim app that will create a batch file that will run on my Parallels VM to start up Visual Studio for me. So this is a real help. I improved on this a bit. I found that the kpos descriptor is there, but the data is in a NSData structure. So rather than going to a fixed offset, you can just use the bytes off the NSData of the EventDescriptor from the kpos key. There is a bunch of data in there, I’m not sure what it is, but the second short is the line number we want. It seems strange to me that its a short, as what if I wanted a 10,000 line file? I guess I can’t have that. Anyway, hope this helps!

    const AEKeyword filekey = ‘—-’;
    const AEKeyword kpos = ‘kpos’;

    NSString *filepath = [[[event descriptorForKeyword:filekey] stringValue] substringFromIndex:7];
    NSAppleEventDescriptor *kposEventDescriptor = [event descriptorForKeyword:kpos];
    NSData *kposData = [kposEventDescriptor data];
    unsigned short *lineNumbers = (unsigned short*)[kposData bytes];
    UInt16 x = lineNumbers[1]; // its the second short, there is a lot of data here, most of it not used.
    // something like , where the 47 is the “71″ we want.
    if (x == 0xFFFE)
    {
    x = 0;
    }
    ++x;

    Brian


Twisted Oak Studios offers consulting and development on high-tech interactive projects. Check out our portfolio, or Give us a shout if you have anything you think some really rad engineers should help you with.

Archive

More interesting posts (10 of 14 articles)

Or check out our Portfolio.