NSTextView Performance May Degrade for Large Plain Text Documents When usesFontPanel Is Active

I was profiling performance bottlenecks in the The Archive and noticed that no matter how much highlighting functionality I removed/commented-out, the apparent slowness was all due to … Touch Bar API?!

1.89 s   98.0%	0 s	   -[NSTextView(NSSharing) setSelectedRanges:affinity:stillSelecting:]
1.00 s   51.8%	0 s	    -[NSTextView updateFontPanel]
1.00 s   51.8%	0 s	     -[NSTextView(NSTextView_TouchBar_API) updateTextTouchBarItems]
1.00 s   51.8%	0 s	      -[NSTextTouchBarItemController setSelectedAttributesWithEnumrator:]

I have been “pruning” the Touch Bar related calls from the profiling stack to focus on what I though would be the real bottlenecks. But, as often, it turns out this was stupid and the instruments did point out the true problem. Something indeed was causing trouble here, it turned out.

Luckily, all my internal libraries/frameworks have little example apps included, and the innermost Markdown highlighting library is no different. The same file that was causing problems in The Archive was working fine there all the time, and that was my main motivator to comment-out stuff I added in modules that used the highlighter and focus on that. But in the end all that was left was a difference in the setup of Text Kit components, and there I found the real culprit. It is this setting that was activated for The Archive but not for the much faster sample app:

textView.usesFontPanel = true

That tells NSTextView to respond to font changes in the system standard font panel (often bound to ⌘T or some such in text editors).

Turning this off made the time-intensive Touch Bar API calls go away immediately. The call stack would’ve told me as much if only I hadn’t dismissed the information it was providing.

I’m not quite sure why this makes both clicking around in the text (i.e. changing the selected range) and typing so slow, yet.

I do know that some rich text editors (including TextEdit) show rich text control buttons in the Touch Bar, like buttons to control bold/italic/underline; and these would need to update as you type or move the insertion point. But these buttons aren’t even visible in a plain text NSTextView. The only text view related touch bar button is the Emoji picker. So I’m not sure why there’s this slow-down when the Touch Bar wouldn’t even need to update. All I can share at the moment is that disabling usesFontPanel outright eliminated this performance problem.

By the way, this problem was reported by a user running macOS High Sierra, so it’s not just a Big Sur bug. He also has no Touch Bar, so it might be possible that the performance bottleneck surfaces even if my dev machine was a Touch Bar-less Mac Mini or something. Not sure, though. Imagine how annoying it’d be to find this performance problem if I didn’t have a MacBook Pro with a Touch Bar, and you’d need a Touch Bar to make the problem surface. That’d be no fun at all.

Why did I use font panel support in a plain text Markdown app at all? To let users modify the app’s default font settings from the font panel, if they so desire, without going to the app preferences. Seems we can’t have nice things, though, so that might have to go away in the next update.

I’ll keep an eye open and investigate. But this is such a stupid thing that I wanted to share it as quickly as possible.

So if your syntax highlighting is slow for large documents, and your Time Profiling instrument points out something related to the Touch Bar API that somewhere in the call stack mentiones “font panel”, try usesFontPanel = false.