From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.org!not-for-mail From: "Drew Adams" Newsgroups: gmane.emacs.devel Subject: RE: customizing key definitions with Customize Date: Tue, 13 May 2008 22:24:09 -0700 Message-ID: <005c01c8b582$bcf2bde0$0200a8c0@us.oracle.com> References: <000301c8b39e$ded16a50$0200a8c0@us.oracle.com><48276D10.6070701@gmail.com><001201c8b3b6$5aa2af10$0200a8c0@us.oracle.com><482775D8.20704@gmail.com> <001501c8b3bb$0e33c830$0200a8c0@us.oracle.com> NNTP-Posting-Host: lo.gmane.org Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="----=_NextPart_000_005D_01C8B548.1093E5E0" X-Trace: ger.gmane.org 1210742679 26313 80.91.229.12 (14 May 2008 05:24:39 GMT) X-Complaints-To: usenet@ger.gmane.org NNTP-Posting-Date: Wed, 14 May 2008 05:24:39 +0000 (UTC) To: Original-X-From: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Wed May 14 07:25:15 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 1Jw9UH-0003nj-8E for ged-emacs-devel@m.gmane.org; Wed, 14 May 2008 07:25:13 +0200 Original-Received: from localhost ([127.0.0.1]:38959 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1Jw9TX-0004Xv-RO for ged-emacs-devel@m.gmane.org; Wed, 14 May 2008 01:24:27 -0400 Original-Received: from mailman by lists.gnu.org with tmda-scanned (Exim 4.43) id 1Jw9TO-0004XX-KJ for emacs-devel@gnu.org; Wed, 14 May 2008 01:24:18 -0400 Original-Received: from exim by lists.gnu.org with spam-scanned (Exim 4.43) id 1Jw9TK-0004Wp-S3 for emacs-devel@gnu.org; Wed, 14 May 2008 01:24:16 -0400 Original-Received: from [199.232.76.173] (port=52621 helo=monty-python.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1Jw9TK-0004Wj-Cw for emacs-devel@gnu.org; Wed, 14 May 2008 01:24:14 -0400 Original-Received: from agminet01.oracle.com ([141.146.126.228]:48785) by monty-python.gnu.org with esmtps (TLS-1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.60) (envelope-from ) id 1Jw9TJ-0003as-Mr for emacs-devel@gnu.org; Wed, 14 May 2008 01:24:14 -0400 Original-Received: from agmgw2.us.oracle.com (agmgw2.us.oracle.com [152.68.180.213]) by agminet01.oracle.com (Switch-3.2.4/Switch-3.1.7) with ESMTP id m4E5OBkN004788 for ; Wed, 14 May 2008 00:24:11 -0500 Original-Received: from acsmt350.oracle.com (acsmt350.oracle.com [141.146.40.150]) by agmgw2.us.oracle.com (Switch-3.2.0/Switch-3.2.0) with ESMTP id m4E3MU5K015117 for ; Tue, 13 May 2008 23:24:10 -0600 Original-Received: from inet-141-146-46-1.oracle.com by acsmt350.oracle.com with ESMTP id 3672030301210742645; Tue, 13 May 2008 22:24:05 -0700 Original-Received: from dradamslap1 (/141.144.72.233) by bhmail.oracle.com (Oracle Beehive Gateway v4.0) with ESMTP ; Tue, 13 May 2008 22:24:04 -0700 X-Mailer: Microsoft Office Outlook 11 In-Reply-To: <001501c8b3bb$0e33c830$0200a8c0@us.oracle.com> X-MimeOLE: Produced By Microsoft MimeOLE V6.00.2900.3198 Thread-Index: AcizuB9E/BkOdqKkQ+ubktXDmi7uzwAArQ+wABF7kOA= X-Brightmail-Tracker: AAAAAQAAAAI= X-Brightmail-Tracker: AAAAAQAAAAI= X-Whitelist: TRUE X-Whitelist: TRUE X-detected-kernel: by monty-python.gnu.org: Linux 2.4-2.6 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:97118 Archived-At: This is a multi-part message in MIME format. ------=_NextPart_000_005D_01C8B548.1093E5E0 Content-Type: text/plain; charset="iso-8859-1" Content-Transfer-Encoding: quoted-printable > > >> I like the idea, but can't it be taken one step further:=20 > > >> Wouldn't it be nice with a "sparse-keymap widget"? And then > > >> of course `customize-sparse-keymap'. > > >> > > >> Or have you already done this too, Drew? > > >=20 > > > No, I haven't taken it further than what I wrote. But I=20 > > > don't really know what you have in mind. Perhaps you can > > > elaborate or go further with the idea yourself? > >=20 > > I was just thinking `customize-sparse-keymap'. That would=20 > > bring up the current bindings in that keymap in a custom > > buffer using widgets similar to your `key-definition' widget. >=20 > I see. Yes, that could be easily done. Not quite as easily as I thought, at least for me. ;-) I did take a look, however, and came up with the following code = (attached). There are no doubt more elegant and more direct ways to do this than to = use `substitute-command-keys' and `eval'. Probably something like = `map-keymap' could be used, but its automatic ancestor keymap recursion scared me off.=20 The approach I used is certainly ugly and perhaps fragile, but it seems = to work. It works for menu maps also. It excludes prefix keys. It special-cases `mouse-face' and `ignore-event', since they are not in fact commands. = (Hence, the `key-definition' widget is different from what I sent before - it no = longer requires the `Command' sexp to be a command.) If nothing else, this gives an idea of what a UI for customizing keymaps = might be like. Please try it out, as follows: Command `custom-create-keymap-option' creates a user option of the same = name as its keymap argument, but with `-defs' appended. E.g., M-x custom-create-keymap-option RET bookmark-map Completion is available for keymap-valued symbols. Then you can = customize the new option that corresponds to the keymap: M-x customize-option RET bookmark-map-defs Note that, as I mentioned before, you can specify a command to remap = instead of a key to bind (use Value Menu). And yes, the keymap itself is modified when you change key definitions = in Customize - the option's :set function does that.=20 However, the option is currently decoupled from the keymap to some = extent: The :set function currently just does `define-key' for each of its key = definitions. It does not also reinitialize (empty out) the keymap so that it will = have only those definitions. That could be done, but it's worth thinking about = whether that is always what is wanted (probably). This means that, currently: If you change the command part of a key = definition, the same key becomes bound to the new command. But if you change the key = part of a key definition, the new key becomes bound to the same command - but = the old key is not unbound from the command. Similarly, DEL deletes the key = definition from the user option, but it does not unbind the key in the keymap. IOW, = you are editing the option, and the :set function for the option currently just = does `define-key' for whatever definitions are present. Also, in a new Emacs session, after saving the option, there is nothing = that automatically defines the corresponding keymap from the option. = Currently, the library that defines the keymap would need to explicitly initialize it = from the option, in order for the saved value to be picked up in a future = session.=20 In Icicles, for instance, the bindings are picked up from the option = whenever you enter Icicle mode. In Icicles, I use code similar to what I sent = previously, which provides users only some of the key definitions, by default. It is = not my intention to invite users to customize the whole keymap (but they are of = course able to do that via INS). That I think is a useful part of this approach of having an option = separate from the keymap: Whereas `custom-create-keymap-option' blindly includes all = of a keymap's bindings in the option it creates, libraries could instead = choose to provide a hand-rolled (or pruned) option that includes only some of the bindings. Defining keymaps from their corresponding options could perhaps be built = into Emacs somehow - either generally or just for specific contexts, such as = minor mode keymaps (e.g. via macro `define-minor-mode'). The code responsible = for this, whether automatic or not, would check whether a keymap option = existed and, if so, would use it to define the keymap. There are lots of possibilities, depending on what uses are seen for = something like this. While testing the code, BTW, I uncovered 2 or 3 Emacs bugs. These are = the bug report threads: 1. "kbd returns wrong value" 2. "substitute-command-keys incorrect for self-insert-command" 3. "\ is considered whitespace syntax in Lisp mode?" (maybe only a doc = bug - not clear to me, anyway) #1 is a bug in `edmacro-parse-keys' (or in the `Describe' menu, = depending on how you look at it). It causes a bogus entry if you try `M-x custom-create-keymap-option menu-bar-help-menu', because `' has a space in it. Related to #2 and #3: if you try `custom-create-keymap-option' on a = non-sparse keymap, such as `global-map' or `dired-mode-map', you will see an entry = that looks like this in Customize: INS DEL Key Definition: List: Choice: Value Menu Key: \200 . . =FF I haven't tried to make such an entry do anything useful. That is, the = custom type here does not recognize \200 . . =FF as a key range - it treats it = as an ordinary, single key sequence. Such an entry should probably just be = filtered out, unless it can be made useful. ------=_NextPart_000_005D_01C8B548.1093E5E0 Content-Type: application/octet-stream; name="keymap-option.el" Content-Transfer-Encoding: quoted-printable Content-Disposition: attachment; filename="keymap-option.el" (define-widget 'key-definition 'lazy=0A= "Key definition.=0A= A list of two components: KEY, BINDING.=0A= KEY is either a key sequence (string or vector) or a command.=0A= If KEY is a command, then the binding represented is its remapping=0A= to BINDING, which must also be a command."=0A= :tag "Key Definition" :indent 1 :offset 0=0A= :type=0A= '(list=0A= (choice=0A= (key-sequence :tag "Key" :value [ignore])=0A= (restricted-sexp :tag "Command to remap"=0A= :match-alternatives (commandp) :value ignore))=0A= (sexp :tag "Command")))=0A= =0A= (defun custom-create-keymap-option (map)=0A= "Define a user option for keymap MAP."=0A= ;; $$$ How to exclude full keymaps? Is it worth it?=0A= (interactive=0A= (list (intern=0A= (completing-read=0A= "Keymap (symbol): " obarray=0A= (lambda (s) (and (boundp s) (keymapp (symbol-value s))))=0A= t nil 'variable-name-history))))=0A= (unless (and (symbolp map) (boundp map)=0A= (keymapp (symbol-value map)))=0A= (error "`%S' is not a keymapp" map))=0A= (let ((opt-name (intern (concat (symbol-name map) "-defs")))=0A= (defs ()))=0A= (with-temp-buffer=0A= (princ (substitute-command-keys=0A= (concat "\\{" (symbol-name map) "}")) (current-buffer))=0A= (goto-char (point-min))=0A= (with-syntax-table emacs-lisp-mode-syntax-table=0A= (while (re-search-forward=0A= "^key +binding\n\\(-+ +\\)-+\n\n" nil t)=0A= (let ((col (- (match-end 1) (match-beginning 1))))=0A= (while (and (not (eobp)) (not (looking-at "\n\\s-*\n")))=0A= (when (eolp) (delete-region (line-beginning-position)=0A= (1+ (line-end-position))))=0A= (while (looking-at "^\\S-+.+\\s-+Prefix Command$")=0A= (delete-region (line-beginning-position)=0A= (1+ (line-end-position))))=0A= (end-of-line)=0A= (skip-chars-backward "^ \t\n")=0A= (when (looking-at "\\(\\sw\\|\\s_\\)+$")=0A= (if (>=3D (current-column) col)=0A= (let ((sym (intern-soft (match-string 0)))=0A= (cmd-beg (match-beginning 0))=0A= eokey-pos)=0A= (cond ((or (fboundp sym)=0A= (memq sym '(mouse-face ignore-event)))=0A= (end-of-line)=0A= (insert ")")=0A= (goto-char cmd-beg)=0A= (skip-chars-backward " \t")=0A= (setq eokey-pos (point))=0A= (insert "\")") ; +2=0A= (forward-line 0)=0A= (insert "(,(kbd \"") ; +8=0A= (while (< (point) (+ 8 eokey-pos))=0A= (when (looking-at "\\(\"\\|\\\\\\)")=0A= (insert "\\"))=0A= (forward-char))=0A= (goto-char (+ 10 cmd-beg))=0A= (forward-line))=0A= (t=0A= (forward-line)=0A= (if (looking-at "^\\s-+\\S-+$")=0A= (custom-create-keymap-option-1)=0A= (beginning-of-line)=0A= (delete-region=0A= (line-beginning-position)=0A= (1+ (line-end-position)))))))=0A= (forward-line)=0A= (if (looking-at "^\\s-+\\S-+$")=0A= (custom-create-keymap-option-1)=0A= (forward-line -1)=0A= (delete-region (line-beginning-position)=0A= (1+ (line-end-position 2))))))))))=0A= (goto-char (point-min))=0A= (while (re-search-forward=0A= "^key +binding\n\\(-+ +\\)-+\n\n" nil t)=0A= (forward-line -3)=0A= (delete-region (line-beginning-position)=0A= (1+ (line-end-position 3))))=0A= (insert "`(\n")=0A= (goto-char (point-max))=0A= (insert ")")=0A= (goto-char (point-min))=0A= (setq defs (eval (read (current-buffer)))))=0A= (eval=0A= `(defcustom ,opt-name=0A= ',defs=0A= ,(format "Customizable keymap for `%s'." map)=0A= :type '(repeat key-definition)=0A= :set #'(lambda (sym defns)=0A= (custom-set-default sym defns)=0A= (let (key command)=0A= (dolist (key-def defns)=0A= (setq key (car key-def)=0A= command (cadr key-def))=0A= (if (symbolp key)=0A= (define-key=0A= ,map (vector 'remap key) command)=0A= (define-key ,map key command)))))=0A= :initialize #'custom-initialize-set))))=0A= =0A= (defun custom-create-keymap-option-1 ()=0A= (end-of-line)=0A= (skip-chars-backward "^ \t\n")=0A= (when (looking-at "\\(\\sw\\|\\s_\\)+$")=0A= (if (>=3D (current-column) col)=0A= (let ((sym (intern-soft (match-string 0))))=0A= (cond ((fboundp sym)=0A= (end-of-line)=0A= (insert ")")=0A= (forward-line -1)=0A= (end-of-line)=0A= (insert "\")") ; +2=0A= (forward-line 0)=0A= (insert "(,(kbd \"") ; +8=0A= (forward-line 2))=0A= (t=0A= (forward-line 0)=0A= (delete-region (line-beginning-position)=0A= (1+ (line-end-position 2)))))))))=0A= =0A= =0A= ------=_NextPart_000_005D_01C8B548.1093E5E0--