Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@hym3242
Last active April 21, 2024 22:32
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hym3242/8e8bc10a2fed8e55973c500f1798c234 to your computer and use it in GitHub Desktop.
Save hym3242/8e8bc10a2fed8e55973c500f1798c234 to your computer and use it in GitHub Desktop.
Dump of /System/Library/Frameworks/AppKit.framework/Resources/StandardKeyBinding.dict on macOS Ventura 13.4, plus some notes
$ # plz forgive this dumb method of visualization.
$ cp /System/Library/Frameworks/AppKit.framework/Resources/StandardKeyBinding.dict .
$ plutil -convert xml1 StandardKeyBinding.dict
$ plutil -p StandardKeyBinding.dict | unicode-vis | cat -v | tr '\t' '+'
{
"^C" => "insertNewline:"
"^H" => "deleteBackward:"
"^Y" => "insertBacktab:" //shift+tab
"^[" => "cancelOperation:"
"^?" => "deleteBackward:"
"+" => "insertTab:"
"
" => "insertNewline:"
"^M" => "insertNewline:"
"@" => "noop:"
"@^?" => "deleteToBeginningOfLine:"
"@ " => "cycleToNextInputScript:"
"@." => "cancelOperation:"
"@^ " => "togglePlatformInputSystem:"
"@^U+F701" => "makeBaseWritingDirectionNatural:"
"@^U+F702" => "makeBaseWritingDirectionRightToLeft:"
"@^U+F703" => "makeBaseWritingDirectionLeftToRight:"
"@~ " => "cycleToNextInputKeyboardLayout:"
"@~^U+F701" => "makeTextWritingDirectionNatural:"
"@~^U+F702" => "makeTextWritingDirectionRightToLeft:"
"@~^U+F703" => "makeTextWritingDirectionLeftToRight:"
"@$U+F700" => "moveToBeginningOfDocumentAndModifySelection:"
"@$U+F701" => "moveToEndOfDocumentAndModifySelection:"
"@$U+F702" => "moveToLeftEndOfLineAndModifySelection:"
"@$U+F703" => "moveToRightEndOfLineAndModifySelection:"
"@U+F700" => "moveToBeginningOfDocument:"
"@U+F701" => "moveToEndOfDocument:"
"@U+F702" => "moveToLeftEndOfLine:"
"@U+F703" => "moveToRightEndOfLine:"
"^" => "noop:"
"^^C" => "insertLineBreak:"
"^^Y" => "selectPreviousKeyView:" //ctrl+shift+tab (remember that tab is ^I(0x09) and shift got its name because it shifts one bit?) (i forgot the source sorry! maybe checkout "things every hacker once knew")
"^^?" => "deleteBackwardByDecomposingPreviousCharacter:"
"^+" => "selectNextKeyView:"
"^
" => "insertLineBreak:"
"^^M" => "insertLineBreak:"
"^'" => "insertSingleQuoteIgnoringSubstitution:"
"^"" => "insertDoubleQuoteIgnoringSubstitution:"
"^/" => "insertRightToLeftSlash:"
"^$U+F702" => "moveToLeftEndOfLineAndModifySelection:"
"^$U+F703" => "moveToRightEndOfLineAndModifySelection:"
"^a" => "moveToBeginningOfParagraph:"
"^A" => "moveToBeginningOfParagraphAndModifySelection:"
"^b" => "moveBackward:"
"^B" => "moveBackwardAndModifySelection:"
"^d" => "deleteForward:"
"^e" => "moveToEndOfParagraph:"
"^E" => "moveToEndOfParagraphAndModifySelection:"
"^f" => "moveForward:"
"^F" => "moveForwardAndModifySelection:"
"^h" => "deleteBackward:"
"^k" => "deleteToEndOfParagraph:"
"^l" => "centerSelectionInVisibleArea:"
"^n" => "moveDown:"
"^N" => "moveDownAndModifySelection:"
"^o" => [
0 => "insertNewlineIgnoringFieldEditor:"
1 => "moveBackward:"
]
"^p" => "moveUp:"
"^P" => "moveUpAndModifySelection:"
"^t" => "transpose:"
"^v" => "pageDown:"
"^V" => "pageDownAndModifySelection:"
"^y" => "yank:"
"^U+F700" => "scrollPageUp:"
"^U+F701" => "scrollPageDown:"
"^U+F702" => "moveToLeftEndOfLine:"
"^U+F703" => "moveToRightEndOfLine:"
"~^C" => "insertNewlineIgnoringFieldEditor:"
"~^H" => "deleteWordBackward:"
"~^[" => "complete:"
"~^?" => "deleteWordBackward:"
"~+" => "insertTabIgnoringFieldEditor:"
"~
" => "insertNewlineIgnoringFieldEditor:"
"~^M" => "insertNewlineIgnoringFieldEditor:"
"~^^?" => "deleteWordBackward:"
"~^b" => "moveWordBackward:"
"~^B" => "moveWordBackwardAndModifySelection:"
"~^f" => "moveWordForward:"
"~^F" => "moveWordForwardAndModifySelection:"
"~$U+F700" => "moveParagraphBackwardAndModifySelection:"
"~$U+F701" => "moveParagraphForwardAndModifySelection:"
"~$U+F702" => "moveWordLeftAndModifySelection:"
"~$U+F703" => "moveWordRightAndModifySelection:"
"~U+F700" => [
0 => "moveBackward:"
1 => "moveToBeginningOfParagraph:"
]
"~U+F701" => [
0 => "moveForward:"
1 => "moveToEndOfParagraph:"
]
"~U+F702" => "moveWordLeft:"
"~U+F703" => "moveWordRight:"
"~U+F704" => "noop:"
"~U+F705" => "noop:"
"~U+F706" => "noop:"
"~U+F707" => "noop:"
"~U+F708" => "noop:"
"~U+F709" => "noop:"
"~U+F70A" => "noop:"
"~U+F70B" => "noop:"
"~U+F70C" => "noop:"
"~U+F70D" => "noop:"
"~U+F70E" => "noop:"
"~U+F70F" => "noop:"
"~U+F710" => "noop:"
"~U+F711" => "noop:"
"~U+F712" => "noop:"
"~U+F713" => "noop:"
"~U+F714" => "noop:"
"~U+F715" => "noop:"
"~U+F716" => "noop:"
"~U+F717" => "noop:"
"~U+F718" => "noop:"
"~U+F719" => "noop:"
"~U+F71A" => "noop:"
"~U+F71B" => "noop:"
"~U+F71C" => "noop:"
"~U+F71D" => "noop:"
"~U+F71E" => "noop:"
"~U+F71F" => "noop:"
"~U+F720" => "noop:"
"~U+F721" => "noop:"
"~U+F722" => "noop:"
"~U+F723" => "noop:"
"~U+F724" => "noop:"
"~U+F725" => "noop:"
"~U+F726" => "noop:"
"~U+F727" => "noop:"
"~U+F728" => "deleteWordForward:"
"~U+F729" => "noop:"
"~U+F72A" => "noop:"
"~U+F72B" => "noop:"
"~U+F72C" => "pageUp:"
"~U+F72D" => "pageDown:"
"~U+F72E" => "noop:"
"~U+F72F" => "noop:"
"~U+F730" => "noop:"
"~U+F731" => "noop:"
"~U+F732" => "noop:"
"~U+F733" => "noop:"
"~U+F734" => "noop:"
"~U+F735" => "noop:"
"~U+F736" => "noop:"
"~U+F737" => "noop:"
"~U+F738" => "noop:"
"~U+F739" => "noop:"
"~U+F73A" => "noop:"
"~U+F73B" => "noop:"
"~U+F73C" => "noop:"
"~U+F73D" => "noop:"
"~U+F73E" => "noop:"
"~U+F73F" => "noop:"
"~U+F740" => "noop:"
"~U+F741" => "noop:"
"~U+F742" => "noop:"
"~U+F743" => "noop:"
"~U+F744" => "noop:"
"~U+F745" => "noop:"
"~U+F746" => "noop:"
"~U+F747" => "noop:"
"$U+F700" => "moveUpAndModifySelection:"
"$U+F701" => "moveDownAndModifySelection:"
"$U+F702" => "moveLeftAndModifySelection:"
"$U+F703" => "moveRightAndModifySelection:"
"$U+F729" => "moveToBeginningOfDocumentAndModifySelection:"
"$U+F72B" => "moveToEndOfDocumentAndModifySelection:"
"$U+F72C" => "pageUpAndModifySelection:"
"$U+F72D" => "pageDownAndModifySelection:"
"U+F700" => "moveUp:"
"U+F701" => "moveDown:"
"U+F702" => "moveLeft:"
"U+F703" => "moveRight:"
"U+F704" => "noop:"
"U+F705" => "noop:"
"U+F706" => "noop:"
"U+F707" => "noop:"
"U+F708" => "complete:"
"U+F709" => "noop:"
"U+F70A" => "noop:"
"U+F70B" => "noop:"
"U+F70C" => "noop:"
"U+F70D" => "noop:"
"U+F70E" => "noop:"
"U+F70F" => "noop:"
"U+F710" => "noop:"
"U+F711" => "noop:"
"U+F712" => "noop:"
"U+F713" => "noop:"
"U+F714" => "noop:"
"U+F715" => "noop:"
"U+F716" => "noop:"
"U+F717" => "noop:"
"U+F718" => "noop:"
"U+F719" => "noop:"
"U+F71A" => "noop:"
"U+F71B" => "noop:"
"U+F71C" => "noop:"
"U+F71D" => "noop:"
"U+F71E" => "noop:"
"U+F71F" => "noop:"
"U+F720" => "noop:"
"U+F721" => "noop:"
"U+F722" => "noop:"
"U+F723" => "noop:"
"U+F724" => "noop:"
"U+F725" => "noop:"
"U+F726" => "noop:"
"U+F727" => "noop:"
"U+F728" => "deleteForward:"
"U+F729" => "scrollToBeginningOfDocument:"
"U+F72A" => "noop:"
"U+F72B" => "scrollToEndOfDocument:"
"U+F72C" => "scrollPageUp:"
"U+F72D" => "scrollPageDown:"
"U+F72E" => "noop:"
"U+F72F" => "noop:"
"U+F730" => "noop:"
"U+F731" => "noop:"
"U+F732" => "noop:"
"U+F733" => "noop:"
"U+F734" => "noop:"
"U+F735" => "noop:"
"U+F736" => "noop:"
"U+F737" => "noop:"
"U+F738" => "noop:"
"U+F739" => "delete:"
"U+F73A" => "noop:"
"U+F73B" => "noop:"
"U+F73C" => "noop:"
"U+F73D" => "noop:"
"U+F73E" => "noop:"
"U+F73F" => "noop:"
"U+F740" => "noop:"
"U+F741" => "noop:"
"U+F742" => "noop:"
"U+F743" => "noop:"
"U+F744" => "noop:"
"U+F745" => "noop:"
"U+F746" => "noop:"
"U+F747" => "noop:"
}
@hym3242
Copy link
Author

