changeset 5801:0e9f791cc655

Support `function-key-map' in #'read-char{,-exclusive}, sync API with GNU lisp/ChangeLog addition: 2014-07-02 Aidan Kehoe <kehoea@parhasard.net> * cmdloop.el: * cmdloop.el (read-char-1): New. * cmdloop.el (read-char, read-char-exclusive): Use #'read-char-1 in these function; sync their API with that of GNU, respect `function-key-map' where we didn't before, add initial support for Quail input methods. * keymap.el (next-key-event): Accept EVENT and PROMPT arguments, as does #'next-command-event. * keymap.el (event-apply-modifiers): Use #'functionp here, no need to exclude lambdas from `function-key-map'. * keymap.el (synthesize-keysym): Correct this function's docstring. src/ChangeLog addition: 2014-07-02 Aidan Kehoe <kehoea@parhasard.net> * event-stream.c (Fnext_command_event): Only snooze displaying keystrokes if PROMPT is nil. If prompt is non-nil, our callers want to see it.
author Aidan Kehoe <kehoea@parhasard.net>
date Wed, 02 Jul 2014 16:32:19 +0100
parents be3ca9e58c92
children 236e4afc565d
files lisp/ChangeLog lisp/cmdloop.el lisp/keymap.el src/ChangeLog src/event-stream.c
diffstat 5 files changed, 148 insertions(+), 45 deletions(-) [+]
line wrap: on
line diff
--- a/lisp/ChangeLog	Thu Jun 19 12:06:33 2014 +0900
+++ b/lisp/ChangeLog	Wed Jul 02 16:32:19 2014 +0100
@@ -1,3 +1,19 @@
+2014-07-02  Aidan Kehoe  <kehoea@parhasard.net>
+
+	* cmdloop.el:
+	* cmdloop.el (read-char-1): New.
+	* cmdloop.el (read-char, read-char-exclusive):
+	Use #'read-char-1 in these function; sync their API with that of
+	GNU, respect `function-key-map' where we didn't before, add
+	initial support for Quail input methods.
+	* keymap.el (next-key-event):
+	Accept EVENT and PROMPT arguments, as does #'next-command-event.
+	* keymap.el (event-apply-modifiers):
+	Use #'functionp here, no need to exclude lambdas from
+	`function-key-map'.
+	* keymap.el (synthesize-keysym):
+	Correct this function's docstring.
+
 2014-04-19  Mats Lidell  <matsl@xemacs.org>
 
 	* help.el: Sync from GNU - Link to customize if applicable and
--- a/lisp/cmdloop.el	Thu Jun 19 12:06:33 2014 +0900
+++ b/lisp/cmdloop.el	Wed Jul 02 16:32:19 2014 +0100
@@ -520,43 +520,120 @@
     (y-or-n-p-minibuf prompt)))
 
 
