OS X Text System Keybindings

Introduction

The OS X text system includes a user-customizable mechanism for binding keystrokes to actions. Some bindings are built into the system by default, many of which might be unfamiliar to you. More importantly, you can easily add your own bindings. This post aims to provide comprehensive documentation of the keybinding system. It brings together material extracted from widely scattered and generally unknown sources.

First, let me say why I think this is appropriate for posting on this forum:

  • I believe that anything to do with keybindings is fair game.
  • People (including myself) may try to implement some of these as macros which are more directly implemented as system keybindings and
  • KM implementations may use keystrokes that behave inconsistently among applications, where the text system commands are more consistent.

Background

An introduction to text system keybindings is found at Text System Defaults and Key Bindings. Commands fall into the following categories listed in that file, under the heading “Standard Action Methods for Selecting and Editing.”

  • Selection movement and expansion
  • Text insertion
  • General deletion of elements
  • Modifying selected text
  • Scrolling a document

Most of the system’s commands have ancient origins. Many are from Emacs. Some are familiar to users of Unix shells. Others were introduced as part of the NeXTSTEP user-interface environment. I don’t actually know how many, if any, of these commands arose after NeSTSTEP found new life as OS X.

To add to or override command bindings for the Apple text system edit, creating if necessary, the file

~/Library/Keybindings/DefaultKeyBinding.dict

(I believe this file must actually be in ~/Library/Keybindings — it can’t be a symbolic link.) This file can be in several formats, but the easiest to use is probably the old property list format. Examples are shown later in this document.

(Relatively) Well-Known Pre-Defined Bindings

Predefined keystrokes other than arrow/page/home/end keys that I believe are — or should be — used most frequently are:

⌃f   forward character
⌃b   backward character
⌃n   next line
⌃p   previous line
⌃v   next page
     (jumps immediately as opposed to the scrolling effect with Page Down)

⌃a   beginning of current line
     (or sometimes what looks like a paragraph but is actually a "line")
⌃e   end of current ine
     (or sometimes what looks like a paragraph but is actually a "line")

⌃d   delete character forward (also ⌦)
⌃h   delete character backward (also ⌫)

⌃o   open line (in effect, ↩←)

The following two commands work with their own clipboard, not the system clipboard. That is something to remember, but note that it also gives you the flexibility of using a sort-of local clipboard for Q&D cut and paste while leaving the system clipboard alone.

⌃k   delete to end of line  (or paragraph, depending)
⌃y   paste the text cut by the last sequence of ⌃k keys

⌃k does not delete the end of line (or paragraph) character — repeating ⌃k keys does that. Repeated ⌃k presses append to the special clipboard.

Defining Keybindings

The general format of the file is:

