テキスト編集のキー操作を自在にコントロールする

その裏で何をしているか?

パソコンで文章を書くという行為は、最も一般的だけど、その裏では高度な技術が動いていたりする。例えば、aというキーを押したら、画面にaと表示されるのが当り前と思ってはいけないのだ。

  • OSは、キーを押した時に発生する電気信号を、キーコードとして受け取るだけである。
  • そのキーコードのよって、画面上の指定された位置に、指定されたフォントで、aという文字が表示されるのは、OSが描き出す幻想である。
  • 最近のフォントは輪郭を数式化された関数で描画する。さらに、アンチエイリアス処理によって、ピクセル間をなめらかな曲線に仕上げる。
  • 人間にとって文字と画像は区別されるモノだが、パソコンにとってはどちらも画像で、CPUにとってはメモリ上に格納された電気信号でしかない。
  • 文字は入力されたらそれでおしまいではなく、絶えず挿入・削除が繰り返され、編集される運命にある。
  • 文字が挿入されたら、その位置から右側にある文字は1文字分右にずれるし、行が折り返していれば、そのズレは次の行、あるいはその段落の最後までの数行に伝播することもある。
  • OSは現在編集中の位置を明示するため、文字カーソルも表示してくれる。
  • その文字カーソルさえ単なる画像なのだが、文字の入力に応じて右側に移動するのは、OSが位置を変えて描き直しているからである。
  • 文字カーソルが点滅するのもすべて、OSが特定の間隔で描いては消すを繰り返しているからである。
  • 上下左右のカーソルキーを押せば、文字カーソルもそれに応じて移動する。
  • shiftキーを組み合わせれば、テキストの背景に色がついて選択中であることが明示される。
  • それらもすべて、OSが位置を変更して、背景色を変更して、描き直しているからである。
  • つまり、画面に表示されていることはすべて、OSが描き出す幻想である。
  • 現実は、キーを押して、それに応じた電気信号が発生する事実しかないのだ。

文章を書くというありふれた行為の中で、OSは上記のような処理*1を黙々とこなしているのである。
このテキスト編集の仕組みは、遥か昔から脈々と進化を続けて今に至る。そこには、達人たちの数多くの創意工夫が込められているはず。

共通の操作性

OSXCocoaアプリケーションであれば、テキストを編集する操作性というのは、ほぼ一貫している。テキストエディットで文章を書く時、Safariのテキストエリアで文章を書く時、Mailで文章を書く時、Xcodeでコードを書く時、いずれの場合もほぼ同じ操作性で、違和感なく、自然な感覚でキー操作できる。

もし、アプリケーションごとにテキスト編集の操作が異なっていたら、おそらく相当使いにくい環境になると思う。当然のように感じているが、共通の操作性が提供されている、ということは素晴らしいことだ。

      • 以下、文中の半角¥は半角\に置き換えて読む必要がある。

OSXの標準キーバインド

  • それではこの共通の操作性はどこから来ているのかというと、まず、OSXの標準を決める設定ファイルがあった。
