NSTextView Bypasses performKeyEquivalent Check for Backspace and Option-Backspace

Here’s an AppKit quirk I found out this week.

Usually, the NSStandardKeyBindingResponding protocol declares standard text movement and editing functions like selectWord or moveToBeginningOfParagraph or deleteWordForward. While text view subclasses can just override these to modify the behavior, you can get the same key event handling from any NSResponder: call interpretKeyEvents(_:) in its keyDown implementation, and you’re set.

There’s a dispatcher function, NSStandardKeyBindingResponding.doCommand(by:), that is called for all the methods from the protocol. It’s a bottleneck that you can also hook into to pre- or post-process handling these events.

Before you get there, though, the window needs to forward the event to your view, first. Check out “The Path of Key Events” in Event Architecture, Cocoa Event Handling Guide from the legacy docs, and you’ll see a diagram stating that key equivalents are processed by performKeyEquivalent(_:), where a view can state that it wants to do something in response to the event. This is the ‘raw’ key event, way before it’s being processed into e.g. NSStandardKeyBindingResponding selectors.

In my observation, (Backspace) and + (Option+Backspace) bypass this in NSTextViews.

When you hit any of these ‘key equivalents’, you can observe these functions being called in sequence:

  1. doCommand(by:)
  2. deleteBackward(_:) and deleteWordBackward(_:), respectively.

It’s different for + (Command+Backspace), so Backspace being involved is not the problem. Like most key equivalents, it’s processed like this:

  1. performKeyEquivalent(_:)
  2. doCommand(by:)
  3. deleteToBeginningOfLine(_:)

I tried a couple of other NSStandardKeyBindingResponding shortcuts and all of them are funneled through performKeyEquivalent(_:). I haven’t tested all of the methods from this protocol, though.

I have no clue why deleting a character and word with backspace behave this way, but now you know in case you want to bind a menu item to or + and run into funny behavior.