mirror of https://github.com/Chizi123/.emacs.d.git

Chizi123
2018-11-18 76bbd07de7add0f9d13c6914f158d19630fe2f62
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
;;; guide-key.el --- Guide the following key bindings automatically and dynamically
 
;; Copyright (C) 2012, 2013 Tsunenobu Kai
 
;; Author: Tsunenobu Kai <kai2nenobu@gmail.com>
;; URL: https://github.com/kai2nenobu/guide-key
;; Package-Version: 20150108.635
;; Version: 1.2.5
;; Package-Requires: ((dash "2.10.0") (popwin "0.3.0") (s "1.9.0"))
;; Keywords: help convenience
 
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
 
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.
 
;; You should have received a copy of the GNU General Public License
;; along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
;;; Commentary:
 
;; Overview:
;;
;; guide-key.el displays the available key bindings automatically and dynamically.
;; guide-key aims to be an alternative of one-key.el.
;;
;; Here are some features of this library.
;; - guide-key automatically pops up the keys following your favorite
;;   prefixes. Moreover, even if you change key bindings, guide-key follows the
;;   change dynamically.
;; - guide-key can highlight particular commands. This makes it easy to find a
;;   command you are looking for, and to learn its key binding.
;; - guide-key doesn't overwrite existing commands and key bindings, so there
;;   is no interference with `describe-key' and `describe-bindings'.
;;
;;
;; Installation:
;;
;; I added guide-key to MELPA. You can install guide-key with package.el.
;; Because guide-key depends on popwin.el, popwin.el is also installed.
;;
;; If you don't have package.el, please download popwin.el and guide-key.el
;; directly from https://github.com/m2ym/popwin-el and
;; https://github.com/kai2nenobu/guide-key, and then put them in your
;; `load-path'.
;;
;;
;; Basic usage:
;;
;; You just add your favorite prefix keys to `guide-key/guide-key-sequence'
;; as below.
;;
;;   (require 'guide-key)
;;   (setq guide-key/guide-key-sequence '("C-x r" "C-x 4"))
;;   (guide-key-mode 1) ; Enable guide-key-mode
;;
;; When you press these prefix keys, key bindings are automatically
;; popped up after a short delay (1 second by default).
;;
;; To activate guide-key for any key sequence instead of just the ones
;; listed above then use:
;;
;;   (setq guide-key/guide-key-sequence t)
;;
;; guide-key can highlight commands which match a specified regular expression.
;; Key bindings following "C-x r" are rectangle family, register family and
;; bookmark family.  If you want to highlight only rectangle family
;; commands, put this setting in your init.el.
;;
;;   (setq guide-key/highlight-command-regexp "rectangle")
;;
;; This feature makes it easy to find commands and learn their key bindings.
;; If you want to highlight all families, you can specify multiple regular
;; expressions and faces as below.
;;
;;   (setq guide-key/highlight-command-regexp
;;         '("rectangle"
;;           ("register" . font-lock-type-face)
;;           ("bookmark" . font-lock-warning-face)))
;;
;; If an element of `guide-key/highlight-command-regexp' is cons, its car
;; means a regular expression to highlight, and its cdr means a face put on
;; command names.
;;
;; Moreover, prefix commands are automatically highlighted.
;;
;; Depending on your level of emacs experience, you may want a shorter or
;; longer delay between pressing a key and the appearance of the guide
;; buffer.  This can be controlled by setting `guide-key/idle-delay':
;;
;;   (setq guide-key/idle-delay 0.1)
;;
;; The guide buffer is displayed only when you pause between keystrokes
;; for longer than this delay, so it will keep out of your way when you
;; are typing key sequences that you already know well.
;;
;; I've confirmed that guide-key works well in these environments.
;; - Emacs 24.2, Ubuntu 12.04 or Windows 7 64bit
;; - Emacs 23.3, Ubuntu 12.04 or Windows 7 64bit
;; - Emacs 22.3, Windows 7 64bit
;; - Emacs 24.3.1, OS X 10.9
;; If popwin works, I think guide-key will work as well. You can use
;; guide-key with Emacs working in terminal.
;;
;;
;; Advanced usage:
;;
;; It is bothering to add many prefixes to `guide-key/guide-key-sequence'.
;; `guide-key/recursive-key-sequence-flag' releases you from this problem.
;; If `guide-key/recursive-key-sequence-flag' is non-nil, guide-key checks a
;; input key sequence recursively. That is, if "C-x 8 ^" is an input key
;; sequence, guide-key checks whether `guide-key/guide-key-sequence' includes
;; "C-x 8" and "C-x".
;;
;; For example, if you configure as below,
;;
;;   (setq guide-key/guide-key-sequence '("C-x"))
;;   (setq guide-key/recursive-key-sequence-flag t)
;;
;; the guide buffer is popped up when you input "C-x r", "C-x 8" and
;; any other prefixes following "C-x".
;;
;;
;; You can add extra settings in a particular mode. Please use
;; `guide-key/add-local-guide-key-sequence',
;; `guide-key/add-local-highlight-command-regexp' and the hook of
;; that mode.
;;
;;
;; This code is a example of org-mode.
;;
;;   (defun guide-key/my-hook-function-for-org-mode ()
;;     (guide-key/add-local-guide-key-sequence "C-c")
;;     (guide-key/add-local-guide-key-sequence "C-c C-x")
;;     (guide-key/add-local-highlight-command-regexp "org-"))
;;   (add-hook 'org-mode-hook 'guide-key/my-hook-function-for-org-mode)
;;
;; In respect of `guide-key/guide-key-sequence', you can add mode specific key
;; sequences without `guide-key/add-local-guide-key-sequence'. For example,
;; configure as below.
;;
;;   (setq guide-key/guide-key-sequence
;;         '("C-x r" "C-x 4"
;;           (org-mode "C-c C-x")
;;           (outline-minor-mode "C-c @")))
;;
;; In this case, if the current major mode is `org-mode', guide key bindings
;; following "C-c C-x".  If `outline-minor-mode' is enabled, guide key bindings
;; following "C-c @".
;;
;;
;; `guide-key' can work with key-chord.el.  If you want to guide key bindings
;; following key chord, you need to execute
;; `guide-key/key-chord-hack-on'.  Then, add your favorite key chord to
;; `guide-key/guide-key-sequence' as below.
;;
;;   (key-chord-define global-map "@4" 'ctl-x-4-prefix)
;;
;;   (guide-key/key-chord-hack-on)
;;   (setq guide-key/guide-key-sequence '("<key-chord> @ 4" "<key-chord> 4 @"))
;;
;; If =guide-key/recursive-key-sequence-flag= is non-nil, more simple.
;;
;;   (guide-key/key-chord-hack-on)
;;   (setq guide-key/recursive-key-sequence-flag t)
;;   (setq guide-key/guide-key-sequence '("<key-chord>"))
;;
;; In this case, key bindings are popped up when you type any of key chords.
;;
;; This hack *may be dangerous* because it advices primitive functions;
;; `this-command-keys' and `this-command-keys-vector'.
;;
;;
;; Here are some functions and variables which control guide-key.
;; - `guide-key-mode':
;;   guide-key-mode is implemented as a minor mode.
;;   Excuting M-x guide-key-mode toggles whether guide-key is enabled or
;;   not.  Because guide-key-mode is a global minor mode, guide-key-mode is
;;   enabled in all buffers or disabled in all buffers.
;; - `guide-key/popup-window-position':
;;   This variable controls where a guide-key buffer is popped up. A value of
;;   this variable is one of `right', `bottom', `left', `top'. The default
;;   value is `right'.
;; - `guide-key/polling-time':
;;   This variable controls a polling time. The default value is 0.1 (in seconds).
;; - `guide-key/idle-delay':
;;   This variable controls the delay between starting a key sequence and
;;   popping up the guide buffer. The default value is 1.0 (in seconds),
;;   which means that guide-key will keep out of your way unless you hesitate
;;   in the middle of a key sequence .  Set this to 0.0 to revert to the old
;;   default behavior.
;; - `guide-key/text-scale-amount':
;;   This variable controls the size of text in guide buffer. The default
;;   value is 0 (it means default size in Emacs). If you want to enlarge
;;   text, set positive number. Otherwise, set negative number.
;;
;; Enjoy!
 
;;; Code:
 
(eval-when-compile
  (require 'cl)
  (require 'face-remap))
 
(require 'dash)
(require 'popwin)
(require 's)
 
;;; variables
(defgroup guide-key nil
  "Guide key bidings."
  :group 'help
  :prefix "guide-key/")
 
(defcustom guide-key/guide-key-sequence nil
  "*Key sequences to guide in `guide-key-mode'.
This variable is a list of string representation.
Both representations, like \"C-x r\" and \"\\C-xr\",
are allowed.
 
In addition, an element of this list can be a list whose car is
the symbol for a certain mode, and whose cdr is a list of key
sequences to consider only if that mode is active.
 
Set this variable to `t' to enable for any key sequence."
  :type '(repeat (choice (string :tag "Prefix key sequence")
                         (cons :tag "Mode specific sequence"
                               (symbol :tag "Symbol for mode")
                               (repeat (string :tag "Prefix key sequence")))))
  :group 'guide-key)
 
(defcustom guide-key/polling-time 0.1
  "*Polling time to check an input key sequence."
  :type 'float
  :group 'guide-key)
 
(defcustom guide-key/idle-delay 1.0
  "*Delay in seconds before guide buffer is displayed."
  :type 'float
  :group 'guide-key)
 
(defcustom guide-key/highlight-prefix-regexp "prefix"
  "*Regexp for prefix commands."
  :type 'regexp
  :group 'guide-key)
 
(defcustom guide-key/highlight-command-regexp nil
  "*Regexp for commands to highlight.
If a command name matches this regexp, it is highlighted with
`guide-key/highlight-command-face'.
 
This variable can be a list and its element is either a regexp or
a cons cell, its car is a regexp and its cdr is face symbol or
color name string.  If regexp, commands which match the regexp
are highlighted with `guide-key/highlight-command-face'.  If cons
cell, commands which match the car regexp are highlighted with
the cdr face or color."
  :type '(choice (regexp :tag "Regexp to highlight")
                 (repeat (choice (regexp :tag "Regexp to highlight")
                                 (cons (regexp :tag "Regexp to highlight")
                                       (choice (face   :tag "Face on command")
                                               (string :tag "Color name string"))))))
  :group 'guide-key)
 
(defcustom guide-key/align-command-by-space-flag nil
  "*If non-nil, align guide buffer by space."
  :type 'boolean
  :group 'guide-key)
 
(defcustom guide-key/popup-window-position 'right
  "*Position where guide buffer is popped up.
This variable must be one of `right', `bottom', `left' and `top'."
  :type '(radio (const right) (const bottom) (const left) (const top))
  :group 'guide-key)
 
(defcustom guide-key/text-scale-amount 0
  "*Amount of scaling text in guide buffer.
 
If positive number, the text becomes larger.  If negative number,
the text becomes smaller.  Scale of the text is detemined by the
value of variable `text-scale-mode-step'."
  :type 'float
  :group 'guide-key)
 
(defcustom guide-key/recursive-key-sequence-flag nil
  "*If non-nil, check an input key sequence recursively.
For example, if `guide-key/guide-key-sequence' includes \"C-x\",
guide buffer is popped up when you input \"C-x r\", \"C-x 4\" and
any other prefixes following \"C-x\"."
  :type 'boolean
  :group 'guide-key)
 
(defface guide-key/prefix-command-face
  '((((class color) (background dark))
     (:foreground "cyan"))
    (((class color) (background light))
     (:foreground "blue")))
  "Face for prefix commands to highlight"
  :group 'guide-key)
 
(defface guide-key/highlight-command-face
  '((((class color) (background dark))
     (:foreground "yellow"))
    (((class color) (background light))
     (:foreground "orange red")))
  "Face for commands to highlight"
  :group 'guide-key)
 