{
    // This is a one-line comment
    /* This is a multi-line comment
       (You can also use this for one-line comments)
    */

    // single key, single command
    "keycode" = "command";
    "keycode" = "command";

    // single key, multiple commands
    "keycode" = ("command1", "command2", "command3";

    // multiple key, single commands (nested dictionary)
    "keycode0" = {"keycode1" = "command1";
                  "keycode2" = "command2";
                   };
 . . .
}

Single-word command names do not need to be enclosed in quotes. Note that command names end in a colon — they are the names of Objective-C methods in the NSResponder class. See Responding to Action Messages for documentation of available commands. (There is also the command noop: which can be used to “unbind” a command bound in the system’s default bindings.)

Modifiers for keycodes are represented as follows:

    ^   Control
    ~   Option
    $   Shift
    @   Command
    #   numeric keypad

Also, instead of $c you can use C (i.e., the uppercase version of the character). Special keys have enum names such as NSF7FunctionKey and NSHomeFunctionKey.

Keys can be represented as regular characters if possible, in octal (\ooo) if possible, or in unicode (\Uxxxx) Key codes for special keys are defined in [NSEvent.h](file:///Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk/System/Library/Frameworks/AppKit.framework/Versions/C/Headers/NSEvent.h), in the enum at the end of the file. Here is a summmary:

Character        Code

Up Arrow         \UF700
Down Arrow       \UF701
Left Arrow       \UF702
Right Arrow      \UF703

F1               \UF704
F2               \UF705
F3               \UF706
...
F12              \UF70F
...
F35              \UF726

Insert           \UF727
Delete           \UF728   (\177)
Home             \UF729
End              \UF72B
Page Up          \UF72C
Page Down        \UF72D

Print Screen     \UF72E
Scroll Lock      \UF72F
Pause            \UF730
SysReq           \UF731
Break            \UF732

Menu             \UF735
Help:            \UF746

You may be worried that binding an option character denies you the ability to type the corresponding Mac keyboard special character. To get that character for a bound option keystroke simply precede the keystroke with ⌃q.

Working with Keybinding Files

The built-in system keybinding file is in binary XML format. You can develop your own ~/Library/Keybindings/DefaultKeyBinding.dict by:

  • starting with a blank file
  • starting with the example file at the end of this document
  • by editing the system file with an application that understands that format (e.g., with PlistEdit Pro)
  • or converting the system keybinding file from binary XML to an old-style property list.

For the latter, the following formulation I found will output the contents of StandardKeyBinding.dict in the old property list format. Pipe this to ~/Library/Keybindings/DefaultKeyBinding.dict then edit it as you wish. Leave out the grep step if you want to include all keycodes including the ones bound to noop:. (I have no idea how to read the ruby command, but it looks like it is doing some kind of substitution(s).)

plutil -convert xml1 \
/System/Library/Frameworks/AppKit.framework/Resources/StandardKeyBinding.dict -o - | \
pl | grep -v noop: | ruby -pe'$_.gsub!(/[^ -~\n]/){"\\U%04x"%$&.ord}'

A disadvantage of starting with a copy of the system keybinding file is the overwhelming amount of detail the file will contain. However, it has advantages in that you get to see:

  • all the bound commands, learning about ones you didn’t know about
  • all the keybindings so you won’t accidentally override one unless you meant to
  • interesting examples of various ways of binding commands
  • the representation of non-ASCII keys

To activate changes in the keybinding file it is not necessary to log out. Changes will not be seen by an open application, but they will be seen by the applications the next time it is opened.

Sample DefaultKeyBinding.dict

{
    // Emacs bindings
    "^l"        = "centerSelectionInVisibleArea:";      // Recenter

    // Two alternate bindings of Undo
    "^/"        = "undo:";
    "^_"        = "undo:";

    "~f"        = "moveWordForward:";
    "~b"        = "moveWordBackward:";
    "~<"        = "moveToBeginningOfDocument:";
    "~>"        = "moveToEndOfDocument:";
    "~v"        = "pageUp:"; // ^_ is already pageDown; in system bindings
    "~d"        = "deleteWordForward:";

    // transposeWords: does not work in all (any?) applications
    // ⌃t is bound to transpose: (characters) in the system keybindings
    "~t"        = "transposeWords:";

    // Three typical bindings for deleeWordBackward:
    "~^h"       = "deleteWordBackward:";
    "~\010"     = "deleteWordBackward:";                // Option-backspace
    "~\177"     = "deleteWordBackward:";                // Option-delete  (\U007F)

    // Three examples of multi-command bindings
    "~c"        = ( "capitalizeWord:",                  // Capitalize
                    "moveForward:",
                    "moveForward:");
    "~u"        = ( "uppercaseWord:",                   // Uppercase
                    "moveForward:",
                    "moveForward:");
    "~l"        = ( "lowercaseWord:",                   // Lowercase
                    "moveForward:",
                    "moveForward:");


    // Emacs-style mark commands
    "^ "        = "setMark:";
    "^\@"       = "setMark:";
    "^w"        = "deleteToMark:";

    //  "Mark" is from point to end of word/paragraph, not entire word/paragraph
    "~\@"       = ( "setMark:",                         // Mark Word
                    "moveWordForward:",
                    "swapWithMark:");
    "~h"        = ( "setMark:",                         // Mark paragraph
                    "moveToEndOfParagraph:",
                    "swapWithMark:");

    //  An example of multi-key bindings -- the ⌃x is a prefix for the other keys
    "^x"        = { "^x" = swapWithMark:;
                    "^m" = selectToMark:;
                   };
}

Here are some old keybinding files I have found around the web, from which some of this document’s informations and examples were taken.

9 Likes