The Hit List Diary #18 – Syntax Highlighting

With iOS 7, Apple introduced Text Kit, giving us a set of classes to handle rich text editing.

One fairly classic use case for this functionality is to achieve syntax highlighting in a text editor. Apple showed this off in their introduction to Text Kit, objc.io did the same in Getting to Know TextKit, and I’m sure there’s plenty of other tutorials out there.

One thing they have in common: subclassing NSTextStorage to achieve the highlighting.

I can’t help wondering if this is actually needed. It seems quite a bit of trouble to go to. NSTextStorage can take a delegate, with this handy method:

- (void)textStorage:(NSTextStorage *)textStorage
 willProcessEditing:(NSTextStorageEditActions)editedMask
                        range:(NSRange)editedRange
     changeInLength:(NSInteger)delta

This seems much easier to me, at least to begin with. Simply implement the above method and take it as a cue to apply syntax highlighting attributes to the text storage. If you’re feeling fancy, adjust only the text ranges which have changed.

Am I missing something? Is this a terrible, flawed idea? Let me know using that handy link/button over in the sidebar.



Initially, I implemented syntax highlighting using the companion delegate method:

- (void)textStorage:(NSTextStorage *)textStorage
  didProcessEditing:(NSTextStorageEditActions)editedMask
                        range:(NSRange)editedRange
    changeInLength:(NSInteger)delta

Turns out this one probably is a bad idea. At first, it will seem to work fine, but then likely at some point you’ll discover the rather nasty flaw:

Let’s say you’re using Helvetica in this text view. Characters are all rendering fine. But then along comes someone with some Korean text, say. Helvetica doesn’t include glyphs for such characters. So what the text system does is substitute Helvetica for a different font (AppleSDGothicNeo at time of writing).

This substitution takes place as part of processing edits. By the time the above method is called, substitution has already taken place. If your code naively tries to use Helvetica as part of its formatting, and applies that to the Korean characters, they’ll be back in an unsupported font, and simply won’t be rendered onscreen.

The solution is to perform the work in …willProcessEditing:… instead. Then the text system will clean up font attributes after you as needed.

© Mike Abdullah 2007-2015