(defface guide-key/key-face
  '((((class color) (background dark))
     (:foreground "red"))
    (((class color) (background light))
     (:foreground "dark green")))
  "Face for keys following to a key sequence"
  :group 'guide-key)
 
;;; internal variables
(defvar guide-key/polling-timer nil
  "Polling timer to check an input key sequence.")
 
(defvar guide-key/idle-timer nil
  "Idle timer to wait before popping up guide buffer.")
 
(defvar guide-key/guide-buffer-name " *guide-key*"
  "Buffer name of guide buffer.")
 
(defvar guide-key/last-key-sequence-vector nil
  "Key sequence input at the last polling operation.")
 
;; or hook
;; (add-hook 'pre-command-hook 'guide-key/hook-command)
;; (setq pre-command-hook nil)
;; (add-hook 'post-command-hook 'guide-key/key-event)
;; (add-hook 'pre-command-hook 'show-this-command)
 
;;; functions
;;;###autoload
(define-minor-mode guide-key-mode
  "Toggle guide key mode.
 
In guide key mode, Guide following keys to an input key sequence
automatically and dynamically.
With a prefix argument ARG, enable guide key mode if ARG is
positive, otherwise disable."
  :global t
  :lighter " Guide"
  (funcall (if guide-key-mode
               'guide-key/turn-on-timer
             'guide-key/turn-off-timer)))
 
(defun guide-key/popup-function (&optional input)
  "Popup function called after delay of `guide-key/idle-delay' second."
  (let ((key-seq (or input (this-single-command-keys)))
        (regexp guide-key/highlight-command-regexp))
    (let ((dsc-buf (current-buffer))
      (max-width 0))
      (with-current-buffer (get-buffer-create guide-key/guide-buffer-name)
    (unless truncate-lines (setq truncate-lines t))   ; don't fold line
    (when indent-tabs-mode (setq indent-tabs-mode nil)) ; don't use tab as white space
    (setq mode-line-format nil)
    (text-scale-set guide-key/text-scale-amount)
    (erase-buffer)
    (describe-buffer-bindings dsc-buf key-seq)
    (when (> (guide-key/format-guide-buffer key-seq regexp) 0)
      (guide-key/close-guide-buffer)
      (guide-key/popup-guide-buffer))))))
 
 
;;; internal functions
(defun guide-key/polling-function ()
  "Polling function executed every `guide-key/polling-time' second."
  (let ((key-seq (this-single-command-keys)))
    (if (guide-key/popup-guide-buffer-p key-seq)
        (when (guide-key/update-guide-buffer-p key-seq)
          (guide-key/turn-on-idle-timer))
      (guide-key/close-guide-buffer))
    (setq guide-key/last-key-sequence-vector key-seq)))
 
(defun guide-key/popup-guide-buffer ()
  "Pop up guide buffer at `guide-key/popup-window-position'."
  (let ((last-config popwin:popup-last-config))
    (apply 'popwin:popup-buffer (get-buffer guide-key/guide-buffer-name)
           :position guide-key/popup-window-position
           :noselect t
           (cond ((popwin:position-horizontal-p guide-key/popup-window-position)
                  `(:width ,(guide-key/popup-window-size 'horizontal)))
                 ((popwin:position-vertical-p guide-key/popup-window-position)
                  `(:height ,(guide-key/popup-window-size)))))
    (setq popwin:popup-last-config last-config)))
 
(defun guide-key/popup-window-size (&optional horizontal)
  "Return an enough height or width of popup window to display
all key bindings in guide buffer.
 
If HORIZONTAL is omitted or nil, return the height of popup
window.  Otherwise, return the width of popup window"
  (with-current-buffer (get-buffer guide-key/guide-buffer-name)
    (let ((margin (if horizontal 5 1))
          (scale (expt text-scale-mode-step text-scale-mode-amount)))
      (if horizontal
          (ceiling (* scale (+ (guide-key/buffer-max-width) margin)))
        (ceiling (* scale (+ (count-lines (point-min) (point-max)) margin))))
      )))
 
(defun guide-key/close-guide-buffer ()
  "Close guide buffer."
  (when (eq popwin:popup-buffer (get-buffer guide-key/guide-buffer-name))
    (popwin:close-popup-window))
  (guide-key/turn-off-idle-timer)
  )
 
(add-hook 'pre-command-hook 'guide-key/close-guide-buffer)
 
(defun guide-key/update-guide-buffer-p (key-seq)
  "Return t if guide buffer should be updated."
  (not (equal guide-key/last-key-sequence-vector key-seq)))
 
(defun guide-key/popup-guide-buffer-p (key-seq)
  "Return t if guide buffer should be popped up."
  (and (> (length key-seq) 0)
       (or (eq guide-key/guide-key-sequence t)
           (member key-seq (guide-key/buffer-key-sequences))
           (and guide-key/recursive-key-sequence-flag
                (guide-key/popup-guide-buffer-p (guide-key/vbutlast key-seq))))))
 
(defun guide-key/buffer-key-sequences ()
  "Return a list of key sequences (vector representation) in current buffer."
  (let (lst)
    ;; global key sequences
    (dolist (ks guide-key/guide-key-sequence)
      (when (stringp ks)
        (setq lst (cons ks lst))))
    ;; major-mode specific key sequences
    (setq lst (append (assoc-default major-mode guide-key/guide-key-sequence) lst))
    ;; minor-mode specific key sequences
    (dolist (mmode minor-mode-list)
      (when (and (boundp mmode) (symbol-value mmode))
        (setq lst (append (assoc-default mmode guide-key/guide-key-sequence) lst))))
    ;; convert key sequences to vector representation
    (mapcar 'guide-key/convert-key-sequence-to-vector lst)))
 
(defun guide-key/vbutlast (vec &optional n)
  "Return a copy of vector VEC with the last N elements removed."
  (vconcat (butlast (append vec nil) n)))
 
(defun guide-key/convert-key-sequence-to-vector (key-seq)
  "Convert key sequence KEY-SEQ to vector representation.
For example, both \"C-x r\" and \"\\C-xr\" are converted to [24 114]"
  (vconcat (read-kbd-macro key-seq)))
 
(defun guide-key/turn-on-idle-timer ()
  "Turn on an idle timer for popping up guide buffer."
  (when (null guide-key/idle-timer)
    (setq guide-key/idle-timer
          (run-with-idle-timer guide-key/idle-delay t 'guide-key/popup-function))
    ))
 
(defun guide-key/turn-off-idle-timer ()
  "Turn off the idle timer."
  (when guide-key/idle-timer
    (cancel-timer guide-key/idle-timer))
  (setq guide-key/idle-timer nil))
 
 
(defun guide-key/turn-on-timer ()
  "Turn on a polling timer."
  (when (null guide-key/polling-timer)
    (setq guide-key/polling-timer
          (run-at-time t guide-key/polling-time 'guide-key/polling-function))))
 
(defun guide-key/turn-off-timer ()
  "Turn off a polling timer."
  (cancel-timer guide-key/polling-timer)
  (setq guide-key/polling-timer nil))
 
(defun guide-key/format-guide-buffer (key-seq &optional regexp)
  "Format guide buffer. This function returns the number of following keys."
  (let ((fkey-list nil)      ; list of (following-key space command)
        (fkey-str-list nil)  ; fontified string of `fkey-list'
        (fkey-list-len 0)    ; length of above lists
        (key-dsc (key-description key-seq)))
    (untabify (point-min) (point-max))  ; replace tab to space
    (goto-char (point-min))
    ;; extract following keys from buffer bindings
    (while (re-search-forward
            (format "^%s \\([^ \t]+\\)\\([ \t]+\\)\\(\\(?:[^ \t\n]+ ?\\)+\\)$" (regexp-quote key-dsc)) nil t)
      (add-to-list 'fkey-list
                   (list (match-string 1) (match-string 2) (match-string 3)) t))
    (erase-buffer)
    (when (> (setq fkey-list-len (length fkey-list)) 0)
      ;; fontify following keys as string
      (setq fkey-str-list
            (loop for (key space command) in fkey-list
                  collect (guide-key/fontified-string key space command regexp)))
      ;; insert a few following keys per line
      (guide-key/insert-following-key fkey-str-list
                                      (popwin:position-horizontal-p guide-key/popup-window-position))
      (goto-char (point-min)))
    fkey-list-len))
 
(defun guide-key/insert-following-key (fkey-str-list horizontal)
  "Insert a few following keys per line.
 
If HORIZONTAL is omitted or nil, assume that guide buffer is
popped up at top or bottom. Otherwise, assume that guide buffer
is popped up at left or right."
  (let* ((scale (expt text-scale-mode-step text-scale-mode-amount))
         ;; Calculate the number of items per line
         (columns
          (if horizontal
              (ceiling (/ (* (length fkey-str-list) scale)
                          (- (frame-height) (if tool-bar-mode 2 0) (if menu-bar-mode 1 0))))
            (floor (/ (frame-width)
                      (* (apply 'max (mapcar 'length fkey-str-list)) scale))))))
    ;; Insert following keys by columns per line.
    (loop for fkey-str in fkey-str-list
          for column from 1
          do (insert fkey-str (if (= (mod column columns) 0) "\n" " ")))
    (align-regexp (point-min) (point-max) "\\(\\s-*\\) \\[" 1 1 t)))
 
(defun guide-key/fontified-string (key space command &optional regexp)
  "Return fontified string of following key"
  (let ((highlight-face (guide-key/get-highlight-face command regexp)))
    (concat (propertize "[" 'face 'guide-key/key-face)
            (if highlight-face (propertize key 'face highlight-face) key)
            (propertize "]" 'face 'guide-key/key-face)
            (if guide-key/align-command-by-space-flag space " ") ; white space
            (if highlight-face (propertize command 'face highlight-face) command))))
 
(defun guide-key/get-highlight-face (string &optional regexp)
  "Return an appropriate face for highlighting STRING according
to `guide-key/highlight-prefix-regexp' and
`guide-key/highlight-command-regexp'. Return nil if an
appropriate face is not found."
  (let ((regexp (or regexp guide-key/highlight-command-regexp)))
    ;; `guide-key/highlight-prefix-regexp' has the highest priority
    (if (string-match guide-key/highlight-prefix-regexp string)
        'guide-key/prefix-command-face
      ;; Else look up the first match in `guide-key/highlight-command-regexp'
      (cond ((stringp regexp)
             (when (string-match regexp string)
               'guide-key/highlight-command-face))
            ((listp regexp)
             (loop for elm in regexp
                   if (cond ((stringp elm)
                             (when (string-match elm string)
                               'guide-key/highlight-command-face))
                            ((consp elm)
                             (when (string-match (car elm) string)
                               (if (stringp (cdr elm))
                                   ;; anonymous face, see (info "(elisp)Faces")
                                   (list :foreground (cdr elm))
                                 (cdr elm)))))
                   return it)))
      )))
 
(defun guide-key/buffer-max-width ()
  "Return max width in current buffer."
  (let ((buf-str (buffer-substring-no-properties (point-min) (point-max))))
    (apply 'max (mapcar 'length (split-string buf-str "\n")))))
 
(defun guide-key/add-local-guide-key-sequence (key)
  (add-to-list (make-local-variable 'guide-key/guide-key-sequence) key))
 
(defun guide-key/add-local-highlight-command-regexp (regexp)
  (make-local-variable 'guide-key/highlight-command-regexp)
  (cond ((stringp guide-key/highlight-command-regexp)
         (setq guide-key/highlight-command-regexp
               (list regexp guide-key/highlight-command-regexp)))
        ((listp guide-key/highlight-command-regexp)
         (add-to-list 'guide-key/highlight-command-regexp regexp))))
 
;;; key-chord hack
(defadvice this-command-keys (after key-chord-hack disable)
  "Add key chord to the key sequence returned by `this-command-keys'.
 
Original `this-command-keys' returns \"[key-chord]\" when you
type any of key chords, so it is difficult to know which key
chord is pressed.  This advice enables to distinguish pressed key
chord."
  (condition-case nil
      (if (equal ad-return-value [key-chord])
          (let ((rkeys (recent-keys)))
            (setq ad-return-value
                  (vector 'key-chord (aref rkeys (- (length rkeys) 2))
                          (aref rkeys (- (length rkeys) 1))))))
    (error "")))
 
(defadvice this-command-keys-vector (after key-chord-hack disable)
  "Add key chord to the key sequence returned by `this-command-keys-vector'.
 
Original `this-command-keys-vector' returns \"[key-chord]\" when you
type any of key chords, so it is difficult to know which key
chord is pressed.  This advice enables to distinguish pressed key
chord."
  (condition-case nil
      (if (equal ad-return-value [key-chord])
          (let ((rkeys (recent-keys)))
            (setq ad-return-value
                  (vector 'key-chord (aref rkeys (- (length rkeys) 2))
                          (aref rkeys (- (length rkeys) 1))))))
    (error [])))
 
(defun guide-key/key-chord-hack-on ()
  "Turn on key-chord hack of guide-key.
 
This hack *may be dangerous* because it advices primitive
functions; this-command-keys and this-command-keys-vector."
  (interactive)
  (dolist (fn '(this-command-keys this-command-keys-vector))
    (ad-enable-advice fn 'after 'key-chord-hack)
    (ad-activate fn))
  (message "Turn on key-chord hack of guide-key"))
 
(defun guide-key/key-chord-hack-off ()
  "Turn off key-chord hack of guide-key."
  (interactive)
  (dolist (fn '(this-command-keys this-command-keys-vector))
    (ad-disable-advice fn 'after 'key-chord-hack)
    (ad-activate fn))
  (message "Turn off key-chord hack of guide-key"))
 
;;; debug
(defun guide-key/message-events ()
  ""
  (message (format "lce:%S tck:%S tckv:%S tsck:%S lie:%S uce:%S"
                   last-command-event
                   (this-command-keys)
                   (this-command-keys-vector)
                   (this-single-command-keys)
                   last-input-event
                   unread-command-events
                   )))
;; (setq ttt (run-at-time t 1 'guide-key/message-events))
;; (cancel-timer ttt)
 
(provide 'guide-key)
;;; guide-key.el ends here