Scripting XCode4 To Generate Method Comments

This article will show you how to use a python script to generate a comment from the actual declaration of the method.

So you’d have something that looked like this (Click image at right for bigger)

XCode 4 Doesn’t Support Scripting of Any Kind

XCode 4 removed the Script Menu and all the hooks that used to be there. They don’t even let you use AppleEvents to set the selection. Read the How This Script Came To Be section of the post for more details.

What you have to do is create a Service that receives the selected text, pass it to your script as an argument, then takes your script’s output and replace the selected text with it.

I wrote a Service once a long time ago using XCode and Cocoa, but that’s really way more than we need to do. Instead we will us Automator to handle the Service part for us.

Create An Automator Service

1. Launch Automator.
I just hit Cmd-Space, and start typing till I see it. But it’s in the Application folder if you’d rather click to it.

2. Create A New Workflow.
Cmd-N, or File > New

3. Select “Service” from the Choose Type Panel

4. Find the Run Shell Script Action.
If the Library isn’t showing, click the button in the tool bar, or View > Show Library.
Then there is a little search field at the top, type in Run and you should see the action (like the image at the left).
Drag the Run Shell Script action to the right hand panel.

5. Configure the Run Shell Script Action.
The core the commenting work is the script. I’m specifically using it to make method comments, but you could write your own script to do anything. Brian wrote scripts that aligned selected routine names so the colons lined up vertically, that’s pretty cool. I’ve written scripts to take a list of serial numbers and format them as variable declarations to a pirate list. The sky is the limit.
Select /usr/bin/python form the Shell: popup. You could also write your script in a different language. If you do select a different shell from the popup.
Here’s the script I’m using for the commenting:
RoutineCommenter.py
I’ll talk about the details in a minute, but right now downloaded it, open it in a text editor like TextWrangler or even XCode, copy its content, and paste it into the Run Shell Script text area.

6. Save your Automator Workflow
Cmd-S, or File > Save. Instead of a standard save dialog, you get a little panel that says “Save service as:” and lets you enter the name you want to use. Pick carefully because this is what will be in the Services menu. And I couldn’t figure out how to change it later. Ended up duplicating the file just to change the name.
The Automator file is actually created in ~/Library/Services.

That’s it as far and hooking up the script. You can try it out by opening some code in XCode4, selecting a declaration in the code, and going to Apple > Services > Generate Method Comment, or whatever you named it.

A new comment will appear and you can even Shift Tab and get taken to the description bubble for editing.

You also can do this in most text editors, so even is you dont’ use XCode you can use the service.

How the Script Works

I won’t go into the details, because I think I did a pretty good job documenting it. Basically it just parses through the string, finds the parts it needs, and generates strings for the comment. It doesn’t really try to understand the code, it just gets what it needs to generate what I want for the comment.

You can go in and change the print statements in main() to reformat it to your comment style.

Debugging An Automator Shell Script Service

Editing and debugging a script in Automator is a pain in the ass, to the point it really isn’t viable . For one you can’t run the Service in XCode and get any feedback in Automator. What Automator will suggest if you click the run button, is you add a “Get Specific Text” action before and it’s output will be passed to your script. This “works”. You run and fail in Automator.

But you can’t read python error messages in the Automator Log window. You only get the first line of the error, which is bad because python outputs a traceback first, so every error is multiple lines long.

Here’s what I ended up doing.

Really you need to run it from the command line, and you need a real text editor, not just a text view. So I opened a new document in TextWrangler (for some reason it seems wrong to use XCode4). I pasted the code from my Automator action into the TextWrangler document and saved it as a .py file.

You’ve got my .py file to start with. You’ll need to change the name of the file you downloaded. I had to add .txt to the end for security reasons.

To run your script you are going to use python from the command line in Terminal. Find where you saved your script and cd there in terminal. Type “python RoutineCommenter.py” and you’ll get a wonderful error message because there is no text passed in to parse. No arguments at all.

You could add a string of a method declaration to your command line, but one thing you’ll want to test is how your script handles multi-line declarations.

Luckily I’ve given you a way in the code. Look at line 26-29:

TESTING = False

