emacs-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

A simple implementation of context-sensitive keys


From: Tassilo Horn
Subject: A simple implementation of context-sensitive keys
Date: Wed, 10 Sep 2008 09:17:27 +0200
User-agent: Gnus/5.13 (Gnus v5.13) Emacs/23.0.60 (gnu/linux)

Hi all,

to get the full power out of emacs, I use quite a lot of different minor
modes with a ton of home-brewn functions.  To make them quickly
accessible, I've bound them to some key.  But "good" keys are rare, and
so I started writing special dispatching commands which DTRT dependend
on the context they're used in.  But that's quite cumbersome, because
ideally you have to recognize each command which would normally be bound
to the key, too.

So I've come up with this macro, which creates such a dispatching
command automagically.  If none of the context predicates match, the
default command bound to the key will be executed.

--8<---------------cut here---------------start------------->8---
(defmacro define-context-key (mode key preds-funs)
  "Bind KEY to an anonymous dispatching command in MODE.
PREDS-FUNS is an alist with elements of the form

    (PREDICATE . FUNCTION).

The dispatching command calls the first PREDICATE, and if that
returns non-nil, it'll call the associated FUNCTION.  If the
first PREDICATE returns nil, the next one will be tested and so
on.

If no PREDICATE matches and KEY is normally bound in MODE, the
corresponding command will be executed.

If KEY isn't normally bound in MODE, MODE will be disabled
temporally (to prevent an infinite recursion) and the function
which is then bound to KEY will be called."
  (let* ((keymap        (intern (concat (symbol-name mode) "-map")))
         (default-fun   (lookup-key (symbol-value keymap) (eval key)))
         (block-name    (gensym))
         (iter-var-name (gensym)))
    `(define-key ,keymap ,key
       (lambda ()       
         (interactive)
         (block ,block-name
           (dolist (,iter-var-name (quote ,preds-funs))
             (when (funcall (car ,iter-var-name))
               (call-interactively (cdr ,iter-var-name))
               (return-from ,block-name)))
           (if (quote ,default-fun)
               (call-interactively (quote ,default-fun))
             (let (,mode)
               (call-interactively (key-binding ,key)))))))))
--8<---------------cut here---------------end--------------->8---

Here's a usage example: I'm used to structure source code files with
`outline-minor-mode'.  But the default keys of this mode are too hard to
type and remember.  (I only use `outline-toggle-children' anyway.)  So
instead of binding that command to some other key, I use my macro to
make TAB context-sensitive:

--8<---------------cut here---------------start------------->8---
(defun outline-context-p ()
  (save-excursion
    (goto-char (line-beginning-position))
    (looking-at outline-regexp)))

(define-context-key outline-minor-mode
  (kbd "TAB")
  ((outline-context-p . outline-toggle-children)))
--8<---------------cut here---------------end--------------->8---
 
`outline-context-p' returns non-nil when point is on a outline heading.
Now the TAB key calls `outline-toggle-children' if I'm on such a
headline.  If not, `outline-minor-mode' will be disabled temporally
(cause `outline-minor-mode' doesn't define a command bound to TAB on its
own) and the next mode will get it's chance, i.e. the code will be
indented according to mode.

What do you think of it?

I use it for some hours now, so it's only very briefly tested, but it
seems to DTRT.  Of course, this cannot go into the emacs core because it
uses `cl' capabilities, but IMO a feature like that could be of great
benefit to users.

BTW: The idea and the mechanism how to get the command that would have
been executed normally is stolen from Carsten Dominik's fabulous
org-mode package (or more accurately `orgstruct-mode').  And there're
other packages (yasnippet comes into mind), which implement such a
feature on their own, too.  So a more general concept of context
sensitive keys seems sensible.

Bye,
Tassilo




reply via email to

[Prev in Thread] Current Thread [Next in Thread]