NSTextView syntax highlighting

Rob Keniger's answer is good, but for someone looking for a more concrete example, here's a short syntax highlighter I wrote that should highlight RegEx template syntax. I want \ to be gray, with the character immediately following them to be black. I want $ to be red, with a digit character immediately following the $ to also be red. Everything else should be black. Here's my solution:

I made a template highlighter class, with a header that looks like this:

@interface RMETemplateHighlighter : NSObject <NSTextStorageDelegate>

@end

I initialize it in the nib file as an object and hook it up to my view controller with an outlet. In awakeFromNib of the view controller, I have this (where replacer is my NSTextView outlet and templateHighlighter is the outlet for the class above):

self.replacer.textStorage.delegate = self.templateHighlighter;

And my implementation looks like this:

- (void)textStorageDidProcessEditing:(NSNotification *)notification {
    NSTextStorage *textStorage = notification.object;
    NSString *string = textStorage.string;
    NSUInteger n = string.length;
    [textStorage removeAttribute:NSForegroundColorAttributeName range:NSMakeRange(0, n)];
    for (NSUInteger i = 0; i < n; i++) {
        unichar c = [string characterAtIndex:i];
        if (c == '\\') {
            [textStorage addAttribute:NSForegroundColorAttributeName value:[NSColor lightGrayColor] range:NSMakeRange(i, 1)];
            i++;
        } else if (c == '$') {
            NSUInteger l = ((i < n - 1) && isdigit([string characterAtIndex:i+1])) ? 2 : 1;
            [textStorage addAttribute:NSForegroundColorAttributeName value:[NSColor redColor] range:NSMakeRange(i, l)];
            i++;
        }
    }
}

So there you go, a fully working example. There were a few details that had me tripped up for ~10 minutes, like the fact that you have to take the string out of textStorage to access the individual characters... maybe this save other people a few minutes.


You should add your controller as the delegate of the NSTextStorage object of the NSTextView ([textView textStorage]) and then implement the delegate method ‑textStorageDidProcessEditing:. This is called whenever the text changes.

In the delegate method you need to get the current NSTextStorage object from the text view using the -textStorage method of NSTextView. NSTextStorage is a subclass of NSAttributedString and contains the attributed contents of the view.

Your code must then parse the string and apply coloring to whatever ranges of text are interesting to you. You apply color to a range using something like this, which will apply a yellow color to the whole string:

//get the range of the entire run of text
NSRange area = NSMakeRange(0, [textStorage length]);

//remove existing coloring
[textStorage removeAttribute:NSForegroundColorAttributeName range:area];

//add new coloring
[textStorage addAttribute:NSForegroundColorAttributeName 
                    value:[NSColor yellowColor] 
                    range:area];

How you parse the text is up to you. NSScanner is a useful class to use when parsing text.

Note that this method is by no means the most efficient way of handling syntax coloring. If the documents you are editing are very large you will most likely want to consider offloading the parsing to a separate thread and/or being clever about which sections of text are reparsed.


I recommend you to start by reading the CocoaDev page about Syntax Highlighing. A lot of people have come with solutions for various goals.

If you want to perform source code syntax highlighting, I suggest you to take a look at the UKSyntaxColoredTextDocument from Uli Kusterer.