K&R. Allman. BSD. Crazy Whitesmiths. No, it’s not a nu-punk-alt-country rock band, but a selection of common (well, sort of) code indent styles. Much blood has been spilled arguing over the merits of each.
I’m not brave enough to venture into that battlefield, but I can at least point the weary foot soldier towards the tent of General Uncrustify and his Marvellous Xcode Contraption.
Previous versions of Xcode allowed fairly powerful scripting. Sadly that feature has gone. It has been replaced by Behaviors instead. While they aren’t quite as flexible, they can be used to run a shell script from wherever your project has been saved.
First download the wrapper script I created for
uncrustify. You can either
save the script from your browser by visiting the
gist page, or you can clone the gist with
$ git clone git://gist.github.com/1641451.git nah_xcode_uncrustify
If you download the script from your browser, don’t forget to make it executable:
$ chmod a+x nah_xcode_uncrustify.rb
Have a quick look over the script to make sure I’m not doing anything evil,
like tarring all your source and emailing it to my swiss github account. Next,
make sure you have
uncrustify installed. If you have the nifty package
manager Homebrew, then installing is as simple as:
$ brew install uncrustify
If you have another package manager installed such as MacPorts, then you
can probably use that to install
uncrustify. Otherwise you’ll have to build it from
source and put the executable in your
/usr/bin folder. See
https://github.com/bengardner/uncrustify for more
Now add a new Behavior to Xcode. From the Xcode menu, select ”Edit Behaviors…”:
Click the + icon in the list to add a new Behavior, give it a name and scroll to the ”Run” script check box. Tick the check box and then select ”Choose script…” from the drop down list next to it:
Navigate to wherever you have the
nah_xcode_uncrustify.rb script and select
Optionally, you can assign a keyboard shortcut to the Behavior by double clicking on the command symbol next to the Behavior name. In the screen above I’ve assigned alt-shift-f to the Behavior.
Now for a test run. Open an Objective-C project. Make sure you have it
safely under version control. This is very important because
has been set to run without creating a backup of the source files. If you
don’t have your source under version control and you hate what it does to your
files, then there’s no going back.
Save all the open files by pressing alt-command-s (the script modifies the
files on disk, you need to save first) and be sure to stage any new source
file changes or whatever the equivalent is in the VCS you are using. Now run
uncrustify either by selecting the Behavior you just created from the
Behaviors menu, or using the keyboard shortcut you assigned.
Depending on the size of the project it may take a few moments to run. When it’s finished, you should see that the source controlled files are marked as modified and any open ones should reload with nice uncrusted spacing.
If you’re really eager to have formatted code, you can edit the ”Build
starts” Behavior in Xcode and tell it to run
I Hate Your Uncrusting Config, How Do I Fix It?
The first time the script is run, it creates a file called
.nah_xcode_uncrustify.cfg in your home folder. To change or tweak how
uncrustify to format your source, simply
edit that config file.
NOTE: the config file starts with a
. indicating it is hidden, so by
default Finder won’t show it. You can either open it from Terminal or else set
Finder to show hidden files (how to).
The config file is fairly well documented. If it all goes hideously wrong
during editing, you can always delete
.nah_xcode_uncrustify.cfg from your
home folder then re-reun the uncrustifying Behavior. A new default config
file will be generated.
If tweaking text config files isn’t your thing, you can use a great utility called UniversalIndentGUI to modify the config file. It allows you to play with values and preview the changes on the currently loaded source file.
To use it, simply download the OS X version from the UniversalIndentGUI
homepage, chuck it in your Applications folder, and then when you run it,
uncrustify as the code formatter:
You can use the ”Live Indent Preview” check box to toggle between the original file and the settings you’ve selected.
If you want to use the default config from the script as a starting point, you
have to tell
uncrustify to modify it a bit first. The following command
should do the trick:
$ uncrustify -c ~/.nah_xcode_uncrustify.cfg --universalindent > \ ~/uigui_nah_xcode_uncrustify.ini
That will create or overwrite a file in your home directory called
uigui_nah_xcode_uncrustify.ini. Import that into UniversalIndentGUI and
fiddle with it as much as you want.
Whether you start from scratch or tweak the default config created by the
script, you will have to save it to your home folder as a file called
.nah_xcode_uncrustify.cfg. When you do, the app will ask you if you want to
save a file starting with
. (you do) and if you want to replace the existing
.nah_xcode_uncrustify.cfg (which you also want to do).
As with manual editing, if it all goes wrong you can delete
.nah_xcode_uncrustify.cfg from your home folder then re-reun the
uncrustifying Behavior to recreate the default config.
nah_xcode_uncrustify.rb has peculiarities.
It only tries to format C, C++, Objective-C, Objective-C++ and header files
What the script considers to be the main source folder depends on the name of
the project. If the project is called
Bob, then the main source folder is
assumed to be called
Bob, and is inside the same folder containing
Bob.xcodeproj. This allows you to place third party submodules outside of
that folder, preventing the script from reformatting them. This is especially
git users as modifying
git submodule folders can cause
issues for other people trying to checkout the code.
The script tries to be clever about the language type of header files.
Objective-C, Objective-C++, C and C++ all happily use
*.h files as header
files. The script will search for a source file with the same name as the
header file and use that extension as the language setting for the header. At
the moment, it searches the main source folder for the associated source file.
If it can’t find one it treats it as an Objective-C header file on the
assumption that it’s probably a protocol.
The default formatting for C and C++ files may be a bit shonky and inconsistent compared to Objective-C files. I spend most of my time in Objective-C land these days, so didn’t have many test files handy.