emacs-devel
[Top][All Lists]
Advanced

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

Enhancements to "minor-mode-map-alist" functionality.


From: Kim F. Storm
Subject: Enhancements to "minor-mode-map-alist" functionality.
Date: 12 Apr 2002 00:56:14 +0200
User-agent: Gnus/5.09 (Gnus v5.9.0) Emacs/21.2.50

I'm almost done reworking cua-mode to use the command remapping
feature, and it is much cleaner than the old package.

However, the new cua package need to make some quite ugly
modifications to the minor-mode-map-alist (in the post-command-hook)
after _every_ command, since:

- the new cua need a total of 7 minor mode keymaps, which must be
  processed in a specific order, and before all other minor mode
  keymaps

- the selection of which of these minor mode keymaps are active may
  depend on the values of as many as 6 different variables for a
  single keymap

However, this can be dramatically simplified by two (simple)
enhancements to the minor-mode-map-alist functionality:

1) Provide a separate emulation-mode-map-alists variable where
modes like cua can insert their own mode-map-alist,

2) Allow selection of the active keymaps in the alist to
be based on evaluating a form in addition to a simple variable.

I have already implemented these features (see attached patch),
which allows me to configure all the minor mode keymaps needed
by cua once and for all:

  (setq cua--emulation-map-alist
        `((lambda . (and mark-active
                         cua-enable-cua-keys
                         (or (eq cua-enable-cua-keys t)
                             (not cua--explicit-region-start))
                         (not executing-kbd-macro)
                         (not cua--prefix-override-timer)
                         '(cua--prefix-override . 
,cua--prefix-override-keymap)))
          (lambda . (and mark-active
                         (timerp cua--prefix-override-timer)
                         '(cua--prefix-override . ,cua--prefix-repeat-keymap)))
          (lambda . (and (or (eq cua-enable-cua-keys t)
                             cua--last-region-shifted)
                         '(cua-enable-cua-keys . ,cua--cua-keys-keymap)))
          (lambda . (and cua--global-mark-active
                         (not (window-minibuffer-p))
                         '(cua--global-mark-active . ,cua--global-mark-keymap)))
          (cua--rectangle . ,cua--rectangle-keymap)
          (mark-active . ,cua--region-keymap)
          (cua-mode . ,cua-global-keymap)))

  (setq emulation-mode-map-alists
        (cons cua--emulation-map-alist emulation-mode-map-alists))

On the user visible part, this is added to the doc string for
minor-mode-map-alist:

Alternatively, an element may look like (lambda . FORM) where FORM is
evaluated and should return either nil or a cons (SYMBOL . KEYMAP);
in this case, KEYMAP is used unconditionally and SYMBOL is displayed
by `describe-bindings' as the variable controlling KEYMAP.


The doc string for emulation-mode-map-alists reads:

List of keymap alists to use for emulations modes.
It is intended for modes or packages using multiple minor-mode keymaps.
Each element is a keymap alist just like `minor-mode-map-alist', and it
is used the same way.  The "active" keymaps in this alist are used before
`minor-mode-map-alist' but after `minor-mode-overriding-map-alist'.



Here is the patch which I expect to commit in a few days (when I
install the new version of cua mode).

Index: keymap.c
===================================================================
RCS file: /cvs/emacs/src/keymap.c,v
retrieving revision 1.258
diff -c -r1.258 keymap.c
*** keymap.c    24 Feb 2002 00:23:20 -0000      1.258
--- keymap.c    11 Apr 2002 21:54:34 -0000
***************
*** 75,80 ****
--- 75,83 ----
     minor mode variables and keymaps.  */
  Lisp_Object Vminor_mode_overriding_map_alist;
  
+ /* List of emulation mode keymap alists.  */
+ Lisp_Object Vemulation_mode_map_alists;
+ 
  /* Keymap mapping ASCII function key sequences onto their preferred forms.
     Initialized by the terminal-specific lisp files.  See DEFVAR for more
     documentation.  */
***************
*** 1277,1358 ****
  {
    int i = 0;
    int list_number = 0;
!   Lisp_Object alist, assoc, var, val;
!   Lisp_Object lists[2];
  
!   lists[0] = Vminor_mode_overriding_map_alist;
!   lists[1] = Vminor_mode_map_alist;
  
!   for (list_number = 0; list_number < 2; list_number++)
!     for (alist = lists[list_number];
!        CONSP (alist);
!        alist = XCDR (alist))
!       if ((assoc = XCAR (alist), CONSP (assoc))
!         && (var = XCAR (assoc), SYMBOLP (var))
!         && (val = find_symbol_value (var), !EQ (val, Qunbound))
!         && !NILP (val))
!       {
!         Lisp_Object temp;
! 
!         /* If a variable has an entry in Vminor_mode_overriding_map_alist,
!            and also an entry in Vminor_mode_map_alist,
!            ignore the latter.  */
!         if (list_number == 1)
            {
!             val = assq_no_quit (var, lists[0]);
!             if (!NILP (val))
!               continue;
            }
  
!         if (i >= cmm_size)
!           {
!             Lisp_Object *newmodes, *newmaps;
  
!             /* Use malloc/realloc here.  See the comment above
!                this function.  */
!             if (cmm_maps)
!               {
!                 BLOCK_INPUT;
!                 cmm_size *= 2;
!                 newmodes
!                   = (Lisp_Object *) realloc (cmm_modes,
!                                               cmm_size * sizeof *newmodes);
!                 newmaps
!                   = (Lisp_Object *) realloc (cmm_maps,
!                                               cmm_size * sizeof *newmaps);
!                 UNBLOCK_INPUT;
!               }
!             else
!               {
!                 BLOCK_INPUT;
!                 cmm_size = 30;
!                 newmodes
!                   = (Lisp_Object *) malloc (cmm_size * sizeof *newmodes);
!                 newmaps
!                   = (Lisp_Object *) malloc (cmm_size * sizeof *newmaps);
!                 UNBLOCK_INPUT;
!               }
  
!             if (newmodes)
!               cmm_modes = newmodes;
!             if (newmaps)
!               cmm_maps = newmaps;
!             
!             if (newmodes == NULL || newmaps == NULL)
!               break;
            }
  
!         /* Get the keymap definition--or nil if it is not defined.  */
!         temp = internal_condition_case_1 (Findirect_function,
!                                           XCDR (assoc),
!                                           Qerror, current_minor_maps_error);
!         if (!NILP (temp))
!           {
!             cmm_modes[i] = var;
!             cmm_maps [i] = temp;
!             i++;
!           }
        }
  
    if (modeptr) *modeptr = cmm_modes;
    if (mapptr)  *mapptr  = cmm_maps;
--- 1280,1397 ----
  {
    int i = 0;
    int list_number = 0;
!   Lisp_Object alist, assoc, var, val, emul_list;
! 
!   emul_list = Vemulation_mode_map_alists;
!   alist = Vminor_mode_overriding_map_alist;
! 
!  next_alist:
!   for ( ; CONSP (alist); alist = XCDR (alist))
!     {
!       Lisp_Object temp;
! 
!       if (assoc = XCAR (alist), !CONSP (assoc))
!       continue;
! 
!       /* Each element in the alist is a CONS cell:
!        The form (VAR . KEYMAP) causes the KEYMAP to be included if the
!        VAR is defined an non-nil.
!        The form (lambda . FORM) evaluates the FORM which shall result in 
!        nil or a CONS cell (SYMBOL . KEYMAP) where the KEYMAP is included
!        unconditionally (as associated with the specified SYMBOL.  */
!        
!       var = XCAR (assoc);
! 
!       if (EQ (var, Qlambda))
!       {
!         if ((temp = XCDR (assoc), NILP (temp))
!             || (assoc = safe_eval (temp), !CONSP (assoc))
!             || (var = XCAR (assoc), !SYMBOLP (var)))
!           continue;
!       }
!       else
!       {
!         if (!SYMBOLP (var)
!             || (val = find_symbol_value (var), EQ (val, Qunbound))
!             || NILP (val))
!           continue;
!       }
  
!       /* If a variable has an entry in Vminor_mode_overriding_map_alist,
!        and also an entry in Vminor_mode_map_alist, ignore the latter.  */
!       if (list_number == 2)
!       {
!         val = assq_no_quit (var, Vminor_mode_overriding_map_alist);
!         if (!NILP (val))
!           continue;
!       }
  
!       if (i >= cmm_size)
!       {
!         Lisp_Object *newmodes, *newmaps;
! 
!         /* Use malloc/realloc here.  See the comment above
!            this function.  */
!         if (cmm_maps)
!           {
!             BLOCK_INPUT;
!             cmm_size *= 2;
!             newmodes = (Lisp_Object *) realloc (cmm_modes,
!                                                 cmm_size * sizeof *newmodes);
!             newmaps = (Lisp_Object *) realloc (cmm_maps,
!                                                cmm_size * sizeof *newmaps);
!             UNBLOCK_INPUT;
!           }
!         else
            {
!             BLOCK_INPUT;
!             cmm_size = 30;
!             newmodes = (Lisp_Object *) malloc (cmm_size * sizeof *newmodes);
!             newmaps = (Lisp_Object *) malloc (cmm_size * sizeof *newmaps);
!             UNBLOCK_INPUT;
            }
  
!         if (newmodes)
!           cmm_modes = newmodes;
!         if (newmaps)
!           cmm_maps = newmaps;
!             
!         if (newmodes == NULL || newmaps == NULL)
!           break;
!       }
  
!       /* Get the keymap definition--or nil if it is not defined.  */
!       temp = internal_condition_case_1 (Findirect_function,
!                                       XCDR (assoc),
!                                       Qerror, current_minor_maps_error);
!       if (!NILP (temp))
!       {
!         cmm_modes[i] = var;
!         cmm_maps [i] = temp;
!         i++;
!       }
!     }
  
!   if (list_number < 2)
!     {
!       while (CONSP (emul_list))
!       {
!         alist = XCAR (emul_list);
!         emul_list = XCDR (emul_list);
!         if (CONSP (alist))
!           {
!             list_number = 1;
!             goto next_alist;
            }
+       }
  
!       alist = Vminor_mode_map_alist;
!       if (CONSP (alist))
!       {
!         list_number = 2;
!         goto next_alist;
        }
+     }
  
    if (modeptr) *modeptr = cmm_modes;
    if (mapptr)  *mapptr  = cmm_maps;
***************
*** 3568,3583 ****
               doc: /* Alist of keymaps to use for minor modes.
  Each element looks like (VARIABLE . KEYMAP); KEYMAP is used to read
  key sequences and look up bindings iff VARIABLE's value is non-nil.
  If two active keymaps bind the same key, the keymap appearing earlier
  in the list takes precedence.  */);
    Vminor_mode_map_alist = Qnil;
  
    DEFVAR_LISP ("minor-mode-overriding-map-alist", 
&Vminor_mode_overriding_map_alist,
               doc: /* Alist of keymaps to use for minor modes, in current 
major mode.
! This variable is a alist just like `minor-mode-map-alist', and it is
! used the same way (and before `minor-mode-map-alist'); however,
! it is provided for major modes to bind locally.  */);
    Vminor_mode_overriding_map_alist = Qnil;
  
    DEFVAR_LISP ("function-key-map", &Vfunction_key_map,
               doc: /* Keymap mapping ASCII function key sequences onto their 
preferred forms.
--- 3607,3635 ----
               doc: /* Alist of keymaps to use for minor modes.
  Each element looks like (VARIABLE . KEYMAP); KEYMAP is used to read
  key sequences and look up bindings iff VARIABLE's value is non-nil.
+ Alternatively, an element may look like (lambda . FORM) where FORM is 
+ evaluated and should return either nil or a cons (SYMBOL . KEYMAP);
+ in this case, KEYMAP is used unconditionally and SYMBOL is displayed
+ by `describe-bindings' as the variable controlling KEYMAP.
  If two active keymaps bind the same key, the keymap appearing earlier
  in the list takes precedence.  */);
    Vminor_mode_map_alist = Qnil;
  
    DEFVAR_LISP ("minor-mode-overriding-map-alist", 
&Vminor_mode_overriding_map_alist,
               doc: /* Alist of keymaps to use for minor modes, in current 
major mode.
! This variable is an alist just like `minor-mode-map-alist', and it is
! used the same way (and before `minor-mode-map-alist' and alists in
! `emulation-mode-map-alists'); however, it is provided for
! major modes to bind locally.  */);
    Vminor_mode_overriding_map_alist = Qnil;
+ 
+   DEFVAR_LISP ("emulation-mode-map-alists", &Vemulation_mode_map_alists,
+              doc: /* List of keymap alists to use for emulations modes.
+ It is intended for modes or packages using multiple minor-mode keymaps.
+ Each element is a keymap alist just like `minor-mode-map-alist', and it
+ is used the same way.  The "active" keymaps in this alist are used before
+ `minor-mode-map-alist' but after `minor-mode-overriding-map-alist'.  */); 
+   Vemulation_mode_map_alists = Qnil;
  
    DEFVAR_LISP ("function-key-map", &Vfunction_key_map,
               doc: /* Keymap mapping ASCII function key sequences onto their 
preferred forms.





reply via email to

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