/System/Library/Frameworks/AppKit.framework/Resources/StandardKeyBinding.dict
  • StandardKeyBinding.dictは、Property List Editor*2で開けるのだが、特殊なキーを表すコードが文字化けしてしまい、区別できない。

  • そこで、デスクトップにText Property List形式で保存し直すと、以下のようなテキストファイルとして確認できた。(= "noop:"の行は除外)
{
	"\U0003" = "insertNewline:";
	"\b" = "deleteBackward:";
	"\t" = "insertTab:";
	"\n" = "insertNewline:";
	"\n" = "insertNewline:";
	"\U0019" = "insertBacktab:";
	"\U001b" = "cancelOperation:";
	"$\Uf700" = "moveUpAndModifySelection:";
	"$\Uf701" = "moveDownAndModifySelection:";
	"$\Uf702" = "moveLeftAndModifySelection:";
	"$\Uf703" = "moveRightAndModifySelection:";
	"$\Uf729" = "moveToBeginningOfDocumentAndModifySelection:";
	"$\Uf72b" = "moveToEndOfDocumentAndModifySelection:";
	"$\Uf72c" = "pageUpAndModifySelection:";
	"$\Uf72d" = "pageDownAndModifySelection:";
	"@ " = "cycleToNextInputScript:";
	"@$\Uf700" = "moveToBeginningOfDocumentAndModifySelection:";
	"@$\Uf701" = "moveToEndOfDocumentAndModifySelection:";
	"@$\Uf702" = "moveToLeftEndOfLineAndModifySelection:";
	"@$\Uf703" = "moveToRightEndOfLineAndModifySelection:";
	"@." = "cancelOperation:";
	"@^ " = "togglePlatformInputSystem:";
	"@^\Uf701" = "makeBaseWritingDirectionNatural:";
	"@^\Uf702" = "makeBaseWritingDirectionRightToLeft:";
	"@^\Uf703" = "makeBaseWritingDirectionLeftToRight:";
	"@~ " = "cycleToNextInputKeyboardLayout:";
	"@~^\Uf701" = "makeTextWritingDirectionNatural:";
	"@~^\Uf702" = "makeTextWritingDirectionRightToLeft:";
	"@~^\Uf703" = "makeTextWritingDirectionLeftToRight:";
	"@^?" = "deleteToBeginningOfLine:";
	"@\Uf700" = "moveToBeginningOfDocument:";
	"@\Uf701" = "moveToEndOfDocument:";
	"@\Uf702" = "moveToLeftEndOfLine:";
	"@\Uf703" = "moveToRightEndOfLine:";
	"^\U0003" = "insertLineBreak:";
	"^\t" = "selectNextKeyView:";
	"^\n" = "insertLineBreak:";
	"^\n" = "insertLineBreak:";
	"^\U0019" = "selectPreviousKeyView:";
	"^\"" = "insertDoubleQuoteIgnoringSubstitution:";
	"^$\Uf702" = "moveToLeftEndOfLineAndModifySelection:";
	"^$\Uf703" = "moveToRightEndOfLineAndModifySelection:";
	"^'" = "insertSingleQuoteIgnoringSubstitution:";
	"^/" = "insertRightToLeftSlash:";
	"^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" = (
		"insertNewlineIgnoringFieldEditor:",
		"moveBackward:",
	);
	"^p" = "moveUp:";
	"^P" = "moveUpAndModifySelection:";
	"^t" = "transpose:";
	"^V" = "pageDownAndModifySelection:";
	"^v" = "pageDown:";
	"^y" = "yank:";
	"^~^?" = "deleteWordBackward:";
	"^^?" = "deleteBackwardByDecomposingPreviousCharacter:";
	"^\Uf700" = "scrollPageUp:";
	"^\Uf701" = "scrollPageDown:";
	"^\Uf702" = "moveToLeftEndOfLine:";
	"^\Uf703" = "moveToRightEndOfLine:";
	"~\U0003" = "insertNewlineIgnoringFieldEditor:";
	"~\b" = "deleteWordBackward:";
	"~\t" = "insertTabIgnoringFieldEditor:";
	"~\n" = "insertNewlineIgnoringFieldEditor:";
	"~\n" = "insertNewlineIgnoringFieldEditor:";
	"~\U001b" = "complete:";
	"~$\Uf700" = "moveParagraphBackwardAndModifySelection:";
	"~$\Uf701" = "moveParagraphForwardAndModifySelection:";
	"~$\Uf702" = "moveWordLeftAndModifySelection:";
	"~$\Uf703" = "moveWordRightAndModifySelection:";
	"~^b" = "moveWordBackward:";
	"~^B" = "moveWordBackwardAndModifySelection:";
	"~^f" = "moveWordForward:";
	"~^F" = "moveWordForwardAndModifySelection:";
	"~^?" = "deleteWordBackward:";
	"~\Uf700" = (
		"moveBackward:",
		"moveToBeginningOfParagraph:",
	);
	"~\Uf701" = (
		"moveForward:",
		"moveToEndOfParagraph:",
	);
	"~\Uf702" = "moveWordLeft:";
	"~\Uf703" = "moveWordRight:";
	"~\Uf728" = "deleteWordForward:";
	"~\Uf72c" = "pageUp:";
	"~\Uf72d" = "pageDown:";
	"^?" = "deleteBackward:";
	"\Uf700" = "moveUp:";
	"\Uf701" = "moveDown:";
	"\Uf702" = "moveLeft:";
	"\Uf703" = "moveRight:";
	"\Uf708" = "complete:";
	"\Uf728" = "deleteForward:";
	"\Uf729" = "scrollToBeginningOfDocument:";
	"\Uf72b" = "scrollToEndOfDocument:";
	"\Uf72c" = "scrollPageUp:";
	"\Uf72d" = "scrollPageDown:";
	"\Uf739" = "delete:";
}

設定ファイルの解読

  • 上記の設定ファイルを一見しただけでは、その意味をなかなか理解できないが...
  • 各種記号やコードに以下の意味があると分かれば、だいぶ理解が進む。
意味 記号
control ^
shift $
option (Alt) ~
command (Apple) @
テンキー #
意味 文字コード (記号) 意味 文字コード (記号)
esc \U001B F1 \UF704
tab \U0009 (\t) F2 \UF705
backtab (shift tab) \U0019 F3 \UF706
delete \U007F(\b) F4 \UF707
forward delete (fn delete) \UF728 F5 \UF708
return \U000D (\n) F6 \UF709
enter \U0003 F7 \UF70A
home \UF729 F8 \UF70B
end \UF72B F9 \UF70C
page up \UF72C F10 \UF70D
page down \UF72D F11 \UF70E
\UF700 F12 \UF70F
\UF701 F13 \UF710
\UF702 F14 \UF711
\UF703 F15 \UF712
例1(基本):
  • StandardKeyBinding.dictの1行目は、以下のようになっている。
"\U0003" = "insertNewline:";
  • コード"\U0003"はenterに対応するので、読み方は以下と同等。
"enter" = "insertNewline:";
  • つまり、enterキーを押すと、insertNewline:という処理が実行されるのだ。(つまり、改行する)
例2(キーコンビネーション):
"@ " = "cycleToNextInputScript:";
  • @はcommandの意。
  • それが半角スペースと組み合わさっているので、command-spaceというお馴染みのショートカットを表現している。
  • OS内部的な処理としては、cycleToNextInputScript:という処理が実行されるのだ。(直前の入力モードに切り替える)
例3(大文字キーの扱い):
"^a" = "moveToBeginningOfParagraph:";
"^A" = "moveToBeginningOfParagraphAndModifySelection:";
  • ^はcontrolの意。
  • それがaと組み合わさっているので、control-aというショートカットを表現している。
    • moveToBeginningOfParagraph:という処理は、文字カーソルを段落の先頭に移動する。
  • さらにAと組み合わさると、通常shift-aでAが入力されので、control-shift-aというショートカットを表現していることになる。
    • moveToBeginningOfParagraphAndModifySelection:という処理は、今のカーソル位置から段落の先頭までを選択状態にする。
例4(複合処理):
"~\Uf700" = (
	"moveBackward:",
	"moveToBeginningOfParagraph:",
);
  • ~はoptionの意。\Uf700は↑の意。つまり、option-↑というショートカットを表現している。
  • それに対応する処理が、今度は()で括られて、その中身の要素がカンマで区切られ、二つある。
  • この場合、moveBackward:とmoveToBeginningOfParagraph:という処理が順に実行されることになる。
    • 文字カーソルを一文字戻る方向に移動して、さらにその段落の先頭に移動する、という処理だ。
    • 実際に操作してみると、文字カーソルが段落の先頭を次々と遡っていく。


OSX標準のキー設定にはないが、以下のように表現することもできるようだ。

例5(末尾の特殊記号):
  • 特殊記号といえども、末尾では文字列そのものと解釈された。
"^$" = "moveBackward:"
  • 上記はcontrol-$である。(control-shiftではない)
例6(キーバインドの連続技):
"^x" = {"u" = "undo:";};
  • control-xに続けて、uでundo:が実行される。(つまり、編集 >> 取り消す が実行される)
  • 上記のように、キー操作の値に、{}で囲ったさらなるキー操作を定義することで、キー操作の連続を表現できる。


以上の例を参考にすれば、OSX標準のテキスト編集キーバインド(ショートカット、あるいはキー操作)をすべて読み解くことができる。理解できない処理は、実際にキー操作を実行してみるのが手っ取り早い。

  • キー操作によっては、機能しないものもあるかもしれない。
  • ショートカットが重複する場合、テキスト編集キーバインドの優先順位は低い。
  • 各アプリケーションが設定しているショートカットや、システム環境設定 >> キーボード >> キーボードショートカット、Quicksilverのショートカットの方が優先された。(気がする)

好みのキーバインドを設定する場所

  • システム標準の上記StandardKeyBinding.dictをルールに従って書き換えれば、自分好みのキーバインドが実現できることは明白だが、そんなことをする必要はない。
  • OSXは、テキスト編集のキーバインドをちゃんとカスタマイズする仕組みを用意してくれていた。
# 全ユーザー共通の設定(ルート直下のLibrary)
/Library/KeyBindings/DefaultKeyBinding.dict

# そのユーザー独自の設定(ホーム直下のLibrary)
~/Library/KeyBindings/DefaultKeyBinding.dict
  • 上記のようにKeyBindingsというフォルダを新規追加して、DefaultKeyBinding.dictというファイルを用意すれば、OSXはそれらのキーバインドを優先してくれるのだ。
  • ところで自分のMacBookには、どこにもKeyBindingsというフォルダは存在しなかった。
  • つまり、これまでOSX標準のキーバインドしか利用していなかった、という証拠である。

この仕組みを知ってしまった今、実に勿体ない話しだ...。(今までかなり損してたよ、トホホ...)

設定可能なアクション

  • OSX標準のテキスト編集キーバインド(ショートカット、あるいはキー操作)には、実に様々な処理が設定されている。
  • ところで、キー操作に対応して設定されるこのinsertNewline:、moveToBeginningOfParagraph:等の処理とは一体何なのか?
  • なんと!その実体は、NSResponderクラスが持つアクションなのである。
  • Respondとは返事をするという意味の動詞で、Responderとはそれにerを付けて擬人化した、返事をする人という造語だと思われる。
    • Speak(話すという意味の動詞)にerを付けたSpeakerが話しをする人、という意味になるのと同様に。
  • NSResponderは、様々なキー入力やマウスクリックといったイベント メッセージに反応する(返事する)仕組みを提供しているのだ。
  • OSXで利用するGUIオブジェクトのほとんどすべてが、NSObject >> NSResponderから継承して作られている。
  • GUIがNSResponderを継承するということは、つまり、NSResponderのアクションが共通のアクションとして必ず存在しているのだ。
  • enterキーを押すと、StandardKeyBinding.dictの設定からinsertNewline:というアクション(メッセージ)がテキストビューに投げられ、
  • 最終的にはNSResponderに設定されるアクション(メソッド)insertNewline:が反応して、その処理が実行されていたのだ。
  • だから、NSResponderのアクションを知ることは、OSX標準で用意されているテキスト操作の基本機能を知ることに繋がる。
  • また、テキストの編集環境は、NSTextViewというオブジェクトが担っている。
  • もちろんNSResponderを継承しているのでNSResponderのアクションに反応するのは当然のこと、
  • さらに、NSTextView自体が実装するアクションにも反応するのだ。
  • 基本的に英語環境で起動した時に表示されるメニューの名称は、そのままアクション名として実装されているはずである。
    • メニューの名称に:を追加する。
    • メニュー末尾の...は取り除く。
  • 一体どんなアクションがあるのだろうか?
alignCenter:
alignJustified:
alignLeft:
alignRight:
breakUndoCoalescing
cancelOperation:
capitalizeWord:
center
centerSelectionInVisibleArea:
changeCaseOfLetter:
checkSpelling:
clearRecentDocuments:
complete:
copy:
copyFont:
copyRuler:
cut:
delete:
deleteBackward:
deleteBackwardByDecomposingPreviousCharacter:
deleteForward:
deleteToBeginningOfLine:
deleteToBeginningOfParagraph:
deleteToEndOfLine:
deleteToEndOfParagraph:
deleteToMark:
deleteWordBackward:
deleteWordForward:
hide:
ignoreSpelling:
indent:
insertBacktab:
insertContainerBreak:
insertLineBreak:
insertNewline:
insertNewlineIgnoringFieldEditor:
insertParagraphSeparator:
insertTab:
insertTabIgnoringFieldEditor:
insertText:
loosenKerning:
lowerBaseline:
lowercaseWord:
moveBackward:
moveBackwardAndModifySelection:
moveDown:
moveDownAndModifySelection:on:
moveForward:cking:
moveForwardAndModifySelection:
moveLeft:
moveLeftAndModifySelection:
moveRight:
moveRightAndModifySelection:
moveToBeginningOfDocument:
moveToBeginningOfDocumentAndModifySelection:
moveToBeginningOfLine:
moveToBeginningOfLineAndModifySelection:
moveToBeginningOfParagraph:
moveToEndOfDocument:
moveToEndOfDocumentAndModifySelection:
moveToEndOfLineAndModifySelection:
moveToEndOfLine:
moveToEndOfParagraph:
moveUp:
moveUpAndModifySelection:
moveWordBackward:
moveWordBackwardAndModifySelection:
moveWordForward:
moveWordForwardAndModifySelection:
moveWordLeft:
moveWordLeftAndModifySelection:
moveWordRight:
moveWordRightAndModifySelection:
newDocument:
openDocument:
orderBack:
orderFront:
orderFrontLinkPanel:
orderFrontListPanel:
orderFrontSpacingPanel:
orderFrontTablePanel:
outline:
pageDown:
pageUp:
paste:
pasteAsPlainText:
pasteAsRichText:
pasteFont:
pasteRuler:
performClose:
performMiniaturize:
performZoom:
printDocument:
raiseBaseline:
redo:
revertDocumentToSaved:
runPageLayout:
saveAllDocuments:
saveDocument:
saveDocumentAs:
saveDocumentTo:
scrollLineDown:
scrollLineUp:
scrollPageDown:
scrollPageUp:
selectAll:
selectLine:
selectParagraph:
selectToMark:
selectWord:
setMark:
showContextHelp:
showGuessPanel:
startSpeaking:
stopSpeaking:
subscript:
superscript:
swapWithMark:
terminate:
tightenKerning:
toggleBaseWritingDirecti
toggleContinuousSpellChe
toggleRuler:
transpose:
transposeWords:
turnOffKerning:
turnOffLigatures:
underline:
undo:
unscript:
uppercaseWord:
useAllLigatures:
useStandardKerning:
useStandardLigatures:
yank:
  • 上記のアクション名一覧*3は、すべてを網羅できていない。
  • また、中には動作しないアクションもあるかもしれない。
  • アクションの意味やさらなる詳細は、以下ページが詳しい。
  • アクション名が:で終わっているのは、各アクションが何らかの引数を取るからであり、アクション名は必ず:まで含める必要がある。(省略できない)

自分仕様のキーバインドを設定する

  • 以上の仕組みを理解して、早速自分が欲しい機能を試してみた。
    • 設定ファイルの保存場所は以下のパスにした。
    • ~/Library/KeyBindings/DefaultKeyBinding.dict
  • 実験にはテキストエディットを利用した。
  • DefaultKeyBinding.dictに保存して、テキストエディットを再起動すると、キー操作に反映された。
設定する時の注意
  • control-shift-o = control-O = "^O"である。("^$o"ではエラーになる)
  • control-shift-1 = control-! = "^!"である。("^$1"ではエラーになる)
  • つまり、shift操作が有効なキーには、shift操作で発生する文字を書いて、shiftマークの$は書かない。
  • だから、shiftマークの$を書くのは、矢印キーの操作ぐらいしかないと思う。
基本
{
  "^l" = "lowercaseWord:";
  "^u" = "uppercaseWord:";
  "^c" = "capitalizeWord:";
}
  • control-l:カーソル右側の単語、あるいは選択範囲のすべての単語を小文字にする。
  • control-u:カーソル右側の単語、あるいは選択範囲のすべての単語を大文字にする。
  • control-c:カーソル右側の単語、あるいは選択範囲のすべての単語の頭文字だけ大文字にする。
  • control-l、control-cはうまく動いたが、control-uだけ反応しない...。
  • たぶん、control-uが他のショートカットと重複しているのだと思う。
  • ちなみに、上記の機能は、編集 >> 変換 >> に続くメニューに、システム環境設定からショートカットを割り当てる方法でも可能だ。
キーバインドの連続技
    "^/" = {
            "l" = "lowercaseWord:";
            "u" = "uppercaseWord:";
            "c" = "capitalizeWord:";
    };
  • もはや、自分のMacBookはショートカットで溢れている。
  • 新たなショートカットを見つけ出すのは結構面倒くさい。
  • テキスト編集キーバインドの良い所は、キー操作を連続技で認識してくれる所だ。
  • 上記のように設定しておけば...
    • control-/に続けてlで、小文字に変換する。
    • control-/に続けてuで、大文字に変換する。
    • control-/に続けてcで、頭文字だけ大文字に変換する。
  • この設定なら3つともちゃんと機能した!
アクションの複合技(基本)
{
    "~\Uf700" = (
	"moveBackward:",
	"moveToBeginningOfParagraph:",
    );
}
  • 上記の設定は、OSXの標準キーバインドの設定にある。
  • option-↑で...
    • moveBackward: ......カーソルを戻る方向に一つ移動する。
    • moveToBeginningOfParagraph: ......カーソルを段落の先頭に移動する。
  • つまり、option-↑を繰り返せば、カーソルが段落の先頭を遡っていくのだ。
アクションの複合技(タイトル化)
{
    "^~u" = ("selectParagraph:",
             "uppercaseWord:",
             "moveToEndOfParagraph:",
             "insertNewline:",
             "insertNewline:");
}
  • control-option-uで...
    • selectParagraph: ......カーソルを含む段落を選択状態にする。
    • uppercaseWord: ......選択範囲を大文字にする。
    • moveToEndOfParagraph: ......カーソルを段落の末尾へ移動する。
    • insertNewline: ......改行を追加する。
  • つまり、入力した行でcontrol-option-uを押すと、その行をタイトルとみなし単語すべてを大文字に変換して、空行を1行入れて、入力待ち状態になるのだ。
アクションの複合技(タグ化)
{
    "^H" = ("setMark:",
            "moveWordBackward:",
            "deleteToMark:",
            "insertText:", "<", "yank:", "insertText:", ">",
            "setMark:",
            "insertText:", "
  • ちょっと複雑になってきたが、基本に忠実にアクションを一つずつ見ていくことにする。
  • control-shift-hで...
    • setMark: ......カーソル位置を記憶する。
    • moveWordBackward: ......カーソルを単語の先頭に移動する。
    • deleteToMark: ......カーソル位置からマークした位置まで削除する。
    • insertText: ......これに続くテキストを挿入する。
    • yank: ......直前に削除したテキストをペーストする。
    • swapWithMark: ......カーソルをマークした位置に移動する。
  • つまり、何をやっているかというと、こんな時は実際に試してみるのが一番いい。
  • divと入力して、control-shift-hを操作してみると、以下のようになった。
<div></div>
  • なんと、入力した単語をタグ化してくれるのだ!
  • ちゃんと閉じるダグまで準備してくれる!
  • おまけに、カーソル位置をタグの真ん中にして!

これは便利!

  • 今まで、こんな処理をするにはAppleScriptAutomatorでコードを書く必要があると思っていた。
  • そして、Quicksilverでショートカットを設定したり、サービスメニューに組み込んだりする必要があると思っていた。
  • ところが、キーバインドの設定なら、このような単純なアクションの組み合わせで実現できてしまうのだ!
  • さらに、キー操作にも瞬時に反応し、AppleScriptAutomatorとは比較にならないほど軽快。

素晴らしい!

  • キーバインドの連続技、アクションの複合技には、新たな可能性を感じる。
新たな可能性を感じて作った設定
  • 調子に乗って作ってみた。
  • USキーボードのMacBook
{
/* 小文字・大文字・頭文字だけ大文字へ変換(control-/、l・u・c) */
  "^/" = {
    "l" = "lowercaseWord:";
    "u" = "uppercaseWord:";
    "c" = "capitalizeWord:";
  };

/* カーソル位置の行をすべて大文字タイトルにする(control-option-u) */
    "^~u" = ("selectParagraph:",
             "uppercaseWord:",
             "moveToEndOfParagraph:",
             "insertNewline:",
             "insertNewline:");

/* 直前の単語をタグにする(control-shift-h) */
    "^H" = ("setMark:",
            "moveWordBackward:",
            "deleteToMark:",
            "insertText:", "<", "yank:", "insertText:", ">",
            "setMark:",
            "insertText:", "</", "yank:", "insertText:", ">",
            "swapWithMark:");

/* 選択範囲の両端を1文字ずつ拡張する(control-shift-=) */
    "^+" = (
            "setMark:",
            "moveForward:",
            "moveForward:",
            "selectToMark:",
            "moveBackwardAndModifySelection:",
    );

/* 選択範囲を''""[]{}()<>「」囲う(control-x、'・"・[・{・(・<・control-[) */
  "^x" = {
    "'" = (
            "setMark:",
            "deleteToMark:",
            "insertText:", "'", "yank:", "insertText:", "'",
    );
    "\"" = (
            "setMark:",
            "deleteToMark:",
            "insertText:", "\"", "yank:", "insertText:", "\"",
    );
    "[" = (
            "setMark:",
            "deleteToMark:",
            "insertText:", "[", "yank:", "insertText:", "]",
    );
    "{" = (
            "setMark:",
            "deleteToMark:",
            "insertText:", "{", "yank:", "insertText:", "}",
    );
    "(" = (
            "setMark:",
            "deleteToMark:",
            "insertText:", "(", "yank:", "insertText:", ")",
    );
    "<" = (
            "setMark:",
            "deleteToMark:",
            "insertText:", "<", "yank:", "insertText:", ">",
    );
    "^[" = (
            "setMark:",
            "deleteToMark:",
            "insertText:", "「", "yank:", "insertText:", "」",
    );
  };

}
  • 選択範囲の両端を1文字ずつ縮小する アクションが思い浮かばない...。無理だろうか?
有名なキーバインド

いくつものキーバインドを正確に設定するのは意外と大変。そんな時は達人の設定が公開されていた。

参考ページ

上記のページとそこからリンクされるページには、非常に興味深い情報が盛りだくさん。(感謝です!)

  • 久々にワクワクしながら、ブログをむさぼり読む感覚を味わってしまった。
  • こんな仕組みがOSXに用意されていたとは...。
  • いつまで経っても分からないことだらけだ...。


*1:一例でしかない、もっと、もっと多くの処理が隠れているはず。

*2:Developer Tools(Xcode)をインストールすると、そこに含まれている。

*3:NSResponder Class Referenceのaction messageとメニュー名称のアクション