From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.org!not-for-mail From: Tassilo Horn Newsgroups: gmane.emacs.devel Subject: A simple implementation of context-sensitive keys Date: Wed, 10 Sep 2008 09:17:27 +0200 Message-ID: <87y72079mg.fsf@thinkpad.tsdh.de> NNTP-Posting-Host: lo.gmane.org Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii X-Trace: ger.gmane.org 1221031187 29802 80.91.229.12 (10 Sep 2008 07:19:47 GMT) X-Complaints-To: usenet@ger.gmane.org NNTP-Posting-Date: Wed, 10 Sep 2008 07:19:47 +0000 (UTC) To: emacs-devel@gnu.org Original-X-From: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Wed Sep 10 09:20:42 2008 Return-path: Envelope-to: ged-emacs-devel@m.gmane.org Original-Received: from lists.gnu.org ([199.232.76.165]) by lo.gmane.org with esmtp (Exim 4.50) id 1KdK0I-00030U-0d for ged-emacs-devel@m.gmane.org; Wed, 10 Sep 2008 09:20:42 +0200 Original-Received: from localhost ([127.0.0.1]:51707 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1KdJzH-0001ex-H9 for ged-emacs-devel@m.gmane.org; Wed, 10 Sep 2008 03:19:39 -0400 Original-Received: from mailman by lists.gnu.org with tmda-scanned (Exim 4.43) id 1KdJzC-0001eD-M5 for emacs-devel@gnu.org; Wed, 10 Sep 2008 03:19:34 -0400 Original-Received: from exim by lists.gnu.org with spam-scanned (Exim 4.43) id 1KdJzB-0001dh-H4 for emacs-devel@gnu.org; Wed, 10 Sep 2008 03:19:33 -0400 Original-Received: from [199.232.76.173] (port=35317 helo=monty-python.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1KdJzB-0001de-AS for emacs-devel@gnu.org; Wed, 10 Sep 2008 03:19:33 -0400 Original-Received: from mx20.gnu.org ([199.232.41.8]:39150) by monty-python.gnu.org with esmtps (TLS-1.0:RSA_AES_256_CBC_SHA1:32) (Exim 4.60) (envelope-from ) id 1KdJzA-0008L7-No for emacs-devel@gnu.org; Wed, 10 Sep 2008 03:19:32 -0400 Original-Received: from deliver.uni-koblenz.de ([141.26.64.15]) by mx20.gnu.org with esmtp (Exim 4.60) (envelope-from ) id 1KdJz9-0004Ow-V1 for emacs-devel@gnu.org; Wed, 10 Sep 2008 03:19:32 -0400 Original-Received: from localhost (localhost [127.0.0.1]) by deliver.uni-koblenz.de (Postfix) with ESMTP id CE7A77896826 for ; Wed, 10 Sep 2008 09:19:30 +0200 (CEST) Original-Received: from deliver.uni-koblenz.de ([127.0.0.1]) by localhost (deliver.uni-koblenz.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id 17352-03 for ; Wed, 10 Sep 2008 09:19:29 +0200 (CEST) X-CHKRCPT: Envelopesender vrfy tassilo@member.fsf.org Original-Received: from thinkpad.tsdh.de (dhcp188.uni-koblenz.de [141.26.71.188]) by deliver.uni-koblenz.de (Postfix) with ESMTP id A657B7854985 for ; Wed, 10 Sep 2008 09:19:29 +0200 (CEST) Mail-Copies-To: never Mail-Followup-To: emacs-devel@gnu.org User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/23.0.60 (gnu/linux) X-Virus-Scanned: amavisd-new at uni-koblenz.de X-detected-kernel: by mx20.gnu.org: Linux 2.6, seldom 2.4 (older, 4) X-detected-kernel: by monty-python.gnu.org: Linux 2.6, seldom 2.4 (older, 4) X-BeenThere: emacs-devel@gnu.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: "Emacs development discussions." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Original-Sender: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Errors-To: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Xref: news.gmane.org gmane.emacs.devel:103749 Archived-At: 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