+(labels
+    ((read-char-1 (errorp prompt inherit-input-method seconds)
+       "Return a character from command input or the current macro.
+Look up said input in `function-key-map' as appropriate.
 
-(defun read-char ()
-  "Read a character from the command input (keyboard or macro).
-If a mouse click or non-ASCII character is detected, an error is
-signalled.  The character typed is returned as an ASCII value.  This
-is most likely the wrong thing for you to be using: consider using
-the `next-command-event' function instead."
-  (save-excursion
-    (let ((event (next-command-event)))
-      (or inhibit-quit
-	  (and (event-matches-key-specifier-p event (quit-char))
-	       (signal 'quit nil)))
-      (prog1 (or (event-to-character event)
-                 ;; Kludge.  If the event we read was a mouse-release,
-                 ;; discard it and read the next one.
-                 (if (button-release-event-p event)
-                     (event-to-character (next-command-event event)))
-                 (error "Key read has no ASCII equivalent %S" event))
-        ;; this is not necessary, but is marginally more efficient than GC.
-        (deallocate-event event)))))
+PROMPT is a prompt for `next-command-event', which see.
+
+If ERRORP is non-nil, error if the key sequence has no character equivalent.
+Otherwise, loop, discarding non-character keystrokes or mouse movements.
+
+If INHERIT-INPUT-METHOD is non-nil, and a Quail input method is active in
+the current buffer, use its translation when choosing a character to return.
 
-(defun read-char-exclusive ()
-  "Read a character from the command input (keyboard or macro).
-If a mouse click or non-ASCII character is detected, it is discarded.
-The character typed is returned as an ASCII value.  This is most likely
-the wrong thing for you to be using: consider using the
-`next-command-event' function instead."
-  (let (event ch)
-    (while (progn
-	     (setq event (next-command-event))
-	     (or inhibit-quit
-		 (and (event-matches-key-specifier-p event (quit-char))
-		      (signal 'quit nil)))
-	     (setq ch (event-to-character event))
-	     (deallocate-event event)
-	     (null ch)))
-    ch))
+If SECONDS is non-nil, only wait that number of seconds for input. If no
+input is received in that time, return nil."
+       (let ((timeout
+              (if seconds
+                  (add-timeout seconds #'(lambda (ignore)
+                                           (return-from read-char-1 nil))
+                               nil)))
+             (events []) binding character)
+         (unwind-protect
+              (while t
+                ;; Read keystrokes scanning `function-key-map'.
+                (while (keymapp
+                        (setq binding
+                              (lookup-key
+                               function-key-map
+                               (setq events
+                                     (vconcat events (list
+                                                      (next-key-event
+                                                       nil prompt))))))))
+                (when binding
+                  ;; Found something in function-key-map. If it's a function
+                  ;; (e.g. synthesize-keysym), call it.
+                  (if (functionp binding)
+                      (setq binding (funcall binding nil)))
+                  (setq events (map 'vector #'character-to-event binding)))
+                ;; Put the remaining keystrokes back on the input queue.
+                (setq unread-command-events
+                      (nconc (reduce #'cons events :start 1 :from-end t
+                                     :initial-value nil)
+                             unread-command-events))
+                (unless inhibit-quit
+                  (and (event-matches-key-specifier-p (aref events 0)
+                                                      (quit-char))
+                       (signal 'quit nil)))
+                (if (setq character (event-to-character (aref events 0)))
+                    (progn
+                      ;; If we have a character (the usual case), deallocate
+                      ;; the event and return the character.
+                      (deallocate-event (aref events 0))
+                      ;; Handle quail, if we've been asked to (maybe we
+                      ;; should default to this).
+                      (if (and inherit-input-method (and-boundp 'quail-mode
+                                                      quail-mode))
+                          (with-fboundp
+                              '(quail-map-definition quail-lookup-key)
+                            (let ((binding
+                                   (quail-map-definition
+                                    (quail-lookup-key (string character)))))
+                              (if (characterp binding)
+                                  (return-from read-char-1 binding))
+                              ;; #### Bug, we don't allow users to select from
+                              ;; among multiple characters that may be input
+                              ;; with the same key sequence.
+                              (if (and (consp binding)
+                                       (characterp
+                                        (aref (cdr binding) (caar binding))))
+                                  (return-from read-char-1
+                                    (aref (cdr binding) (caar binding)))))))
+                      (return-from read-char-1 character)))
+                (if errorp
+                    (error 'invalid-key-binding "Not a character keystroke"
+                           (aref events 0)))
+                ;; If we're not erroring, loop until we get a character
+                (setq events []))
+           (if timeout (disable-timeout timeout))))))
+  ;; Because of byte compiler limitations, each function has its own copy of
+  ;; #'read-char-1, so why not inline it.
+  (declare (inline read-char-1))
+
+  (defun read-char (&optional prompt inherit-input-method seconds)
+    "Read a character from the command input (keyboard or macro).
+If a mouse click or non-character keystroke is detected, signal an error.
+The character typed is returned as a Lisp object.  This is most likely the
+wrong thing for you to be using: consider using the `next-command-event'
+function instead.
+
+PROMPT is a prompt, as used by `next-command-event'.
+
+If INHERIT-INPUT-METHOD is non-nil, and a Quail input method is active in
+the current buffer, use its translation for the character returned.
+
+If SECONDS is non-nil, only wait that number of seconds for input. If no
+input is received in that time, return nil."
+    (read-char-1 t prompt inherit-input-method seconds))
+
+  (defun read-char-exclusive (&optional prompt inherit-input-method seconds)
+    "Read a character from the command input (keyboard or macro).
+
+If a mouse click or a non-character keystroke is detected, it is discarded.
+The character typed is returned as a Lisp object. This is most likely the
+wrong thing for you to be using: consider using the `next-command-event'
+function instead.
+
+PROMPT is a prompt, as used by `next-command-event'.
+
+If INHERIT-INPUT-METHOD is non-nil, and a Quail input method is active in
+the current buffer, use its translation for the character returned.
+
+If SECONDS is non-nil, only wait that number of seconds for input. If no
+input is received in that time, return nil."
+    (read-char-1 nil prompt inherit-input-method seconds)))
 
 ;;;; Input and display facilities.
 
--- a/lisp/keymap.el	Thu Jun 19 12:06:33 2014 +0900
+++ b/lisp/keymap.el	Wed Jul 02 16:32:19 2014 +0100
@@ -381,12 +381,12 @@
             (setq i (1+ i)))
           new))))
 
-(defun next-key-event ()
+(defun next-key-event (&optional event prompt)
   "Return the next available keyboard event."
-  (let (event)
-    (while (not (key-press-event-p (setq event (next-command-event))))
-      (dispatch-event event))
-    event))
+  (while (not (key-press-event-p
+               (setq event (next-command-event event prompt))))
+    (dispatch-event event))
+  event)
 
 (defun key-sequence-list-description (keys)
   "Convert a key sequence KEYS to the full [(modifiers... key)...] form.
@@ -457,7 +457,7 @@
     (if binding				; found a binding
 	(progn
 	  ;; allow for several modifiers
-	  (if (and (symbolp binding) (fboundp binding))
+	  (if (functionp binding)
 	      (setq binding (funcall binding nil)))
 	  (setq events (append binding nil))
 	  ;; put remaining keystrokes back into input queue
@@ -477,9 +477,9 @@
   (event-apply-modifiers (list symbol)))
 
 (defun synthesize-keysym (ignore-prompt)
-  "Read a sequence of keys, and returned the corresponding key symbol.
-The characters must be from the [-_a-zA-Z0-9].  Reading is terminated
- by RET (which is discarded)."
+  "Read a sequence of characters, and return the corresponding keysym.
+The characters must be ?-, or ?_, or have word syntax.  Reading is
+terminated by RET (which is discarded)."
   (let ((continuep t)
 	event char list)
     (while continuep
--- a/src/ChangeLog	Thu Jun 19 12:06:33 2014 +0900
+++ b/src/ChangeLog	Wed Jul 02 16:32:19 2014 +0100
@@ -1,3 +1,9 @@
+2014-07-02  Aidan Kehoe  <kehoea@parhasard.net>
+
+	* event-stream.c (Fnext_command_event):
+	Only snooze displaying keystrokes if PROMPT is nil. If prompt is
+	non-nil, our callers want to see it.
+
 2014-06-19  Stephen J. Turnbull  <stephen@xemacs.org>
 
 	* buffer.c (case_fold_search): Improve docstring.
--- a/src/event-stream.c	Thu Jun 19 12:06:33 2014 +0900
+++ b/src/event-stream.c	Wed Jul 02 16:32:19 2014 +0100
@@ -2436,7 +2436,11 @@
    
   maybe_echo_keys (XCOMMAND_BUILDER
 		   (XCONSOLE (Vselected_console)->
-		    command_builder), 0); /* #### This sucks bigtime */
+		    command_builder),
+                   /* Only snooze displaying keystrokes if we don't have a
+                      prompt. (If we have a prompt, our callers want us to
+                      show it!) */
+                   !NILP (prompt));
 
   for (;;)
     {