# for debugging here are some string to parse.
testString = “- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation”

And then at 102-103:

if TESTING: routineName = testString
else: routineName = sys.argv[1]

If you set TESTING to True, then instead of using a passed in argument from the command line, the code uses testString, which is defined on line 29. Make Testing True and go back and rerun your script. Now you will see a comment.

There are a few other bits of debugging code I left in, but commented out. Hopefully they make sense if you need them.

Some things you might want to watch out for when debugging your own scripts here.

Multi-line inputs. If the declaration has returns in it, you can run into problems. For me I take out all of the returns and get a flat declaration before processing.

Tabs and Spaces. There is a difference and they can trick you. There are places in the code where I search for whitespace/code breaks by looking for a space. Really I probably should look for a tab character too.

Formatting Output. Tabs and spaces are a pain on output too. Luckily we’re programmers here and are probably using a monospace font for our code. So I just used spaces for tabs. Matter of fact I wrote a little routine that generates a number of spaces. Then I made a convenience method that returns a tab. Then I can just put it in a print statement and have a “tab” of spaces. There is also a constant for the number of spaces in a tab, so you can easily change it is you want more or less.

How This Script Came To Be

I was away from coding for months and then got a gig converting a board game to the iPhone/iPad. With much excitement I updated my tools to XCode 4 and jumped into it. There are a lot of cool features and I was overall impressed. Then I wrote my first code and realized I hadn’t migrated my scripts that generate method headers by selecting the method declaration in the code.

One of my former co-workers, Brian Webster of Fat Cat Software, had written a cool script to do it, and we put them in XCode 3’s script folder and were off to the races.

You know how it is with things like installing script menu items or editing templates etc that you did once along time ago. I couldn’t figure out where the scripts went. Then I starting looking around online and realized quickly the Script Menu was GONE in XCode4. Matter of fact there was NO way to even get the selection and pass it to AppleScript and then set it back.

I looked around occasionally for a couple of months and never found a way to do it. But today I got a little fed up. Code Snippets just weren’t doing it for me, so I went looking again. General consensus was Apple had screwed its developers again and there was no way to do it.

Then I found Brad Oliver’s post on using Automator and Perl to do it. That was all I needed, I could figure out how to replace the selection now. I just needed to find those scripts again. And I couldn’t. So I emailed Brian and started building the Service to do the replacing. When I had that figured out, I hadn’t heard back from Brian, so I just started writing my own script to do the processing.

I wrote the scripts in Python which I had never used before. I’d avoided Python because the idea of using indentation as bracketing had just rubbed me wrong. But I’d been looking into doing some game programming and found there are a lot of 3D engines that are scripted via Python, so I’d decided to learn it. This seemed as good a time as any. I was pleasantly surprised. I made a lot of progress in a relatively short amount of time, especially considering I knew nothing of the language this morning.

Hopefully this post will help you both scripting XCode4 and commenting your code better. If you have any questions feel free to ask them in comments.

UPDATE: 07/17/2012 made a change to the script to handle routines with no parameters.

4 Comments

  1. hsoi says:

    I hate that Xcode 4 removed scripting support. I’ve done the Service route, and the Behaviors don’t quite cut it either.

    I had a bunch of hotkeys and scripts that would do things like insert comment blocks with my name and the time/date stamp. Alas, no can do now, because a Service requires a selection. It’s now faster for me to just type it always than to try to fumble to type something, select it, then hit the keystroke to run my script. *sigh*

    But something like this? That’s pretty awesome, and reasonable within the constraints Apple has stuck us with. I may start using this script myself. Thanx for sharing it.

    And you’ll love Python.

  2. hsoi says:

    I finally tried using this but it kept choking on me. I think it’s trailing returns, and other whitespace getting in the way, but I haven’t had an opportunity to debug it yet.

  3. Ron says:

    The first thing it does is strip white space off the beginning and end. Mmmm… I just updated it last night because I noticed a bug on routines with no parameters, you might try the updated script.

  4. hsoi says:

    I tried it early this morning, just prior to posting my comment. So if that has your fix, well… something else is amiss.

    If you need me to, I can try to debug it later.

Comments are closed.