Skip to content

Advanced Text Editing Using Karabiner & macOS KeyBindings

Categories: Productivity

Table of Contents

I’ve always wanted some of the fancy keybindings I have in VS Code across my entire macOS experience. Additionally, ever since I discovered back/forward for code navigation in VS Code I wanted to bind my mouse keys to these shortcuts.

I ended up digging into Karabiner and the native macOS keybindings. Here are my notes! Most of the resulting code is here.

macOS Keybindings

Here are some notes about what I learned about this hidden macOS feature:

  • There are a set of special commands that control the native cocoa text system. You can combine these commands and tie them to keyboard shortcuts, but they only work in apps that use the native cocoa text system (not Chrome, for example!).
  • In Karabiner there is not a application_windows command, but there is a mission_control. Holding cntrl while executing mission_control (which I have bound to my middle button) will execute "application windows" and holding cmd executes show desktop.
  • All of the standard macOS keybindings are located /System/Library/Frameworks/AppKit.framework/Resources/StandardKeyBinding.dict. However, this file is not a plain text dict. You can read it using plist buddy plistbuddy -c "print" /System/Library/Frameworks/AppKit.framework/Resources/StandardKeyBinding.dict
    • There was some strange output encoding issues that caused many of the bindings to not display properly.
  • Any user defined keybindings override system-level keybindings.
  • You don’t need to add the shift modifier ($) to a keybinding if shift is modifying a letter that can be uppercased.
  • It looks like macOS has a emacs-like kill-ring that is managed separately from the system clipboard.

Resources

Karabiner Key Modifications

  • You can use karabiner to convert any mouse/keyboard input into other mouse/keyboard input. The benefit of this approach is this works across any applications, and you apply filters (only activate when a specific application is active, etc) to keyboard shortcuts.
  • If you are adding a new rule entry to a complex modification you already have, you’ll need to explicitly add it in the karabiner complex modifications UI.
  • Bundle identifiers can be specified as a regex.

Resources

Delete Line Using Karabiner

The delete line command in VS Code is so convenient. For a while, I’ve wanted to bring a similar command to all of macOS. Here’s the stackoverflow post that pointed me in the right direction.

However, this was slightly glitchy in some applications (like gdocs).

{
  "title": "cmd+shift+k delete line",
  "rules": [
    {
      "description": "cmd+shift+k delete line",
      "manipulators": [
        {
          "type": "basic",
          "from": {
            "modifiers": { "mandatory": ["left_command", "left_shift"] },
            "key_code": "k"
          },
          "to": [
            { "repeat": false, "key_code": "a", "modifiers": ["left_control"] },
            { "repeat": false, "key_code": "k", "modifiers": ["left_control"] },
            { "repeat": false, "key_code": "delete_or_backspace" }
          ]
        }
      ]
    }
}

Duplicate Line Using Karabiner

{
  "title": "duplicate line",
  "rules": [
    {
      "description": "opt+shift+down duplicate line",
      "manipulators": [
        {
          "type": "basic",
          "conditions": [
            {
              "bundle_identifiers": [
                "com\.microsoft\.VSCode",
                "com\.microsoft\.VSCodeInsiders"
              ],
              "type": "frontmost_application_unless"
            }
          ],
          "from": {
            "modifiers": { "mandatory": ["left_option", "left_shift"] },
            "key_code": "down_arrow"
          },
          "to": [
            { "repeat": false, "key_code": "right_arrow", "modifiers": ["left_command"] },
            { "repeat": false, "key_code": "up_arrow", "modifiers": ["left_shift", "left_option"] },
            { "repeat": false, "key_code": "c", "modifiers": ["left_command"] },
            { "repeat": false, "key_code": "right_arrow" },
            { "repeat": false, "key_code": "return_or_enter" },
            { "repeat": false, "key_code": "v", "modifiers": ["left_command"] }
          ]
        }
      ]
    }
  ]
}

Back & Forward Code Navigation in VS Code Using Karabiner

I have a logitech MX vertical mouse (which I’ve enjoyed). I wanted to conditionally map the back/forward buttons to the VS code keyboard shortcut for back/forward code navigation.

{
  "title": "Back/Forward in Visual Studio Code",
  "rules": [
    {
      "description": "Change mouse button 4/5 to navigate back/forward in VSCode",
      "manipulators": [
        {
          "type": "basic",
          "conditions": [
            {
              "bundle_identifiers": [
                "^com\.microsoft\.VSCode"
              ],
              "type": "frontmost_application_if"
            }
          ],
          "from": {
            "pointing_button": "button4"
          },
          "to": [
            {
              "key_code": "hyphen",
              "modifiers": [
                "left_control"
              ]
            }
          ]
        },
        {
          "conditions": [
            {
              "bundle_identifiers": [
                "^com\.microsoft\.VSCode"
              ],
              "type": "frontmost_application_if"
            }
          ],
          "from": {
            "pointing_button": "button5"
          },
          "to": [
            {
              "key_code": "hyphen",
              "modifiers": [
                "left_control",
                "left_shift"
              ]
            }
          ],
          "type": "basic"
        }
      ]
    },
    {
      "description": "When VS Code is not active, use standard browser back/forward commands",
      "manipulators": [
        {
          "conditions": [
            {
              "bundle_identifiers": [
                "^com\.microsoft\.VSCode"
              ],
              "type": "frontmost_application_unless"
            }
          ],
          "from": {
            "pointing_button": "button4"
          },
          "to": [
            {
              "key_code": "open_bracket",
              "modifiers": [
                "left_command"
              ]
            }
          ],
          "type": "basic"
        },
        {
          "conditions": [
            {
              "bundle_identifiers": [
                "^com\.microsoft\.VSCode"
              ],
              "type": "frontmost_application_unless"
            }
          ],
          "from": {
            "pointing_button": "button5"
          },
          "to": [
            {
              "key_code": "close_bracket",
              "modifiers": [
                "left_command"
              ]
            }
          ],
          "type": "basic"
        }
      ]
    }
  ]
}

Duplicate Line, Move Line, Delete Line using macOS KeyBindings

The Karabiner versions work well enough, but they are a bit glitchy in some applications. An alternative is implementing these functions in macOS keybindings. The downside is they do not work on applications that do not use the native text input and are glitchy in applications that overload the native text input in strange ways.

{
  // duplicate paragraph, opt + shift + down
  "~$UF701" = (setMark:, moveToBeginningOfParagraph:, moveToEndOfParagraphAndModifySelection:, copy:, swapWithMark:, moveToEndOfParagraph:,moveRight:,insertNewline:,moveLeft:, paste:);

  // delete line/paragraph, cmd + shift + k
  "@K" = (selectParagraph:, delete:, moveToBeginningOfParagraph:);

  // Move line up
  "~UF700" = (selectParagraph:, setMark:, deleteToMark:, moveLeft:, moveToBeginningOfParagraph:, yank:, moveLeft:, selectToMark:, moveLeft:);

  // Move line down
  "~UF701" = (selectParagraph:, setMark:, deleteToMark:, moveToEndOfParagraph:, moveRight:, setMark:, yank:, moveLeft:, selectToMark:);
}

Open Questions

  • On my Logitech mouse, there is a fourth button (not conveniently located) that doesn’t show up in the Karabiner event viewer. I wonder if there’s any way to map this via karabiner?
  • How exactly how the emacs kill ring work in macOS?
  • Can zsh/tmux be customized to use similar shortcuts?