unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
* customizing key definitions with Customize
@ 2008-05-11 19:40 Drew Adams
  2008-05-11 22:02 ` Lennart Borgman (gmail)
                   ` (2 more replies)
  0 siblings, 3 replies; 43+ messages in thread
From: Drew Adams @ 2008-05-11 19:40 UTC (permalink / raw)
  To: emacs-devel

[-- Attachment #1: Type: text/plain, Size: 4025 bytes --]

Users can customize key definitions by using commands such as `global-set-key'
or by using functions such as `define-key' in their init files. And `kbd' helps
them by letting them use external key descriptions. 

It can sometimes be useful to let users use Customize to update key-binding
definitions. The `key-sequence' widget added in Emacs 22 is helpful here, but it
only takes care of the key-sequence part of a binding, not the map part or the
target command part. Also, defining keys can sometimes mean remapping existing
commands instead of providing a key sequence to bind, and the `key-sequence'
widget doesn't apply in that case.

I've been using a key definition widget as a custom type, to let users of a
library use only Customize to customize key bindings, if they want. 

The widget uses a `choice' to let you either specify a key sequence to bind or a
command to be remapped. Dunno if this is of general interest, but here is what I
use.

(define-widget 'key-definition 'lazy
  "Key definition.
A list of three components: KEYMAP, KEY, COMMAND, that represents a
binding of command COMMAND in keymap KEYMAP according to KEY.
KEY is either a key sequence (string or vector) or a command.
If KEY is a command, then the binding represented is its remapping to
command COMMAND."
  :tag "Key Definition" :indent 1 :offset 0
  :type
  '(list
    (restricted-sexp :tag "Keymap"
     :match-alternatives ((lambda (x) (keymapp (eval x))))
     :value global-map)
    (choice
     (key-sequence :tag "Key" :value [ignore])
     (restricted-sexp :tag "Command to remap"
      :match-alternatives (commandp) :value ignore))
    (restricted-sexp :tag "Command"
     :match-alternatives (commandp) :value ignore)))

You can then create a user option for a set of key definitions (or, less likely,
for a single key binding), where a key definition includes the keymap, the key
(or command to remap), and the target command.

For example, here is a `defcustom' that uses a `repeat' of the widget to define
a set of bindings:

(defcustom some-bindings
  `((test-map ,(kbd "C-a") forward-char)
    (test-map end-of-line backward-char)
    (test-map-2 ,(kbd "C-a") forward-word)
    (test-map-2 end-of-line backward-word)) ""
  :type '(repeat key-definition)
  :set #'(lambda (sym defs)
           (custom-set-default sym defs)
           (dolist (key-def defs) (define-key-from-key-def key-def)))
  :initialize #'custom-initialize-set)

The :set function uses this helper:

(defun define-key-from-key-def (key-def)
  "Define a key using a key-definition data structure
Argument KEY-DEF is a list of type `key-definition' or
`conditional-key-definition'."
  (let ((map (eval (car key-def)))
        (key (cadr key-def))
        (command (caddr key-def))
        (condition (cadddr key-def)))
    (when (or (not condition) (eval condition))
      (if (symbolp key)
          (define-key map (vector 'remap key) command)
        (define-key map key command)))))

The condition part is not present, and so is not used, in `key-definition', but
it is present and used in the similar widget `conditional-key-definition'
mentioned in the doc string. That has an additional list item that is eval'd to
determine under what conditions to make the binding. The definition is the same
as for `key-definition', except that the `list' has this entry, in addition:
(sexp :tag "Condition"). I use conditional key definitions to, for example, deal
with different Emacs versions.

Attached is a file you can play with to try this out. I'm no widget wizard, so
there is perhaps a better way to define the widgets. In particular, what is
common between them could perhaps be factored out somehow. Suggestions welcome.

If there is interest in something like this, we could add it to wid-edit.el. Key
bindings are something that most users want to change, and there currently is no
ready way for them to do that entirely within Customize. This approach lets
libraries define a default set of bindings and lets users use Customize to
modify them.


[-- Attachment #2: test-key-def-widgets.el --]
[-- Type: application/octet-stream, Size: 4011 bytes --]

(define-widget 'conditional-key-definition 'lazy
  "Conditional key definition.
A list of four components: KEYMAP, KEY, COMMAND, CONDITION, that
represents a binding of command COMMAND in keymap KEYMAP according to
KEY, if CONDITION evaluates to non-nil.

KEY is either a key sequence (string or vector) or a command.
COMMAND is a command.
CONDITION is a sexp.

If KEY is a command, then the binding represented is its remapping to
COMMAND."
  :tag "Key Definition" :indent 1 :offset 0
  :type
  '(list
    (restricted-sexp :tag "Keymap"
     :match-alternatives ((lambda (x) (keymapp (eval x))))
     :value global-map)
    (choice
     (key-sequence :tag "Key" :value [ignore])
     (restricted-sexp :tag "Command to remap"
      :match-alternatives (commandp) :value ignore))
    (restricted-sexp :tag "Command"
     :match-alternatives (commandp) :value ignore)
    (sexp :tag "Condition")))

(define-widget 'key-definition 'lazy
  "Key definition.
A list of three components: KEYMAP, KEY, COMMAND, that represents a
binding of command COMMAND in keymap KEYMAP according to KEY.
KEY is either a key sequence (string or vector) or a command.
If KEY is a command, then the binding represented is its remapping to
command COMMAND."
  :tag "Key Definition" :indent 1 :offset 0
  :type
  '(list
    (restricted-sexp :tag "Keymap"
     :match-alternatives ((lambda (x) (keymapp (eval x))))
     :value global-map)
    (choice
     (key-sequence :tag "Key" :value [ignore])
     (restricted-sexp :tag "Command to remap"
      :match-alternatives (commandp) :value ignore))
    (restricted-sexp :tag "Command"
     :match-alternatives (commandp) :value ignore)))

(defun define-key-from-key-def (key-def)
  "Define a key using a key-definition data structure
Argument KEY-DEF is a list of type `key-definition' or
`conditional-key-definition'."
  (let ((map (eval (car key-def)))
        (key (cadr key-def))
        (command (caddr key-def))
        (condition (cadddr key-def)))
    (when (or (not condition) (eval condition))
      (if (symbolp key)
          (define-key map (vector 'remap key) command)
        (define-key map key command)))))

(defun key-def-set (var key-def)
  "Set function for types `key-definition', `conditional-key-definition'."
  (custom-set-default var key-def)
  (define-key-from-key-def key-def))

(setq test-map (make-sparse-keymap))
(setq test-map-2 (make-sparse-keymap))
(setq foobar t)

(defcustom foo `(test-map ,(kbd "C-a") forward-char) ""
  :type 'key-definition
  :set #'key-def-set
  :initialize #'custom-initialize-set)

(defcustom bar `(test-map end-of-line forward-char) ""
  :type 'key-definition
  :set #'key-def-set
  :initialize #'custom-initialize-set)

(defcustom some-bindings
  `((test-map ,(kbd "C-a") forward-char)
    (test-map end-of-line backward-char)
    (test-map-2 ,(kbd "C-a") forward-word)
    (test-map-2 end-of-line backward-word)) ""
  :type '(repeat key-definition)
  :set #'(lambda (sym defs)
           (custom-set-default sym defs)
           (dolist (key-def defs) (define-key-from-key-def key-def)))
  :initialize #'custom-initialize-set)

(defcustom cond-foo `(test-map ,(kbd "C-a") forward-char t) ""
  :type 'conditional-key-definition
  :set #'key-def-set
  :initialize #'custom-initialize-set)

(defcustom cond-bar `(test-map end-of-line forward-char foobar) ""
  :type 'conditional-key-definition
  :set #'key-def-set
  :initialize #'custom-initialize-set)

(defcustom some-conditional-bindings
  `((test-map ,(kbd "C-a") forward-char t)
    (test-map end-of-line backward-char foobar)
    (test-map-2 ,(kbd "C-a") forward-word t)
    (test-map-2 end-of-line backward-word foobar)) ""
  :type '(repeat conditional-key-definition)
  :set #'(lambda (sym defs)
           (custom-set-default sym defs)
           (dolist (key-def defs)
             (define-key-from-key-def key-def)))
  :initialize #'custom-initialize-set)



^ permalink raw reply	[flat|nested] 43+ messages in thread

end of thread, other threads:[~2008-05-18  9:07 UTC | newest]

Thread overview: 43+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-05-11 19:40 customizing key definitions with Customize Drew Adams
2008-05-11 22:02 ` Lennart Borgman (gmail)
2008-05-11 22:28   ` Drew Adams
2008-05-11 22:40     ` Lennart Borgman (gmail)
2008-05-11 23:02       ` Drew Adams
2008-05-11 23:09         ` Lennart Borgman (gmail)
2008-05-11 23:19           ` Drew Adams
2008-05-11 23:23             ` Lennart Borgman (gmail)
2008-05-11 23:34               ` Drew Adams
2008-05-12 20:42                 ` Lennart Borgman (gmail)
2008-05-14  5:24         ` Drew Adams
2008-05-12  8:59 ` Reiner Steib
2008-05-12 20:58   ` Drew Adams
2008-05-12 11:20 ` Richard M Stallman
2008-05-12 14:01   ` Drew Adams
2008-05-13  0:03     ` Juri Linkov
2008-05-13  0:40       ` Lennart Borgman (gmail)
2008-05-13 14:59       ` Richard M Stallman
2008-05-13 23:59         ` Juri Linkov
2008-05-14  1:10           ` Stefan Monnier
2008-05-14 16:40           ` Richard M Stallman
2008-05-15  4:46             ` Drew Adams
2008-05-15 17:39               ` Richard M Stallman
2008-05-16  8:01                 ` Drew Adams
2008-05-16 17:46                   ` Richard M Stallman
2008-05-16 18:00                     ` David Kastrup
2008-05-16 23:58                       ` Drew Adams
2008-05-17  5:00                       ` Richard M Stallman
2008-05-16  7:51               ` Drew Adams
2008-05-18  1:22                 ` Drew Adams
2008-05-18  9:07                   ` Key/menu bug? (was: customizing key definitions with Customize) David Kastrup
2008-05-13 15:07       ` customizing key definitions with Customize David Reitter
2008-05-13 19:05         ` David Kastrup
2008-05-14  5:23       ` Drew Adams
2008-05-13  5:16     ` Richard M Stallman
2008-05-14  5:23       ` Drew Adams
2008-05-14 16:39         ` Richard M Stallman
2008-05-15  4:36           ` Drew Adams
2008-05-15 17:39             ` Richard M Stallman
2008-05-16  8:02               ` Drew Adams
2008-05-16 17:46                 ` Richard M Stallman
2008-05-16 23:58                   ` Drew Adams
2008-05-12 20:42   ` Lennart Borgman (gmail)

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).