hym3242 commented Jan 15, 2024

see

you can even ^q (NSQuotedKeystrokeBinding) (or without ^q, if it does not have a related binding) to do quoted-insert 0x01,0x02...etc (including stuff like 0x1b with ctrl+opt+[ oh my god) with ctrl+opt+KEY directly into any textarea (just like ctrl+v(lnext) then ctrl+KEY in tty canonical). To insert literal newline, use ctrl+opt+j (you know your ascii table) or ctrl+q then return. ^q can also insert a special char with Option after you rebinded it.

fuck close source! if they open sourced how they discovered them, this would not be so little known today! karabiner may not even need to exist!

@hym3242
Copy link
Author

hym3242 commented Jan 18, 2024

you can strings /System/Library/Frameworks/AppKit.framework/Versions/C/AppKit for more(like selectParagraph:, selectWord:, pageUp:!).

or better, symbols(1) it. see https://gist.github.com/hym3242/4ade5dbcabbe547f3d78687897b8ddfe

You know the drill. Disable SIP or do this on another volume. May require extraction from dyld shared cache?

@hym3242
Copy link
Author

hym3242 commented Jan 18, 2024

insertNewline: and insertNewlineIgnoringFieldEditor: do not work in Chrome. It seems the only way to insert newline in Chrome is the big RETURN

ctrl+up/down to scrollpage(scroll,not moving cursor) , and capitalizeWord: in textarea are also not possible in chrome.

selectWord: and selectParagraph: can extend selection when pressed repeatedly in chrome, but not in macos native apps.

deleteWordBackward: etc in chrome pushes them into yank buffer, not in macos native. in native & chrome, use deleteToBeginningOfLine:(default bound to cmd+del), deleteToBeginningOfParagraph:, deleteToEndOfLine:, deleteToEndOfParagraph: to push selection to yank buffer.

@hym3242
Copy link
Author

hym3242 commented Jan 18, 2024

not all methods are bindable of course...

@hym3242
Copy link
Author

hym3242 commented Jan 18, 2024

symbols /Volumes/Macintosh\ HD/System/Library/Frameworks/AppKit.framework/Versions/C/AppKit | grep 'NSKeyBindingCommands'
better than strings(1)

@hym3242
Copy link
Author

hym3242 commented Jan 18, 2024

here is my binding: explore for more yourself.

// ~/Library/KeyBindings/DefaultKeyBinding.dict (this path is hardcoded in AppKit)
{
        "^~h" = "deleteWordBackward:"; //bash style...
        "^~d" = "deleteWordForward:"; //ibid
        "^u" = "deleteToBeginningOfParagraph:"; //unix tty line disc style kill
        "^w" = "deleteWordBackward:"; //unix tty line disc style werase
        "~a" = "selectParagraph:"; //cocoa paragraph ≈ unix line
        "^m" = "insertNewline:";  //RETURN as in "Carriage Return"
        "^j" = "insertNewlineIgnoringFieldEditor:"; //default bound to opt+return, alternate newline, bypasses some editor smart features
        "~v" = "pageUp:"; //you know your emacs.
        "~V" = "pageUpAndModifySelection:";  //shift+option+v
        "~A" = "selectWord:";
        "~c" = "capitalizeWord:";  //uppercaseWord: does  not seem to work
        "~t" = "convertToTraditionalChinese:"; //convert selection
        "~s" = "convertToSimplifiedChinese:"; //ibid.
        "^J" = "insertLineBreak:";  //ctrl+shift+j, linebreak is different from newline. default also bound to ctrl+return and shift+return
        "^~\UF701" = (selectParagraph:, setMark:, deleteToMark:, moveToEndOfParagraph:, moveRight:, setMark:, yank:, moveLeft:, selectToMark:);  //move line down
        "^~\UF700" = (selectParagraph:, setMark:, deleteToMark:, moveLeft:, moveToBeginningOfParagraph:, yank:, moveLeft:, selectToMark:); //somehow bugged , movelineup
        "~m" = (moveToBeginningOfParagraph:,moveWordRight:, moveWordLeft:); // Carriage Return (resembles ^M) to first non-blank char (does not work perfectly in all cases)
        "^~n" = (moveForward:, moveToEndOfParagraph:); // like the default opt+down
        "^~p" = (moveBackward:, moveToBeginningOfParagraph:); // like the default opt+up

// add these to your liking
//      "~h" = "deleteWordBackward:";
//      "~d" = "deleteWordForward:";
//      "~b" = "moveWordBackward:";
//      "~f" = "moveWordForward:";
}

@hym3242
Copy link
Author

hym3242 commented Mar 13, 2024

currently using:

{
	"^~h" = "deleteWordBackward:";
	"^~d" = "deleteWordForward:";
	"^u" = "deleteToBeginningOfParagraph:";
	"^w" = "deleteWordBackward:";
	"~a" = "selectParagraph:";
	"^m" = "insertNewline:";
	"^j" = "insertNewlineIgnoringFieldEditor:";
	"~v" = "pageUp:"; //inserts symbol if already at top in chrome
	"~V" = "pageUpAndModifySelection:";
	"~A" = "selectWord:";
	"~c" = (capitalizeWord:, moveWordForward:, moveWordBackward:);
	"~t" = "convertToTraditionalChinese:"; //a little unreliable
	"~s" = "convertToSimplifiedChinese:"; //a little unreliable
	"~h" = "deleteWordBackward:";
	"~d" = "deleteWordForward:";
	"~b" = "moveWordBackward:";
	"~B" = "moveWordBackwardAndModifySelection:";
	"~f" = "moveWordForward:";
	"~F" = "moveWordForwardAndModifySelection:";
	"^J" = "insertLineBreak:";
	"^~\UF701" = (selectParagraph:, setMark:, deleteToMark:, moveToEndOfParagraph:, moveRight:, setMark:, yank:, moveLeft:, selectToMark:);
	"^~\UF700" = (selectParagraph:, setMark:, deleteToMark:, moveLeft:, moveToBeginningOfParagraph:, yank:, moveLeft:, selectToMark:); //somehow bugged
	"~m" = (moveToBeginningOfParagraph:,moveWordRight:, moveWordLeft:); //move cursor to first non-whitespace (or non-seperator) character. 
	"^~n" = (moveForward:, moveToEndOfParagraph:);
	"^~m" = (moveToEndOfParagraph:, moveForward:);//, moveToBeginningOfParagraph:);
	"^~N" = "moveParagraphForwardAndModifySelection:";
	"^~p" = (moveBackward:, moveToBeginningOfParagraph:);
	"^~o" = (moveToBeginningOfParagraph:, moveBackward:);
	"^~P" = "moveParagraphBackwardAndModifySelection:";
//	"@/" = (moveToBeginningOfParagraph:, insertText:, "// ", moveToEndOfParagraph:);
	"^U" = "deleteToBeginningOfLine:"; //"uppercaseWord:";
	"^L" = "lowercaseWord:";
	"^~u" = (uppercaseWord:, moveWordForward:, moveWordBackward:); //does not work in chrome
	"^~l" = (lowercaseWord:, moveWordForward:, moveWordBackward:); //does not work in chrome
	"^~a" = "moveToBeginningOfLine:"; 
	"^~e" = "moveToEndOfLine:"; 
	"^~w" = (moveWordForward: ,moveWordForward:,moveWordBackward:); //vi
	"~w" = (moveWordForward: ,moveWordForward:,moveWordBackward:); //vi
	"~U" = "uppercaseWord:"; //does not work in chrome
	//"~l" = "centerSelectionInVisibleArea:"; //does not work in chrome...
	"^K" = "deleteToEndOfLine:";
	"~ " = "setMark:";
	"^x" = "swapWithMark:"; //flaky, unreliable, mark sometimes jump to your current position randomly
	"^X" = "selectToMark:"; //does not work in chrome
	"^H" = "deleteBackwardByDecomposingPreviousCharacter:";
	//"~e" = (moveWordBackward: ,moveWordBackward:,moveWordForward:); //does not work. see below.
	//^~j is literal LF. works the same as "insertNewlineIgnoringFieldEditor:". in wikipedia codemirror this is the only way to input newline without using return.
	//inserting line breaks does not create new paragraphs. Because paragraphs are seperated by NL, lines are seperated with U+2028 LINE SEPARATOR
	//in chrome yank buffer is page specific(hooray!).
	//only deletetobeginningofline/para, deletetoendofline/para push stuff to the yank buffer
	//opt+v/b/t stop working in SCIM, their keylayout take higher precedence. but ctrl+q with them work as expected! the opt+numbers, cmd+opt+stuff as defined in keylayout work very weirdly.
	//other notes on this text system: literal 0x12 FORM FEED ((ctrl+q) ctrl+opt+l)and 0x11 VERTICAL TAB ((ctrl+q) ctrl+opt+k) can have funny results. CARRIAGE RETURN can sometimes be a paragraph seperator.
}

@hym3242
Copy link
Author

hym3242 commented Mar 13, 2024

don't forget defaults write -g NSRepeatCountBinding -string "^r" for emacs style numeric argument! (forgot the source!)

@hym3242
Copy link
Author

hym3242 commented Mar 13, 2024

see also "Keyboard Viewer" in your input source menu bar icon.

see also Ukelele.

@hym3242
Copy link
Author

hym3242 commented Mar 15, 2024

Don't forget option+esc to complete! (show possible completions of words) superhelpful! (especially for language learners!) unfortunately does not work in chrome. or for other languages.
Screenshot 2024-03-15 at 9 47 19 AM

@hym3242
Copy link
Author

hym3242 commented Mar 21, 2024

The setMark: and swapWithMark: can be very useful, but the mark is a bit unstable, can sometimes unexpectedly change to your current cursor position. In some apps setMark also remembers selection.

@hym3242
Copy link
Author

hym3242 commented Apr 4, 2024

It seems we can know about the defaults write -g NSRepeatCountBinding -string "^r" trick by just disassembling the AppKit...
Hopper Disassembler

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment