├── .ert-runner ├── .gitignore ├── .travis.yml ├── Cask ├── Makefile ├── README.ja.org ├── README.org ├── guide-key.el ├── img ├── guide-key-example-org-anime.gif ├── guide-key-example.png ├── guide-key-example2.png ├── guide-key-multiple-highlight.png └── guide-key-package-install.png └── test └── guide-key-test.el /.ert-runner: -------------------------------------------------------------------------------- 1 | -L . 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.elc 2 | /.cask 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: emacs-lisp 2 | env: 3 | - EMACS=emacs EVM_EMACS=emacs-24.1-bin 4 | - EMACS=emacs EVM_EMACS=emacs-24.2-bin 5 | - EMACS=emacs EVM_EMACS=emacs-24.3-bin 6 | - EMACS=emacs EVM_EMACS=emacs-24.4-bin 7 | - EMACS=emacs-snapshot EMACS_PPA=ppa:ubuntu-elisp/ppa 8 | before_install: 9 | # Install Emacs 10 | - if [ -n "$EMACS_PPA" ]; then 11 | sudo add-apt-repository -y "$EMACS_PPA"; 12 | sudo apt-get update -qq; 13 | sudo apt-get install -qq "$EMACS" "${EMACS}-el"; 14 | else 15 | sudo mkdir -p /usr/local/evm; 16 | sudo chown "$USER:$USER" /usr/local/evm; 17 | curl -fsSkL --max-time 10 --retry 10 --retry-delay 10 https://raw.github.com/rejeep/evm/master/go | bash; 18 | export PATH="$HOME/.evm/bin:$PATH"; 19 | evm install "$EVM_EMACS" --use; 20 | fi 21 | # Install Cask 22 | - curl -fsSkL --max-time 10 --retry 10 --retry-delay 10 23 | https://raw.github.com/cask/cask/master/go | python 24 | - export PATH="$HOME/.cask/bin:$PATH" 25 | - cask 26 | script: 27 | - emacs --version 28 | - make -------------------------------------------------------------------------------- /Cask: -------------------------------------------------------------------------------- 1 | (source gnu) 2 | (source melpa) 3 | (source marmalade) 4 | 5 | (package-file "guide-key.el") 6 | 7 | (development 8 | (depends-on "ert") 9 | (depends-on "ert-runner") 10 | (depends-on "undercover") 11 | (depends-on "dash") 12 | (depends-on "popwin") 13 | (depends-on "s")) -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | EMACS ?= emacs 2 | CASK ?= cask 3 | 4 | all: 5 | ${MAKE} clean 6 | ${MAKE} test 7 | ${MAKE} compile 8 | ${MAKE} test 9 | ${MAKE} clean 10 | 11 | compile: 12 | ${CASK} exec ${EMACS} -Q -batch -L . -eval \ 13 | "(progn \ 14 | (setq byte-compile-error-on-warn t) \ 15 | (batch-byte-compile))" guide-key.el 16 | test: 17 | ${CASK} exec ert-runner 18 | clean: 19 | rm -f guide-key.elc 20 | 21 | .PHONY: all compile test clean 22 | -------------------------------------------------------------------------------- /README.ja.org: -------------------------------------------------------------------------------- 1 | * guide-key.el [[https://travis-ci.org/kai2nenobu/guide-key][https://api.travis-ci.org/kai2nenobu/guide-key.png]] [[https://coveralls.io/r/kai2nenobu/guide-key][https://coveralls.io/repos/kai2nenobu/guide-key/badge.png?branch=master]] [[http://melpa.org/#/guide-key][http://melpa.org/packages/guide-key-badge.svg]] [[http://stable.melpa.org/#/guide-key][http://stable.melpa.org/packages/guide-key-badge.svg]] 2 | ** 概要 3 | guide-keyはキーバインドを自動的かつ動的に表示します。guide-keyは[[http://www.emacswiki.org/emacs/OneKey][one-key.el]]の置 4 | き換えを狙っています。 5 | 6 | このライブラリの特徴は以下のようなものです。 7 | - guide-keyはあなたの指定したプレフィクスキーに続くキーバインドを自動的にポッ 8 | プアップします。さらにキーバインドを変更しても、guide-keyは動的にその変更に 9 | 追随することができます。 10 | - guide-keyはコマンドをハイライトすることができます。これによりコマンドを探し 11 | やすくなり、キーバインドを覚えることが簡単になります。 12 | - guide-keyは既存のコマンドやキーバインドを上書きしません。 =describe-key= や 13 | =describe-bindings= を実行するのに悪影響がありません。 14 | ** インストール 15 | guide-keyは[[http://melpa.milkbox.net/][MELPA]]に追加されているので、guide-keyはpackage.elを使ってインストー 16 | ルすることができます。 17 | 18 | [[img/guide-key-package-install.png]] 19 | 20 | guide-keyは[[https://github.com/m2ym/popwin-el][popwin.el]]に依存しているので、popwin.elも同時にインストールされます。 21 | 22 | package.elがない場合は、[[https://github.com/m2ym/popwin-el][m2ym/popwin-el]]と[[https://github.com/kai2nenobu/guide-key][kai2nenobu/guide-key]]から直接ダウンロー 23 | ドして、 =load-path= 上においてください。 24 | ** 基本的な使い方 25 | =guide-key/guide-key-sequence= にお好きなプレイフィクスキーを追加してください。 26 | #+BEGIN_SRC emacs-lisp 27 | (require 'guide-key) 28 | (setq guide-key/guide-key-sequence '("C-x r" "C-x 4")) 29 | (guide-key-mode 1) ; Enable guide-key-mode 30 | #+END_SRC 31 | ここで設定したプレフィクスキーを押すと、それに続くキーバインドが少し遅れて(デ 32 | フォルトでは1秒後)自動的にポップアップされます。このスクリーンショットは 33 | =C-x r= を押した時のものです。 34 | 35 | [[img/guide-key-example.png]] 36 | 37 | あるいは =guide-key/guide-key-sequence= を =t= に設定すれば、すべてのプレフィ 38 | クスキーに対してポップアップされます。 39 | 40 | guide-keyは指定された正規表現に一致するコマンドをハイライトします。 =C-x r= 41 | に続くキーバインドは =rectangle= 系、 =register= 系、 =bookmark= 系に分かれま 42 | す。もし、 =rectangle= 系のみをハイライトしたい場合は、init.elに以下のように 43 | 設定します。 44 | #+BEGIN_SRC emacs-lisp 45 | (setq guide-key/highlight-command-regexp "rectangle") 46 | #+END_SRC 47 | 48 | [[img/guide-key-example2.png]] 49 | 50 | この機能によりコマンドが見つけやすくなり、キーバインドを覚えやすくなります。も 51 | しすべての系統のコマンドをハイライトしたい場合は、複数の正規表現とフェイスを 52 | 指定することができます。 53 | 54 | #+BEGIN_SRC emacs-lisp 55 | (setq guide-key/highlight-command-regexp 56 | '("rectangle" 57 | ("register" . font-lock-type-face) 58 | ("bookmark" . "hot pink"))) 59 | #+END_SRC 60 | 61 | [[img/guide-key-multiple-highlight.png]] 62 | 63 | =guide-key/highlight-command-regexp= の要素がコンスセルの場合、carはハイライ 64 | トすべきコマンドの正規表現であり、cdrはハイライトに利用されるフェイスか色名に 65 | なります。 66 | 67 | さらにプレフィクスコマンドは自動的にハイライトされます。 68 | 69 | プレフィクスキーを押してからどのくらいの時間でキーバインドをポップアップさせる 70 | かは、Emacsの経験によって変わってくるでしょう。これは =guide-key/idle-delay= 71 | で変更することができます。 72 | #+BEGIN_SRC emacs-lisp 73 | (setq guide-key/idle-delay 0.1) 74 | #+END_SRC 75 | プレフィクスキーを押してから、ここで設定した時間何も入力しなかった時だけキー 76 | バインドが表示されます。このためすばやく入力できるキーバインドの場合は、いま 77 | までと変わらず、ポップアップが表示されることなく入力することができます。 78 | 79 | guide-keyは以下の環境で動くことを確認しています。 80 | - Emacs 24.4, Ubuntu 14.04 or Windows 8.1 64bit 81 | - Emacs 24.2, Ubuntu 12.04 or Windows 7 64bit 82 | - Emacs 23.3, Ubuntu 12.04 or Windows 7 64bit 83 | - Emacs 22.3, Windows 7 64bit 84 | - Emacs 24.3.1, OS X 10.9 85 | popwinが動けばguide-keyも動作すると思います。また端末内で動いているEmacsでも 86 | guide-keyは問題なく動作します。 87 | ** 発展的な使い方 88 | *** 再帰的なチェック 89 | たくさんのプレフィクスを =guide-key/guide-key-sequence= に追加するのは大変です。 90 | =guide-key/recursive-key-sequence-flag= を使えばこの問題が解決します。 91 | =guide-key/recursive-key-sequence-flag= がnon-nilの時、guide-keyは入力されたキー 92 | を再帰的にチェックします。つまり =C-x 8 ^= が入力されている時、guide-keyは 93 | =guide-key/guide-key-sequence= に =C-x 8= や =C-x= が含まれているかをチェック 94 | します。 95 | 96 | 例えば以下のように設定した場合 97 | #+BEGIN_SRC emacs-lisp 98 | (setq guide-key/guide-key-sequence '("C-x")) 99 | (setq guide-key/recursive-key-sequence-flag t) 100 | #+END_SRC 101 | =C-x r= や =C-x 8= など =C-x= に続くプレフィクスキーを押した際に、キーバイン 102 | ドがポップされます。 103 | *** 特定のモードに関する設定 104 | 特定のモードのみに追加の設定をすることができます。 105 | =guide-key/add-local-guide-key-sequence= 、 106 | =guide-key/add-local-highlight-command-regexp= とモードのフックを使ってくださ 107 | い。 108 | 109 | 以下のコードは =org-mode= の設定例です。 110 | #+BEGIN_SRC emacs-lisp 111 | (defun guide-key/my-hook-function-for-org-mode () 112 | (guide-key/add-local-guide-key-sequence "C-c") 113 | (guide-key/add-local-guide-key-sequence "C-c C-x") 114 | (guide-key/add-local-highlight-command-regexp "org-")) 115 | (add-hook 'org-mode-hook 'guide-key/my-hook-function-for-org-mode) 116 | #+END_SRC 117 | =org-mode= バッファで =C-c C-x p= を押して =org-set-property= を実行すると、 118 | 以下のようになります。 119 | 120 | [[img/guide-key-example-org-anime.gif]] 121 | 122 | =guide-key/guide-key-sequence= については 123 | =guide-key/add-local-guide-key-sequence= を使わずに設定することもできます。例 124 | えば以下のように設定してください。 125 | #+BEGIN_SRC emacs-lisp 126 | (setq guide-key/guide-key-sequence 127 | '("C-x r" "C-x 4" 128 | (org-mode "C-c C-x") 129 | (outline-minor-mode "C-c @"))) 130 | #+END_SRC 131 | メジャーモードが =org-mode= の場合 =C-c C-x= に続くキーバインドがポップアップ 132 | されます。 =outline-minor-mode= が有効な場合、 =C-c @= に続くキーバインドがポッ 133 | プアップされます。 134 | *** =key-chord= との連携 135 | guide-keyは[[http://www.emacswiki.org/emacs/KeyChord][key-chord.el]]と連携することができます。key chordに続くキーバインドを 136 | ポップアップしたい場合、 =guide-key/key-chord-hack-on= を実行する必要がありま 137 | す。その上で以下のように =guide-key/guide-key-sequence= にkey chordを追加して 138 | ください。 139 | #+BEGIN_SRC emacs-lisp 140 | (key-chord-define global-map "@4" 'ctl-x-4-prefix) 141 | 142 | (guide-key/key-chord-hack-on) 143 | (setq guide-key/guide-key-sequence '(" @ 4" " 4 @")) 144 | #+END_SRC 145 | 146 | =guide-key/recursive-key-sequence-flag= がnon-nilの場合、さらにシンプルになり 147 | ます。 148 | #+BEGIN_SRC emacs-lisp 149 | (guide-key/key-chord-hack-on) 150 | (setq guide-key/recursive-key-sequence-flag t) 151 | (setq guide-key/guide-key-sequence '("")) 152 | #+END_SRC 153 | この設定の場合は、すべてのkey chordに続くキーバインドがポップアップされます。 154 | 155 | =guide-key/key-chord-hack-on= は =this-command-keys= と 156 | =this-command-keys-vector= という原始的な関数をアドバイスしているため、 *危 157 | 険* な可能性があります。 158 | *** その他の関数や変数 159 | guide-keyを操作するその他の関数や変数を以下に示します。 160 | - =(guide-key-mode ARG)=: =guide-key-mode= はマイナーモードとして実装されてい 161 | ます。"M-x =guide-key-mode="を実行するとguide-keyの有効無効を切り替えます。 162 | =guide-key-mode= はグローバルマイナーモードであるため、バッファ個別に有効無 163 | 効を切り替えることはできません。 164 | - =guide-key/popup-window-position=: この変数はキーバインドがポップアップされ 165 | る位置を設定します。この変数の値は =right=, =bottom=, =left=, =top= のいず 166 | れかです。デフォルト値は =right= です。 167 | - =guide-key/polling-time=: この変数はポーリング時間を設定します。デフォルト 168 | 値は0.1秒です。 169 | - =guide-key/idle-delay=: この変数はプレフィクスキーを押してからキーバインドが 170 | ポップアップされるまでの時間を設定します。デフォルト値は1.0秒です。キー入力 171 | の途中で止まらない限りは、いつもどおり入力することができます。0.0に設定すれ 172 | ば旧来の動作と同様になります。 173 | - =guide-key/text-scale-amount=: この変数はポップアップするキーバインドの文字 174 | の大きさを設定します。デフォルト値は0です(文字の大きさはEmacsでのデフォル 175 | トの大きさになります)。テキストを大きくしたい場合は正の数、小さくしたい場 176 | 合は負の数に設定してください。 177 | ** Known issues 178 | いくつかの問題や欠点があります。 179 | - guide-keyはすべてのキーバインドを表示しようとするため、ポップアップするウィ 180 | ンドウは大きくなりがちです。ポップアップするウィンドウの大きさがフレームよ 181 | り大きくなると、正常にポップアップできなくなります。 =C-x= のようにキーバイ 182 | ンドが多いプレフィクスキーはポップアップさせないことをお勧めします。 183 | - ポップアップされたウィンドウがすぐに閉じてしまうことがあります(キーバイン 184 | ドがポップアップされないように見える)。popwinがコントロールしているウィン 185 | ドウを閉じた直後にその現象が起きがちです。そのような時は、 =C-g= を連打して 186 | からもう一度試してみてください。 187 | - =one-key= はコマンド名の代わりに短い説明文字列を表示することができます。こ 188 | れは手動でテンプレートを作っている利点です。対照的にguide-keyは動的にキーバ 189 | インドを抽出しているため、コマンド名以外を表示することはできません。 190 | ** TODOs 191 | - [ ] confine a length of command name 192 | - [ ] confine the number of items to guide 193 | - [ ] a feature to exclude or include guide by command name 194 | - [X] enrichment of guide buffer 195 | - [ ] select more user-friendly colors 196 | - [X] automatically guide all following keys 197 | - [X] pop up guide buffer at top or bottom 198 | - [X] prefix argument processing 199 | - [X] define global minor mode 200 | ** ChangeLog 201 | *** Version 1.2.5 202 | - Enable setting =guide-key/guide-key-sequence= to =t= so that any key 203 | sequence will pop up bindings ([[https://github.com/kai2nenobu/guide-key/pull/22][#22]]) 204 | - Enable to guide key sequences with universal argument ([[https://github.com/kai2nenobu/guide-key/issues/6][#6]]) 205 | - Add a feature to specify a direct color name ([[https://github.com/kai2nenobu/guide-key/issues/25][#25]]) 206 | *** Version 1.2.4 207 | - Change the format of tag name to suit [[http://stable.melpa.org/#/][MELPA Stable]] rule (=ver1.x.y= -> 208 | =v1.x.y=) 209 | - Add some tests and work with [[https://travis-ci.org/kai2nenobu/guide-key][Travis CI]] and [[https://coveralls.io/r/kai2nenobu/guide-key][Coveralls]] 210 | - Fix a regression bug about a local highlight face ([[https://github.com/kai2nenobu/guide-key/pull/16][#16]]) 211 | - Add a feature to specify multiple highlight faces according to each 212 | regular expression ([[https://github.com/kai2nenobu/guide-key/issues/14][#14]]) 213 | *** Version 1.2.3 214 | - Modify a link of one-key to more explanatory page 215 | - Allow key sequences with regexp special characters. (Thanks to @mrc) 216 | - Allow popup function to be called directly. (Thanks to @mlf176f2) 217 | *** Version 1.2.2 218 | - Add a Japanese README. 219 | - Add a documentation about key-chord hack. 220 | *** Version 1.2.1 221 | - Support for mode specific key sequences in 222 | =guide-key/guide-key-sequence=. (Thanks to @Fuco1) 223 | *** Version 1.2.0 224 | - Add a feature to scale the text size in the guide buffer. 225 | - Add a feature to popup the guide buffer with delay. (Thanks to 226 | @deprecated) 227 | - Fix up README. (Thanks to @haxney) 228 | *** Version 1.1.1 229 | - Suppress an annoying message, "No following key". 230 | *** Version 1.1.0 231 | - Add a functionality to check an input key sequence recursively. This 232 | enables to avoid adding many prefixes to 233 | =guide-key/guide-key-sequence=. (Thanks @kui) 234 | *** Version 1.0.1 235 | - Change to save and restore a last configuration of popwin 236 | *** Version 1.0.0 237 | - First release version 238 | - Adjust names of functions and variables 239 | - Add some documentations 240 | *** Version 0.1.2 241 | - Enable to guide key-chord bindings. 242 | *** Version 0.1.1 243 | - Make =guide-key-mode= global minor mode. 244 | *** Version 0.1.0 245 | - Initial version. 246 | -------------------------------------------------------------------------------- /README.org: -------------------------------------------------------------------------------- 1 | This package *is not maintained actively*. [[https://github.com/justbur/emacs-which-key][which-key]] is a better altenative. 2 | 3 | ----- 4 | 5 | * guide-key.el [[https://travis-ci.org/kai2nenobu/guide-key][https://api.travis-ci.org/kai2nenobu/guide-key.png]] [[https://coveralls.io/r/kai2nenobu/guide-key][https://coveralls.io/repos/kai2nenobu/guide-key/badge.png?branch=master]] [[http://melpa.org/#/guide-key][http://melpa.org/packages/guide-key-badge.svg]] [[http://stable.melpa.org/#/guide-key][http://stable.melpa.org/packages/guide-key-badge.svg]] 6 | ** Overview 7 | guide-key.el displays the available key bindings automatically and dynamically. 8 | guide-key aims to be an alternative of [[http://www.emacswiki.org/emacs/OneKey][one-key.el]]. 9 | 10 | Here are some features of this library. 11 | - guide-key automatically pops up the keys following your favorite 12 | prefixes. Moreover, even if you change key bindings, guide-key follows the 13 | change dynamically. 14 | - guide-key can highlight particular commands. This makes it easy to find a 15 | command you are looking for, and to learn its key binding. 16 | - guide-key doesn't overwrite existing commands and key bindings, so there 17 | is no interference with =describe-key= and =describe-bindings=. 18 | ** Installation 19 | I added guide-key to [[http://melpa.milkbox.net/][MELPA]]. You can install guide-key with package.el: 20 | 21 | [[img/guide-key-package-install.png]] 22 | 23 | Because guide-key depends on [[https://github.com/m2ym/popwin-el][popwin.el]], popwin.el is also installed. 24 | 25 | If you don't have package.el, please download [[https://github.com/m2ym/popwin-el][m2ym/popwin-el]] and 26 | [[https://github.com/kai2nenobu/guide-key][kai2nenobu/guide-key]] directly, and then put them in your =load-path=. 27 | ** Basic usage 28 | Just add your favorite prefix keys to =guide-key/guide-key-sequence= as 29 | below. 30 | #+BEGIN_SRC emacs-lisp 31 | (require 'guide-key) 32 | (setq guide-key/guide-key-sequence '("C-x r" "C-x 4")) 33 | (guide-key-mode 1) ; Enable guide-key-mode 34 | #+END_SRC 35 | When you press these prefix keys, key bindings are automatically 36 | popped up after a short delay (1 second by default). This is a 37 | screenshot when you press "C-x r". 38 | 39 | [[img/guide-key-example.png]] 40 | 41 | If =guide-key/guide-key-sequence= is =t=, any prefixes will pop up bindings. 42 | 43 | guide-key can highlight commands which match a specified regular expression. 44 | Key bindings following "C-x r" are =rectangle= family, =register= family and 45 | =bookmark= family. If you want to highlight only =rectangle= family 46 | commands, put this setting in your init.el. 47 | #+BEGIN_SRC emacs-lisp 48 | (setq guide-key/highlight-command-regexp "rectangle") 49 | #+END_SRC 50 | 51 | [[img/guide-key-example2.png]] 52 | 53 | This feature makes it easy to find commands and learn their key bindings. If 54 | you want to highlight all families, you can specify multiple regular 55 | expressions and faces as below. 56 | 57 | #+BEGIN_SRC emacs-lisp 58 | (setq guide-key/highlight-command-regexp 59 | '("rectangle" 60 | ("register" . font-lock-type-face) 61 | ("bookmark" . "hot pink"))) 62 | #+END_SRC 63 | 64 | [[img/guide-key-multiple-highlight.png]] 65 | 66 | If an element of =guide-key/highlight-command-regexp= is a cons cell, its car 67 | means a regular expression to highlight, and its cdr means a face or a color 68 | name put on command names. 69 | 70 | Moreover, prefix commands are automatically highlighted. 71 | 72 | Depending on your level of emacs experience, you may want a shorter or 73 | longer delay between pressing a key and the appearance of the guide 74 | buffer. This can be controlled by setting =guide-key/idle-delay=: 75 | #+BEGIN_SRC emacs-lisp 76 | (setq guide-key/idle-delay 0.1) 77 | #+END_SRC 78 | The guide buffer is displayed only when you pause between keystrokes 79 | for longer than this delay, so it will keep out of your way when you 80 | are typing key sequences that you already know well. 81 | 82 | I've confirmed that guide-key works well in these environments. 83 | - Emacs 24.4, Ubuntu 14.04 or Windows 8.1 64bit 84 | - Emacs 24.2, Ubuntu 12.04 or Windows 7 64bit 85 | - Emacs 23.3, Ubuntu 12.04 or Windows 7 64bit 86 | - Emacs 22.3, Windows 7 64bit 87 | - Emacs 24.3.1, OS X 10.9 88 | If popwin works, I think guide-key will work as well. You can use 89 | guide-key with Emacs working in terminal. 90 | ** Advanced Usage 91 | *** Check key sequence recursively 92 | It is annoying to add many prefixes to =guide-key/guide-key-sequence=. 93 | =guide-key/recursive-key-sequence-flag= releases you from this problem. If 94 | =guide-key/recursive-key-sequence-flag= is non-nil, guide-key checks an input 95 | key sequence recursively. That is, if "C-x 8 ^" is an input key sequence, 96 | guide-key checks whether =guide-key/guide-key-sequence= includes "C-x 8" and 97 | "C-x". 98 | 99 | For example, if you configure as below, 100 | #+BEGIN_SRC emacs-lisp 101 | (setq guide-key/guide-key-sequence '("C-x")) 102 | (setq guide-key/recursive-key-sequence-flag t) 103 | #+END_SRC 104 | the guide buffer is popped up when you input "C-x r", "C-x 8" and 105 | any other prefixes following "C-x". 106 | *** Add settings in a particular mode 107 | You can add extra settings in a particular mode. Please use 108 | =guide-key/add-local-guide-key-sequence=, 109 | =guide-key/add-local-highlight-command-regexp= and the hook of 110 | that mode. 111 | 112 | This code is an example for org-mode. 113 | #+BEGIN_SRC emacs-lisp 114 | (defun guide-key/my-hook-function-for-org-mode () 115 | (guide-key/add-local-guide-key-sequence "C-c") 116 | (guide-key/add-local-guide-key-sequence "C-c C-x") 117 | (guide-key/add-local-highlight-command-regexp "org-")) 118 | (add-hook 'org-mode-hook 'guide-key/my-hook-function-for-org-mode) 119 | #+END_SRC 120 | If you execute =org-set-property= by pressing "C-c C-x p" in org-mode buffer, 121 | Emacs behaves as below. 122 | 123 | [[img/guide-key-example-org-anime.gif]] 124 | 125 | In respect of =guide-key/guide-key-sequence=, you can add mode specific key 126 | sequences without =guide-key/add-local-guide-key-sequence=. For example, 127 | configure as below. 128 | #+BEGIN_SRC emacs-lisp 129 | (setq guide-key/guide-key-sequence 130 | '("C-x r" "C-x 4" 131 | (org-mode "C-c C-x") 132 | (outline-minor-mode "C-c @"))) 133 | #+END_SRC 134 | In this case, if the current major mode is =org-mode=, guide key bindings 135 | following "C-c C-x". If =outline-minor-mode= is enabled, guide key bindings 136 | following "C-c @". 137 | *** Work with =key-chord= 138 | guide-key can work with [[http://www.emacswiki.org/emacs/KeyChord][key-chord.el]]. If you want to guide key bindings 139 | following key chord, you need to execute 140 | =guide-key/key-chord-hack-on=. Then, add your favorite key chord to 141 | =guide-key/guide-key-sequence= as below. 142 | #+BEGIN_SRC emacs-lisp 143 | (key-chord-define global-map "@4" 'ctl-x-4-prefix) 144 | 145 | (guide-key/key-chord-hack-on) 146 | (setq guide-key/guide-key-sequence '(" @ 4" " 4 @")) 147 | #+END_SRC 148 | 149 | If =guide-key/recursive-key-sequence-flag= is non-nil, more simple. 150 | #+BEGIN_SRC emacs-lisp 151 | (guide-key/key-chord-hack-on) 152 | (setq guide-key/recursive-key-sequence-flag t) 153 | (setq guide-key/guide-key-sequence '("")) 154 | #+END_SRC 155 | In this case, key bindings are popped up when you type any of key chords. 156 | 157 | This hack *may be dangerous* because it advices primitive functions; 158 | =this-command-keys= and =this-command-keys-vector=. 159 | *** Other functions and variables 160 | Here are some functions and variables which control guide-key. 161 | - =(guide-key-mode ARG)=: =guide-key-mode= is implemented as a minor mode. 162 | Executing "M-x =guide-key-mode=" toggles whether guide-key is enabled or not. 163 | Because =guide-key-mode= is a global minor mode, =guide-key-mode= is enabled 164 | in all buffers or disabled in all buffers. 165 | - =guide-key/popup-window-position=: This variable controls where a guide-key 166 | buffer is popped up. A value of this variable is one of =right=, =bottom=, 167 | =left=, =top=. The default value is =right=. 168 | - =guide-key/polling-time=: This variable controls a polling time. The 169 | default value is 0.1 (in seconds). 170 | - =guide-key/idle-delay=: This variable controls the delay between 171 | starting a key sequence and popping up the guide buffer. The default 172 | value is 1.0 (in seconds), which means that guide-key will keep out 173 | of your way unless you hesitate in the middle of a key sequence . 174 | Set this to 0.0 to revert to the old default behavior. 175 | - =guide-key/text-scale-amount=: This variable controls the size of text in 176 | guide buffer. The default value is 0 (it means default size in Emacs). If 177 | you want to enlarge text, set positive number. Otherwise, set negative 178 | number. 179 | ** Known issues 180 | Here are some issues and drawbacks. 181 | - Because guide-key tries to pop up all key bindings, a size of popup window 182 | tends to be big. If the popup window is bigger than the current frame, 183 | guide-key cannot pop up normally. I recommend you not to add a prefix which 184 | has many key bindings like "C-x". 185 | - A popup window sometimes closes immediately. It tends to happen right after 186 | a window controlled by popwin.el closes. In that case, please retry after 187 | you type "C-g" a couple of times. 188 | - one-key can display a short description instead of its command name. This 189 | is an advantage to creating template manually. In contrast, because 190 | guide-key extracts key bindings dynamically, guide-key can display nothing 191 | except a command name. 192 | ** TODOs 193 | - [ ] confine a length of command name 194 | - [ ] confine the number of items to guide 195 | - [ ] a feature to exclude or include guide by command name 196 | - [X] enrichment of guide buffer 197 | - [ ] select more user-friendly colors 198 | - [X] automatically guide all following keys 199 | - [X] pop up guide buffer at top or bottom 200 | - [X] prefix argument processing 201 | - [X] define global minor mode 202 | ** ChangeLog 203 | *** Version 1.2.5 204 | - Enable setting =guide-key/guide-key-sequence= to =t= so that any key 205 | sequence will pop up bindings ([[https://github.com/kai2nenobu/guide-key/pull/22][#22]]) 206 | - Enable to guide key sequences with universal argument ([[https://github.com/kai2nenobu/guide-key/issues/6][#6]]) 207 | - Add a feature to specify a direct color name ([[https://github.com/kai2nenobu/guide-key/issues/25][#25]]) 208 | *** Version 1.2.4 209 | - Change the format of tag name to suit [[http://stable.melpa.org/#/][MELPA Stable]] rule (=ver1.x.y= -> 210 | =v1.x.y=) 211 | - Add some tests and work with [[https://travis-ci.org/kai2nenobu/guide-key][Travis CI]] and [[https://coveralls.io/r/kai2nenobu/guide-key][Coveralls]] 212 | - Fix a regression bug about a local highlight face ([[https://github.com/kai2nenobu/guide-key/pull/16][#16]]) 213 | - Add a feature to specify multiple highlight faces according to each 214 | regular expression ([[https://github.com/kai2nenobu/guide-key/issues/14][#14]]) 215 | *** Version 1.2.3 216 | - Modify a link of one-key to more explanatory page 217 | - Allow key sequences with regexp special characters. (Thanks to @mrc) 218 | - Allow popup function to be called directly. (Thanks to @mlf176f2) 219 | *** Version 1.2.2 220 | - Add a Japanese README. 221 | - Add a documentation about key-chord hack. 222 | *** Version 1.2.1 223 | - Support for mode specific key sequences in 224 | =guide-key/guide-key-sequence=. (Thanks to @Fuco1) 225 | *** Version 1.2.0 226 | - Add a feature to scale the text size in the guide buffer. 227 | - Add a feature to popup the guide buffer with delay. (Thanks to 228 | @deprecated) 229 | - Fix up README. (Thanks to @haxney) 230 | *** Version 1.1.1 231 | - Suppress an annoying message, "No following key". 232 | *** Version 1.1.0 233 | - Add a functionality to check an input key sequence recursively. This 234 | enables to avoid adding many prefixes to 235 | =guide-key/guide-key-sequence=. (Thanks @kui) 236 | *** Version 1.0.1 237 | - Change to save and restore a last configuration of popwin 238 | *** Version 1.0.0 239 | - First release version 240 | - Adjust names of functions and variables 241 | - Add some documentations 242 | *** Version 0.1.2 243 | - Enable to guide key-chord bindings. 244 | *** Version 0.1.1 245 | - Make =guide-key-mode= global minor mode. 246 | *** Version 0.1.0 247 | - Initial version. 248 | -------------------------------------------------------------------------------- /guide-key.el: -------------------------------------------------------------------------------- 1 | ;;; guide-key.el --- Guide the following key bindings automatically and dynamically 2 | 3 | ;; Copyright (C) 2012, 2013 Tsunenobu Kai 4 | 5 | ;; Author: Tsunenobu Kai 6 | ;; URL: https://github.com/kai2nenobu/guide-key 7 | ;; Version: 1.2.5 8 | ;; Package-Requires: ((dash "2.10.0") (popwin "0.3.0") (s "1.9.0")) 9 | ;; Keywords: help convenience 10 | 11 | ;; This program is free software; you can redistribute it and/or modify 12 | ;; it under the terms of the GNU General Public License as published by 13 | ;; the Free Software Foundation, either version 3 of the License, or 14 | ;; (at your option) any later version. 15 | 16 | ;; This program is distributed in the hope that it will be useful, 17 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | ;; GNU General Public License for more details. 20 | 21 | ;; You should have received a copy of the GNU General Public License 22 | ;; along with this program. If not, see . 23 | 24 | ;;; Commentary: 25 | 26 | ;; Overview: 27 | ;; 28 | ;; guide-key.el displays the available key bindings automatically and dynamically. 29 | ;; guide-key aims to be an alternative of one-key.el. 30 | ;; 31 | ;; Here are some features of this library. 32 | ;; - guide-key automatically pops up the keys following your favorite 33 | ;; prefixes. Moreover, even if you change key bindings, guide-key follows the 34 | ;; change dynamically. 35 | ;; - guide-key can highlight particular commands. This makes it easy to find a 36 | ;; command you are looking for, and to learn its key binding. 37 | ;; - guide-key doesn't overwrite existing commands and key bindings, so there 38 | ;; is no interference with `describe-key' and `describe-bindings'. 39 | ;; 40 | ;; 41 | ;; Installation: 42 | ;; 43 | ;; I added guide-key to MELPA. You can install guide-key with package.el. 44 | ;; Because guide-key depends on popwin.el, popwin.el is also installed. 45 | ;; 46 | ;; If you don't have package.el, please download popwin.el and guide-key.el 47 | ;; directly from https://github.com/m2ym/popwin-el and 48 | ;; https://github.com/kai2nenobu/guide-key, and then put them in your 49 | ;; `load-path'. 50 | ;; 51 | ;; 52 | ;; Basic usage: 53 | ;; 54 | ;; You just add your favorite prefix keys to `guide-key/guide-key-sequence' 55 | ;; as below. 56 | ;; 57 | ;; (require 'guide-key) 58 | ;; (setq guide-key/guide-key-sequence '("C-x r" "C-x 4")) 59 | ;; (guide-key-mode 1) ; Enable guide-key-mode 60 | ;; 61 | ;; When you press these prefix keys, key bindings are automatically 62 | ;; popped up after a short delay (1 second by default). 63 | ;; 64 | ;; To activate guide-key for any key sequence instead of just the ones 65 | ;; listed above then use: 66 | ;; 67 | ;; (setq guide-key/guide-key-sequence t) 68 | ;; 69 | ;; guide-key can highlight commands which match a specified regular expression. 70 | ;; Key bindings following "C-x r" are rectangle family, register family and 71 | ;; bookmark family. If you want to highlight only rectangle family 72 | ;; commands, put this setting in your init.el. 73 | ;; 74 | ;; (setq guide-key/highlight-command-regexp "rectangle") 75 | ;; 76 | ;; This feature makes it easy to find commands and learn their key bindings. 77 | ;; If you want to highlight all families, you can specify multiple regular 78 | ;; expressions and faces as below. 79 | ;; 80 | ;; (setq guide-key/highlight-command-regexp 81 | ;; '("rectangle" 82 | ;; ("register" . font-lock-type-face) 83 | ;; ("bookmark" . font-lock-warning-face))) 84 | ;; 85 | ;; If an element of `guide-key/highlight-command-regexp' is cons, its car 86 | ;; means a regular expression to highlight, and its cdr means a face put on 87 | ;; command names. 88 | ;; 89 | ;; Moreover, prefix commands are automatically highlighted. 90 | ;; 91 | ;; Depending on your level of emacs experience, you may want a shorter or 92 | ;; longer delay between pressing a key and the appearance of the guide 93 | ;; buffer. This can be controlled by setting `guide-key/idle-delay': 94 | ;; 95 | ;; (setq guide-key/idle-delay 0.1) 96 | ;; 97 | ;; The guide buffer is displayed only when you pause between keystrokes 98 | ;; for longer than this delay, so it will keep out of your way when you 99 | ;; are typing key sequences that you already know well. 100 | ;; 101 | ;; I've confirmed that guide-key works well in these environments. 102 | ;; - Emacs 24.2, Ubuntu 12.04 or Windows 7 64bit 103 | ;; - Emacs 23.3, Ubuntu 12.04 or Windows 7 64bit 104 | ;; - Emacs 22.3, Windows 7 64bit 105 | ;; - Emacs 24.3.1, OS X 10.9 106 | ;; If popwin works, I think guide-key will work as well. You can use 107 | ;; guide-key with Emacs working in terminal. 108 | ;; 109 | ;; 110 | ;; Advanced usage: 111 | ;; 112 | ;; It is bothering to add many prefixes to `guide-key/guide-key-sequence'. 113 | ;; `guide-key/recursive-key-sequence-flag' releases you from this problem. 114 | ;; If `guide-key/recursive-key-sequence-flag' is non-nil, guide-key checks a 115 | ;; input key sequence recursively. That is, if "C-x 8 ^" is an input key 116 | ;; sequence, guide-key checks whether `guide-key/guide-key-sequence' includes 117 | ;; "C-x 8" and "C-x". 118 | ;; 119 | ;; For example, if you configure as below, 120 | ;; 121 | ;; (setq guide-key/guide-key-sequence '("C-x")) 122 | ;; (setq guide-key/recursive-key-sequence-flag t) 123 | ;; 124 | ;; the guide buffer is popped up when you input "C-x r", "C-x 8" and 125 | ;; any other prefixes following "C-x". 126 | ;; 127 | ;; 128 | ;; You can add extra settings in a particular mode. Please use 129 | ;; `guide-key/add-local-guide-key-sequence', 130 | ;; `guide-key/add-local-highlight-command-regexp' and the hook of 131 | ;; that mode. 132 | ;; 133 | ;; 134 | ;; This code is a example of org-mode. 135 | ;; 136 | ;; (defun guide-key/my-hook-function-for-org-mode () 137 | ;; (guide-key/add-local-guide-key-sequence "C-c") 138 | ;; (guide-key/add-local-guide-key-sequence "C-c C-x") 139 | ;; (guide-key/add-local-highlight-command-regexp "org-")) 140 | ;; (add-hook 'org-mode-hook 'guide-key/my-hook-function-for-org-mode) 141 | ;; 142 | ;; In respect of `guide-key/guide-key-sequence', you can add mode specific key 143 | ;; sequences without `guide-key/add-local-guide-key-sequence'. For example, 144 | ;; configure as below. 145 | ;; 146 | ;; (setq guide-key/guide-key-sequence 147 | ;; '("C-x r" "C-x 4" 148 | ;; (org-mode "C-c C-x") 149 | ;; (outline-minor-mode "C-c @"))) 150 | ;; 151 | ;; In this case, if the current major mode is `org-mode', guide key bindings 152 | ;; following "C-c C-x". If `outline-minor-mode' is enabled, guide key bindings 153 | ;; following "C-c @". 154 | ;; 155 | ;; 156 | ;; `guide-key' can work with key-chord.el. If you want to guide key bindings 157 | ;; following key chord, you need to execute 158 | ;; `guide-key/key-chord-hack-on'. Then, add your favorite key chord to 159 | ;; `guide-key/guide-key-sequence' as below. 160 | ;; 161 | ;; (key-chord-define global-map "@4" 'ctl-x-4-prefix) 162 | ;; 163 | ;; (guide-key/key-chord-hack-on) 164 | ;; (setq guide-key/guide-key-sequence '(" @ 4" " 4 @")) 165 | ;; 166 | ;; If =guide-key/recursive-key-sequence-flag= is non-nil, more simple. 167 | ;; 168 | ;; (guide-key/key-chord-hack-on) 169 | ;; (setq guide-key/recursive-key-sequence-flag t) 170 | ;; (setq guide-key/guide-key-sequence '("")) 171 | ;; 172 | ;; In this case, key bindings are popped up when you type any of key chords. 173 | ;; 174 | ;; This hack *may be dangerous* because it advices primitive functions; 175 | ;; `this-command-keys' and `this-command-keys-vector'. 176 | ;; 177 | ;; 178 | ;; Here are some functions and variables which control guide-key. 179 | ;; - `guide-key-mode': 180 | ;; guide-key-mode is implemented as a minor mode. 181 | ;; Excuting M-x guide-key-mode toggles whether guide-key is enabled or 182 | ;; not. Because guide-key-mode is a global minor mode, guide-key-mode is 183 | ;; enabled in all buffers or disabled in all buffers. 184 | ;; - `guide-key/popup-window-position': 185 | ;; This variable controls where a guide-key buffer is popped up. A value of 186 | ;; this variable is one of `right', `bottom', `left', `top'. The default 187 | ;; value is `right'. 188 | ;; - `guide-key/polling-time': 189 | ;; This variable controls a polling time. The default value is 0.1 (in seconds). 190 | ;; - `guide-key/idle-delay': 191 | ;; This variable controls the delay between starting a key sequence and 192 | ;; popping up the guide buffer. The default value is 1.0 (in seconds), 193 | ;; which means that guide-key will keep out of your way unless you hesitate 194 | ;; in the middle of a key sequence . Set this to 0.0 to revert to the old 195 | ;; default behavior. 196 | ;; - `guide-key/text-scale-amount': 197 | ;; This variable controls the size of text in guide buffer. The default 198 | ;; value is 0 (it means default size in Emacs). If you want to enlarge 199 | ;; text, set positive number. Otherwise, set negative number. 200 | ;; 201 | ;; Enjoy! 202 | 203 | ;;; Code: 204 | 205 | (eval-when-compile 206 | (require 'cl) 207 | (require 'face-remap)) 208 | 209 | (require 'dash) 210 | (require 'popwin) 211 | (require 's) 212 | 213 | ;;; variables 214 | (defgroup guide-key nil 215 | "Guide key bidings." 216 | :group 'help 217 | :prefix "guide-key/") 218 | 219 | (defcustom guide-key/guide-key-sequence nil 220 | "*Key sequences to guide in `guide-key-mode'. 221 | This variable is a list of string representation. 222 | Both representations, like \"C-x r\" and \"\\C-xr\", 223 | are allowed. 224 | 225 | In addition, an element of this list can be a list whose car is 226 | the symbol for a certain mode, and whose cdr is a list of key 227 | sequences to consider only if that mode is active. 228 | 229 | Set this variable to `t' to enable for any key sequence." 230 | :type '(repeat (choice (string :tag "Prefix key sequence") 231 | (cons :tag "Mode specific sequence" 232 | (symbol :tag "Symbol for mode") 233 | (repeat (string :tag "Prefix key sequence"))))) 234 | :group 'guide-key) 235 | 236 | (defcustom guide-key/polling-time 0.1 237 | "*Polling time to check an input key sequence." 238 | :type 'float 239 | :group 'guide-key) 240 | 241 | (defcustom guide-key/idle-delay 1.0 242 | "*Delay in seconds before guide buffer is displayed." 243 | :type 'float 244 | :group 'guide-key) 245 | 246 | (defcustom guide-key/highlight-prefix-regexp "prefix" 247 | "*Regexp for prefix commands." 248 | :type 'regexp 249 | :group 'guide-key) 250 | 251 | (defcustom guide-key/highlight-command-regexp nil 252 | "*Regexp for commands to highlight. 253 | If a command name matches this regexp, it is highlighted with 254 | `guide-key/highlight-command-face'. 255 | 256 | This variable can be a list and its element is either a regexp or 257 | a cons cell, its car is a regexp and its cdr is face symbol or 258 | color name string. If regexp, commands which match the regexp 259 | are highlighted with `guide-key/highlight-command-face'. If cons 260 | cell, commands which match the car regexp are highlighted with 261 | the cdr face or color." 262 | :type '(choice (regexp :tag "Regexp to highlight") 263 | (repeat (choice (regexp :tag "Regexp to highlight") 264 | (cons (regexp :tag "Regexp to highlight") 265 | (choice (face :tag "Face on command") 266 | (string :tag "Color name string")))))) 267 | :group 'guide-key) 268 | 269 | (defcustom guide-key/align-command-by-space-flag nil 270 | "*If non-nil, align guide buffer by space." 271 | :type 'boolean 272 | :group 'guide-key) 273 | 274 | (defcustom guide-key/popup-window-position 'right 275 | "*Position where guide buffer is popped up. 276 | This variable must be one of `right', `bottom', `left' and `top'." 277 | :type '(radio (const right) (const bottom) (const left) (const top)) 278 | :group 'guide-key) 279 | 280 | (defcustom guide-key/text-scale-amount 0 281 | "*Amount of scaling text in guide buffer. 282 | 283 | If positive number, the text becomes larger. If negative number, 284 | the text becomes smaller. Scale of the text is detemined by the 285 | value of variable `text-scale-mode-step'." 286 | :type 'float 287 | :group 'guide-key) 288 | 289 | (defcustom guide-key/recursive-key-sequence-flag nil 290 | "*If non-nil, check an input key sequence recursively. 291 | For example, if `guide-key/guide-key-sequence' includes \"C-x\", 292 | guide buffer is popped up when you input \"C-x r\", \"C-x 4\" and 293 | any other prefixes following \"C-x\"." 294 | :type 'boolean 295 | :group 'guide-key) 296 | 297 | (defface guide-key/prefix-command-face 298 | '((((class color) (background dark)) 299 | (:foreground "cyan")) 300 | (((class color) (background light)) 301 | (:foreground "blue"))) 302 | "Face for prefix commands to highlight" 303 | :group 'guide-key) 304 | 305 | (defface guide-key/highlight-command-face 306 | '((((class color) (background dark)) 307 | (:foreground "yellow")) 308 | (((class color) (background light)) 309 | (:foreground "orange red"))) 310 | "Face for commands to highlight" 311 | :group 'guide-key) 312 | 313 | (defface guide-key/key-face 314 | '((((class color) (background dark)) 315 | (:foreground "red")) 316 | (((class color) (background light)) 317 | (:foreground "dark green"))) 318 | "Face for keys following to a key sequence" 319 | :group 'guide-key) 320 | 321 | ;;; internal variables 322 | (defvar guide-key/polling-timer nil 323 | "Polling timer to check an input key sequence.") 324 | 325 | (defvar guide-key/idle-timer nil 326 | "Idle timer to wait before popping up guide buffer.") 327 | 328 | (defvar guide-key/guide-buffer-name " *guide-key*" 329 | "Buffer name of guide buffer.") 330 | 331 | (defvar guide-key/last-key-sequence-vector nil 332 | "Key sequence input at the last polling operation.") 333 | 334 | ;; or hook 335 | ;; (add-hook 'pre-command-hook 'guide-key/hook-command) 336 | ;; (setq pre-command-hook nil) 337 | ;; (add-hook 'post-command-hook 'guide-key/key-event) 338 | ;; (add-hook 'pre-command-hook 'show-this-command) 339 | 340 | ;;; functions 341 | ;;;###autoload 342 | (define-minor-mode guide-key-mode 343 | "Toggle guide key mode. 344 | 345 | In guide key mode, Guide following keys to an input key sequence 346 | automatically and dynamically. 347 | With a prefix argument ARG, enable guide key mode if ARG is 348 | positive, otherwise disable." 349 | :global t 350 | :lighter " Guide" 351 | (funcall (if guide-key-mode 352 | 'guide-key/turn-on-timer 353 | 'guide-key/turn-off-timer))) 354 | 355 | (defun guide-key/popup-function (&optional input) 356 | "Popup function called after delay of `guide-key/idle-delay' second." 357 | (let ((key-seq (or input (this-single-command-keys))) 358 | (regexp guide-key/highlight-command-regexp)) 359 | (let ((dsc-buf (current-buffer)) 360 | (max-width 0)) 361 | (with-current-buffer (get-buffer-create guide-key/guide-buffer-name) 362 | (unless truncate-lines (setq truncate-lines t)) ; don't fold line 363 | (when indent-tabs-mode (setq indent-tabs-mode nil)) ; don't use tab as white space 364 | (setq mode-line-format nil) 365 | (text-scale-set guide-key/text-scale-amount) 366 | (erase-buffer) 367 | (describe-buffer-bindings dsc-buf key-seq) 368 | (when (> (guide-key/format-guide-buffer key-seq regexp) 0) 369 | (guide-key/close-guide-buffer) 370 | (guide-key/popup-guide-buffer)))))) 371 | 372 | 373 | ;;; internal functions 374 | (defun guide-key/polling-function () 375 | "Polling function executed every `guide-key/polling-time' second." 376 | (let ((key-seq (this-single-command-keys))) 377 | (if (guide-key/popup-guide-buffer-p key-seq) 378 | (when (guide-key/update-guide-buffer-p key-seq) 379 | (guide-key/turn-on-idle-timer)) 380 | (guide-key/close-guide-buffer)) 381 | (setq guide-key/last-key-sequence-vector key-seq))) 382 | 383 | (defun guide-key/popup-guide-buffer () 384 | "Pop up guide buffer at `guide-key/popup-window-position'." 385 | (let ((last-config popwin:popup-last-config)) 386 | (apply 'popwin:popup-buffer (get-buffer guide-key/guide-buffer-name) 387 | :position guide-key/popup-window-position 388 | :noselect t 389 | (cond ((popwin:position-horizontal-p guide-key/popup-window-position) 390 | `(:width ,(guide-key/popup-window-size 'horizontal))) 391 | ((popwin:position-vertical-p guide-key/popup-window-position) 392 | `(:height ,(guide-key/popup-window-size))))) 393 | (setq popwin:popup-last-config last-config))) 394 | 395 | (defun guide-key/popup-window-size (&optional horizontal) 396 | "Return an enough height or width of popup window to display 397 | all key bindings in guide buffer. 398 | 399 | If HORIZONTAL is omitted or nil, return the height of popup 400 | window. Otherwise, return the width of popup window" 401 | (with-current-buffer (get-buffer guide-key/guide-buffer-name) 402 | (let ((margin (if horizontal 5 1)) 403 | (scale (expt text-scale-mode-step text-scale-mode-amount))) 404 | (if horizontal 405 | (ceiling (* scale (+ (guide-key/buffer-max-width) margin))) 406 | (ceiling (* scale (+ (count-lines (point-min) (point-max)) margin)))) 407 | ))) 408 | 409 | (defun guide-key/close-guide-buffer () 410 | "Close guide buffer." 411 | (when (eq popwin:popup-buffer (get-buffer guide-key/guide-buffer-name)) 412 | (popwin:close-popup-window)) 413 | (guide-key/turn-off-idle-timer) 414 | ) 415 | 416 | (add-hook 'pre-command-hook 'guide-key/close-guide-buffer) 417 | 418 | (defun guide-key/update-guide-buffer-p (key-seq) 419 | "Return t if guide buffer should be updated." 420 | (not (equal guide-key/last-key-sequence-vector key-seq))) 421 | 422 | (defun guide-key/popup-guide-buffer-p (key-seq) 423 | "Return t if guide buffer should be popped up." 424 | (and (> (length key-seq) 0) 425 | (or (eq guide-key/guide-key-sequence t) 426 | (member key-seq (guide-key/buffer-key-sequences)) 427 | (and guide-key/recursive-key-sequence-flag 428 | (guide-key/popup-guide-buffer-p (guide-key/vbutlast key-seq)))))) 429 | 430 | (defun guide-key/buffer-key-sequences () 431 | "Return a list of key sequences (vector representation) in current buffer." 432 | (let (lst) 433 | ;; global key sequences 434 | (dolist (ks guide-key/guide-key-sequence) 435 | (when (stringp ks) 436 | (setq lst (cons ks lst)))) 437 | ;; major-mode specific key sequences 438 | (setq lst (append (assoc-default major-mode guide-key/guide-key-sequence) lst)) 439 | ;; minor-mode specific key sequences 440 | (dolist (mmode minor-mode-list) 441 | (when (and (boundp mmode) (symbol-value mmode)) 442 | (setq lst (append (assoc-default mmode guide-key/guide-key-sequence) lst)))) 443 | ;; convert key sequences to vector representation 444 | (mapcar 'guide-key/convert-key-sequence-to-vector lst))) 445 | 446 | (defun guide-key/vbutlast (vec &optional n) 447 | "Return a copy of vector VEC with the last N elements removed." 448 | (vconcat (butlast (append vec nil) n))) 449 | 450 | (defun guide-key/convert-key-sequence-to-vector (key-seq) 451 | "Convert key sequence KEY-SEQ to vector representation. 452 | For example, both \"C-x r\" and \"\\C-xr\" are converted to [24 114]" 453 | (vconcat (read-kbd-macro key-seq))) 454 | 455 | (defun guide-key/turn-on-idle-timer () 456 | "Turn on an idle timer for popping up guide buffer." 457 | (when (null guide-key/idle-timer) 458 | (setq guide-key/idle-timer 459 | (run-with-idle-timer guide-key/idle-delay t 'guide-key/popup-function)) 460 | )) 461 | 462 | (defun guide-key/turn-off-idle-timer () 463 | "Turn off the idle timer." 464 | (when guide-key/idle-timer 465 | (cancel-timer guide-key/idle-timer)) 466 | (setq guide-key/idle-timer nil)) 467 | 468 | 469 | (defun guide-key/turn-on-timer () 470 | "Turn on a polling timer." 471 | (when (null guide-key/polling-timer) 472 | (setq guide-key/polling-timer 473 | (run-at-time t guide-key/polling-time 'guide-key/polling-function)))) 474 | 475 | (defun guide-key/turn-off-timer () 476 | "Turn off a polling timer." 477 | (cancel-timer guide-key/polling-timer) 478 | (setq guide-key/polling-timer nil)) 479 | 480 | (defun guide-key/format-guide-buffer (key-seq &optional regexp) 481 | "Format guide buffer. This function returns the number of following keys." 482 | (let ((fkey-list nil) ; list of (following-key space command) 483 | (fkey-str-list nil) ; fontified string of `fkey-list' 484 | (fkey-list-len 0) ; length of above lists 485 | (key-dsc (key-description key-seq))) 486 | (untabify (point-min) (point-max)) ; replace tab to space 487 | (goto-char (point-min)) 488 | ;; extract following keys from buffer bindings 489 | (while (re-search-forward 490 | (format "^%s \\([^ \t]+\\)\\([ \t]+\\)\\(\\(?:[^ \t\n]+ ?\\)+\\)$" (regexp-quote key-dsc)) nil t) 491 | (add-to-list 'fkey-list 492 | (list (match-string 1) (match-string 2) (match-string 3)) t)) 493 | (erase-buffer) 494 | (when (> (setq fkey-list-len (length fkey-list)) 0) 495 | ;; fontify following keys as string 496 | (setq fkey-str-list 497 | (loop for (key space command) in fkey-list 498 | collect (guide-key/fontified-string key space command regexp))) 499 | ;; insert a few following keys per line 500 | (guide-key/insert-following-key fkey-str-list 501 | (popwin:position-horizontal-p guide-key/popup-window-position)) 502 | (goto-char (point-min))) 503 | fkey-list-len)) 504 | 505 | (defun guide-key/insert-following-key (fkey-str-list horizontal) 506 | "Insert a few following keys per line. 507 | 508 | If HORIZONTAL is omitted or nil, assume that guide buffer is 509 | popped up at top or bottom. Otherwise, assume that guide buffer 510 | is popped up at left or right." 511 | (let* ((scale (expt text-scale-mode-step text-scale-mode-amount)) 512 | ;; Calculate the number of items per line 513 | (columns 514 | (if horizontal 515 | (ceiling (/ (* (length fkey-str-list) scale) 516 | (- (frame-height) (if tool-bar-mode 2 0) (if menu-bar-mode 1 0)))) 517 | (floor (/ (frame-width) 518 | (* (apply 'max (mapcar 'length fkey-str-list)) scale)))))) 519 | ;; Insert following keys by columns per line. 520 | (loop for fkey-str in fkey-str-list 521 | for column from 1 522 | do (insert fkey-str (if (= (mod column columns) 0) "\n" " "))) 523 | (align-regexp (point-min) (point-max) "\\(\\s-*\\) \\[" 1 1 t))) 524 | 525 | (defun guide-key/fontified-string (key space command &optional regexp) 526 | "Return fontified string of following key" 527 | (let ((highlight-face (guide-key/get-highlight-face command regexp))) 528 | (concat (propertize "[" 'face 'guide-key/key-face) 529 | (if highlight-face (propertize key 'face highlight-face) key) 530 | (propertize "]" 'face 'guide-key/key-face) 531 | (if guide-key/align-command-by-space-flag space " ") ; white space 532 | (if highlight-face (propertize command 'face highlight-face) command)))) 533 | 534 | (defun guide-key/get-highlight-face (string &optional regexp) 535 | "Return an appropriate face for highlighting STRING according 536 | to `guide-key/highlight-prefix-regexp' and 537 | `guide-key/highlight-command-regexp'. Return nil if an 538 | appropriate face is not found." 539 | (let ((regexp (or regexp guide-key/highlight-command-regexp))) 540 | ;; `guide-key/highlight-prefix-regexp' has the highest priority 541 | (if (string-match guide-key/highlight-prefix-regexp string) 542 | 'guide-key/prefix-command-face 543 | ;; Else look up the first match in `guide-key/highlight-command-regexp' 544 | (cond ((stringp regexp) 545 | (when (string-match regexp string) 546 | 'guide-key/highlight-command-face)) 547 | ((listp regexp) 548 | (loop for elm in regexp 549 | if (cond ((stringp elm) 550 | (when (string-match elm string) 551 | 'guide-key/highlight-command-face)) 552 | ((consp elm) 553 | (when (string-match (car elm) string) 554 | (if (stringp (cdr elm)) 555 | ;; anonymous face, see (info "(elisp)Faces") 556 | (list :foreground (cdr elm)) 557 | (cdr elm))))) 558 | return it))) 559 | ))) 560 | 561 | (defun guide-key/buffer-max-width () 562 | "Return max width in current buffer." 563 | (let ((buf-str (buffer-substring-no-properties (point-min) (point-max)))) 564 | (apply 'max (mapcar 'length (split-string buf-str "\n"))))) 565 | 566 | (defun guide-key/add-local-guide-key-sequence (key) 567 | (add-to-list (make-local-variable 'guide-key/guide-key-sequence) key)) 568 | 569 | (defun guide-key/add-local-highlight-command-regexp (regexp) 570 | (make-local-variable 'guide-key/highlight-command-regexp) 571 | (cond ((stringp guide-key/highlight-command-regexp) 572 | (setq guide-key/highlight-command-regexp 573 | (list regexp guide-key/highlight-command-regexp))) 574 | ((listp guide-key/highlight-command-regexp) 575 | (add-to-list 'guide-key/highlight-command-regexp regexp)))) 576 | 577 | ;;; key-chord hack 578 | (defadvice this-command-keys (after key-chord-hack disable) 579 | "Add key chord to the key sequence returned by `this-command-keys'. 580 | 581 | Original `this-command-keys' returns \"[key-chord]\" when you 582 | type any of key chords, so it is difficult to know which key 583 | chord is pressed. This advice enables to distinguish pressed key 584 | chord." 585 | (condition-case nil 586 | (if (equal ad-return-value [key-chord]) 587 | (let ((rkeys (recent-keys))) 588 | (setq ad-return-value 589 | (vector 'key-chord (aref rkeys (- (length rkeys) 2)) 590 | (aref rkeys (- (length rkeys) 1)))))) 591 | (error ""))) 592 | 593 | (defadvice this-command-keys-vector (after key-chord-hack disable) 594 | "Add key chord to the key sequence returned by `this-command-keys-vector'. 595 | 596 | Original `this-command-keys-vector' returns \"[key-chord]\" when you 597 | type any of key chords, so it is difficult to know which key 598 | chord is pressed. This advice enables to distinguish pressed key 599 | chord." 600 | (condition-case nil 601 | (if (equal ad-return-value [key-chord]) 602 | (let ((rkeys (recent-keys))) 603 | (setq ad-return-value 604 | (vector 'key-chord (aref rkeys (- (length rkeys) 2)) 605 | (aref rkeys (- (length rkeys) 1)))))) 606 | (error []))) 607 | 608 | (defun guide-key/key-chord-hack-on () 609 | "Turn on key-chord hack of guide-key. 610 | 611 | This hack *may be dangerous* because it advices primitive 612 | functions; this-command-keys and this-command-keys-vector." 613 | (interactive) 614 | (dolist (fn '(this-command-keys this-command-keys-vector)) 615 | (ad-enable-advice fn 'after 'key-chord-hack) 616 | (ad-activate fn)) 617 | (message "Turn on key-chord hack of guide-key")) 618 | 619 | (defun guide-key/key-chord-hack-off () 620 | "Turn off key-chord hack of guide-key." 621 | (interactive) 622 | (dolist (fn '(this-command-keys this-command-keys-vector)) 623 | (ad-disable-advice fn 'after 'key-chord-hack) 624 | (ad-activate fn)) 625 | (message "Turn off key-chord hack of guide-key")) 626 | 627 | ;;; debug 628 | (defun guide-key/message-events () 629 | "" 630 | (message (format "lce:%S tck:%S tckv:%S tsck:%S lie:%S uce:%S" 631 | last-command-event 632 | (this-command-keys) 633 | (this-command-keys-vector) 634 | (this-single-command-keys) 635 | last-input-event 636 | unread-command-events 637 | ))) 638 | ;; (setq ttt (run-at-time t 1 'guide-key/message-events)) 639 | ;; (cancel-timer ttt) 640 | 641 | (provide 'guide-key) 642 | ;;; guide-key.el ends here 643 | -------------------------------------------------------------------------------- /img/guide-key-example-org-anime.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kai2nenobu/guide-key/8f8b839f42edd53af13d588254f07727108ae312/img/guide-key-example-org-anime.gif -------------------------------------------------------------------------------- /img/guide-key-example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kai2nenobu/guide-key/8f8b839f42edd53af13d588254f07727108ae312/img/guide-key-example.png -------------------------------------------------------------------------------- /img/guide-key-example2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kai2nenobu/guide-key/8f8b839f42edd53af13d588254f07727108ae312/img/guide-key-example2.png -------------------------------------------------------------------------------- /img/guide-key-multiple-highlight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kai2nenobu/guide-key/8f8b839f42edd53af13d588254f07727108ae312/img/guide-key-multiple-highlight.png -------------------------------------------------------------------------------- /img/guide-key-package-install.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kai2nenobu/guide-key/8f8b839f42edd53af13d588254f07727108ae312/img/guide-key-package-install.png -------------------------------------------------------------------------------- /test/guide-key-test.el: -------------------------------------------------------------------------------- 1 | (require 'ert) 2 | (require 'undercover) 3 | (undercover "guide-key.el") 4 | 5 | (require 'guide-key) 6 | (eval-when-compile 7 | (require 'cl)) 8 | 9 | (defconst guide-key-test/global-keybindings 10 | '((1 . guide-key-test/global-keybinding1) 11 | (2 . guide-key-test/global-keybinding2))) 12 | 13 | (defconst guide-key-test/prefix-key (kbd "s-S-C-M-x")) 14 | 15 | (defmacro guide-key-test/deftest (name doc-string &rest body) 16 | (declare (indent 1) 17 | (doc-string 2)) 18 | `(ert-deftest ,(intern (concat "guide-key-test/" (symbol-name name))) () 19 | ,doc-string 20 | ;; setup 21 | (loop for (event . definition) in guide-key-test/global-keybindings 22 | do 23 | (global-set-key (vconcat guide-key-test/prefix-key (vector event)) definition)) 24 | ;; test 25 | ,@body 26 | ;; teardown 27 | (loop for (event . definition) in guide-key-test/global-keybindings 28 | do 29 | (global-unset-key (vconcat guide-key-test/prefix-key (vector event)))) 30 | )) 31 | 32 | (guide-key-test/deftest setup-test 33 | "Assert that setup is done successfully." 34 | (should (same-keymap-p (keymap-canonicalize (key-binding guide-key-test/prefix-key)) 35 | (cons 'keymap guide-key-test/global-keybindings)))) 36 | 37 | (ert-deftest guide-key-test/get-highlight-face () 38 | "Test of `guide-key/get-highlight-face'" 39 | (let ((guide-key/highlight-command-regexp 40 | '("rectangle" 41 | ("register" . font-lock-type-face) 42 | ("bookmark" . font-lock-warning-face) 43 | ("anonymous" . "hot pink") ; specify a color name 44 | )) 45 | (fixtures 46 | '(("Prefix Command" . guide-key/prefix-command-face) 47 | ("string-rectangle" . guide-key/highlight-command-face) 48 | ("jump-to-register" . font-lock-type-face) 49 | ("bookmark-jump" . font-lock-warning-face) 50 | ("copy-rectangle-to-register" . guide-key/highlight-command-face) 51 | ("anonymous-command" . (:foreground "hot pink")) ; anonymous face 52 | ("" . nil) 53 | )) 54 | actual) 55 | (loop for (input . expected) in fixtures 56 | do 57 | (setq actual (guide-key/get-highlight-face input)) 58 | (should (equal actual expected))) 59 | )) 60 | 61 | (defun same-keymap-p (keymap1 keymap2) 62 | "Return if two KEYMAPs are the same. 63 | 64 | This matcher ignores an order of alist (cdr of keymap)." 65 | (and (keymapp keymap1) (keymapp keymap2) 66 | (flet ((keymap-sorter (e1 e2) (< (car e1) (car e2)))) 67 | (equal (sort (cdr keymap1) 'keymap-sorter) 68 | (sort (cdr keymap2) 'keymap-sorter))))) 69 | 70 | --------------------------------------------------------------------------------