]> code.delx.au - gnu-emacs/blobdiff - lisp/international/quail.el
Merged from emacs@sv.gnu.org
[gnu-emacs] / lisp / international / quail.el
index 2f275a59dca2de586938e5b069930f94f40af941..43251b3721e33ed6bc927cc8aaa1a050f929b94a 100644 (file)
@@ -1,8 +1,11 @@
 ;;; quail.el --- provides simple input method for multilingual text
 
-;; Copyright (C) 1995, 2000 Electrotechnical Laboratory, JAPAN.
-;; Licensed to the Free Software Foundation.
-;; Copyright (C) 2001, 2002 Free Software Foundation, Inc.
+;; Copyright (C) 1997, 1998, 2000, 2001, 2002, 2003, 2004, 2005,
+;;   2006, 2007  Free Software Foundation, Inc.
+;; Copyright (C) 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
+;;   2005, 2006, 2007
+;;   National Institute of Advanced Industrial Science and Technology (AIST)
+;;   Registration Number H14PRO021
 
 ;; Author: Kenichi HANDA <handa@etl.go.jp>
 ;;        Naoto TAKAHASHI <ntakahas@etl.go.jp>
@@ -23,8 +26,8 @@
 
 ;; You should have received a copy of the GNU General Public License
 ;; along with GNU Emacs; see the file COPYING.  If not, write to the
-;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-;; Boston, MA 02111-1307, USA.
+;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+;; Boston, MA 02110-1301, USA.
 
 ;;; Commentary:
 
@@ -896,7 +899,7 @@ The format of KBD-LAYOUT is the same as `quail-keyboard-layout'."
 The variable `quail-keyboard-layout-type' holds the currently selected
 keyboard type."
   (interactive
-   (list (completing-read "Keyboard type (default, current choice): "
+   (list (completing-read "Keyboard type (default current choice): "
                          quail-keyboard-layout-alist
                          nil t)))
   (or (and keyboard-type (> (length keyboard-type) 0))
@@ -1043,9 +1046,13 @@ which to install MAP.
 The installed decode map can be referred by the function `quail-decode-map'."
   (if (null quail-current-package)
       (error "No current Quail package"))
-  (if (not (and (consp decode-map) (eq (car decode-map) 'decode-map)))
-      (error "Invalid Quail decode map `%s'" decode-map))
-  (setcar (nthcdr 10 quail-current-package) decode-map))
+  (if (if (consp decode-map)
+         (eq (car decode-map) 'decode-map)
+       (if (char-table-p decode-map)
+           (eq (char-table-subtype decode-map) 'quail-decode-map)))
+      (setcar (nthcdr 10 quail-current-package) decode-map)
+    (error "Invalid Quail decode map `%s'" decode-map)))
+
 
 ;;;###autoload
 (defun quail-defrule (key translation &optional name append)
@@ -1090,8 +1097,8 @@ Optional 5th arg DECODE-MAP is a Quail decode map.
 
 Optional 6th arg PROPS is a property list annotating TRANS.  See the
 function `quail-define-rules' for the detail."
-  (if (null (stringp key))
-      "Invalid Quail key `%s'" key)
+  (if (not (or (stringp key) (vectorp key)))
+      (error "Invalid Quail key `%s'" key))
   (if (not (or (numberp trans) (stringp trans) (vectorp trans)
               (consp trans)
               (symbolp trans)
@@ -1218,7 +1225,7 @@ selected translation."
    (t
     (error "Invalid object in Quail map: %s" def))))
 
-(defun quail-lookup-key (key &optional len)
+(defun quail-lookup-key (key &optional len not-reset-indices)
   "Lookup KEY of length LEN in the current Quail map and return the definition.
 The returned value is a Quail map specific to KEY."
   (or len
@@ -1256,7 +1263,7 @@ The returned value is a Quail map specific to KEY."
          (if (and (consp translation) (vectorp (cdr translation)))
              (progn
                (setq quail-current-translations translation)
-               (if (quail-forget-last-selection)
+               (if (and (not not-reset-indices) (quail-forget-last-selection))
                    (setcar (car quail-current-translations) 0))))))
     ;; We may have to reform cdr part of MAP.
     (if (and (cdr map) (functionp (cdr map)))
@@ -1308,7 +1315,8 @@ Do so while interleaving with the following special events:
       (list key)
     (quail-setup-overlays (quail-conversion-keymap))
     (let ((modified-p (buffer-modified-p))
-         (buffer-undo-list t))
+         (buffer-undo-list t)
+         (inhibit-modification-hooks t))
       (unwind-protect
          (let ((input-string (if (quail-conversion-keymap)
                                  (quail-start-conversion key)
@@ -1367,11 +1375,12 @@ Return the input string."
        (while quail-translating
          (set-buffer-modified-p modified-p)
          (quail-show-guidance)
-         (let* ((keyseq (read-key-sequence
-                         (and input-method-use-echo-area
-                              (concat input-method-previous-message
-                                      quail-current-str))
-                         nil nil t))
+         (let* ((prompt (if input-method-use-echo-area
+                            (format "%s%s %s" 
+                                    (or input-method-previous-message "")
+                                    quail-current-str
+                                    quail-guidance-str)))
+                (keyseq (read-key-sequence prompt nil nil t))
                 (cmd (lookup-key (quail-translation-keymap) keyseq)))
            (if (if key
                    (and (commandp cmd) (not (eq cmd 'quail-other-command)))
@@ -1432,12 +1441,13 @@ Return the input string."
                      quail-translating t)
                (quail-setup-overlays nil)))
          (quail-show-guidance)
-         (let* ((keyseq (read-key-sequence
-                         (and input-method-use-echo-area
-                              (concat input-method-previous-message
-                                      quail-conversion-str
-                                      quail-current-str))
-                         nil nil t))
+         (let* ((prompt (if input-method-use-echo-area
+                            (format "%s%s%s %s" 
+                                    (or input-method-previous-message "")
+                                    quail-conversion-str
+                                    quail-current-str
+                                    quail-guidance-str)))
+                (keyseq (read-key-sequence prompt nil nil t))
                 (cmd (lookup-key (quail-conversion-keymap) keyseq)))
            (if (if key (commandp cmd) (eq cmd 'quail-self-insert-command))
                (progn
@@ -1524,6 +1534,28 @@ with more keys."
       (let (pos)
        (quail-delete-region)
        (setq pos (point))
+       (or enable-multibyte-characters
+           (let (char)
+             (if (stringp quail-current-str)
+                 (catch 'tag
+                   (mapc #'(lambda (ch)
+                             (when (/= (unibyte-char-to-multibyte
+                                        (multibyte-char-to-unibyte ch))
+                                       ch)
+                                 (setq char ch)
+                                 (throw 'tag nil)))
+                         quail-current-str))
+               (if (/= (unibyte-char-to-multibyte
+                        (multibyte-char-to-unibyte quail-current-str))
+                       quail-current-str)
+                   (setq char quail-current-str)))
+             (when char
+               (message "Can't input %c in the current unibyte buffer" char)
+               (ding)
+               (sit-for 2)
+               (message nil)
+               (setq quail-current-str nil)
+               (throw 'quail-tag nil))))
        (insert quail-current-str)
        (move-overlay quail-overlay pos (point))
        (if (overlayp quail-conv-overlay)
@@ -1568,13 +1600,21 @@ Quail map for the sequence."
   (or (and (consp def) (aref (cdr def) (car (car def))))
       def
       (and (> len 1)
-          (let ((str (quail-get-current-str
-                      (1- len)
-                      (quail-map-definition (quail-lookup-key
-                                             quail-current-key (1- len))))))
+          (let* ((str (quail-get-current-str
+                       (1- len)
+                       (quail-map-definition (quail-lookup-key
+                                              quail-current-key (1- len)))))
+                 (substr1 (substring quail-current-key (1- len) len))
+                 (str1 (and (quail-deterministic)
+                            (quail-get-current-str
+                             1
+                             (quail-map-definition (quail-lookup-key
+                                                    substr1 1))))))
             (if str
                 (concat (if (stringp str) str (char-to-string str))
-                        (substring quail-current-key (1- len) len)))))))
+                        (if str1
+                            (if (stringp str1) str1 (char-to-string str1))
+                          substr1)))))))
 
 (defvar quail-guidance-translations-starting-column 20)
 
@@ -1690,6 +1730,20 @@ sequence counting from the head."
             ;; And, we can terminate the current translation.
             t)
 
+           ((quail-deterministic)
+            ;; No way to handle the last character in this context.
+            ;; Commit the longest successfully translated characters, and
+            ;; handle the remaining characters in a new loop.
+            (setq def nil)
+            (while (and (not def) (> len 1))
+              (setq len (1- len))
+              (setq def (quail-map-definition
+                         (quail-lookup-key quail-current-key len))))
+            (if def (setq quail-current-str
+                          (quail-get-current-str len def))
+              (setq quail-current-str (aref quail-current-key 0)))
+            len)
+
            (t
             ;; No way to handle the last character in this context.
             (setq def (quail-map-definition
@@ -1883,6 +1937,7 @@ Remaining args are for FUNC."
     (let ((default-enable-multibyte-characters enable-multibyte-characters))
       (setq quail-completion-buf (get-buffer-create "*Quail Completions*")))
     (with-current-buffer quail-completion-buf
+      (setq buffer-read-only t)
       (setq quail-overlay (make-overlay 1 1))
       (overlay-put quail-overlay 'face 'highlight))))
 
@@ -1924,10 +1979,10 @@ minibuffer and the selected frame has no other windows)."
 
   ;; Then, show the guidance.
   (when (and (quail-require-guidance-buf)
+            (not input-method-use-echo-area)
             (null unread-command-events)
             (null unread-post-input-method-events))
-    (if (or (eq (selected-window) (minibuffer-window))
-           input-method-use-echo-area)
+    (if (eq (selected-window) (minibuffer-window))
        (if (eq (minibuffer-window) (frame-root-window))
            ;; Use another frame.  It is sure that we are using some
            ;; window system.
@@ -2021,7 +2076,7 @@ minibuffer and the selected frame has no other windows)."
 
 (defun quail-get-translations ()
   "Return a string containing the current possible translations."
-  (let ((map (quail-lookup-key quail-current-key))
+  (let ((map (quail-lookup-key quail-current-key nil t))
        (str (copy-sequence quail-current-key)))
     (if quail-current-translations
        (quail-update-current-translations))
@@ -2092,7 +2147,7 @@ are shown (at most to the depth specified `quail-completion-max-depth')."
   (quail-setup-completion-buf)
   (let ((win (get-buffer-window quail-completion-buf 'visible))
        (key quail-current-key)
-       (map (quail-lookup-key quail-current-key))
+       (map (quail-lookup-key quail-current-key nil t))
        (require-update nil))
     (with-current-buffer quail-completion-buf
       (if (and win
@@ -2108,9 +2163,11 @@ are shown (at most to the depth specified `quail-completion-max-depth')."
                  (minibuffer-scroll-window nil))
              (scroll-other-window)))
        (setq quail-current-key key)
-       (erase-buffer)
-       (insert "Possible completion and corresponding characters are:\n")
-       (quail-completion-1 key map 1)
+       (let ((inhibit-read-only t))
+         (erase-buffer)
+         (insert "Possible completion and corresponding characters are:\n")
+         (quail-completion-1 key map 1)
+         (set-buffer-modified-p nil))
        (goto-char (point-min))
        (display-buffer (current-buffer))
        (setq require-update t)))
@@ -2568,6 +2625,171 @@ KEY BINDINGS FOR CONVERSION
          (quail-update-guidance)
          ))))
 \f
+;; Add KEY (string) to the element of TABLE (char-table) for CHAR if
+;; it is not yet stored.  As a result, the element is a string or a
+;; list of strings.
+
+(defsubst quail-store-decode-map-key (table char key)
+  (let ((elt (aref table char)))
+    (if elt
+       (if (consp elt)
+           (or (member key elt)
+               (aset table char (cons key elt)))
+         (or (string= key elt)
+             (aset table char (list key elt))))
+      (aset table char key))
+    (if (and translation-table-for-input
+            (setq char (aref translation-table-for-input char)))
+       (let ((translation-table-for-input nil))
+         (quail-store-decode-map-key table char key)))))
+
+;; Helper function for quail-gen-decode-map.  Store key strings to
+;; type each character under MAP in TABLE (char-table).  MAP is an
+;; element of the current Quail map reached by typing keys in KEY
+;; (string).
+
+(defun quail-gen-decode-map1 (map key table)
+  (when (and (consp map) (listp (cdr map)))
+    (let ((trans (car map)))
+      (cond ((integerp trans)
+            (quail-store-decode-map-key table trans key))
+           ((stringp trans)
+            (dotimes (i (length trans))
+              (quail-store-decode-map-key table (aref trans i) key)))
+           ((or (vectorp trans)
+                (and (consp trans)
+                     (setq trans (cdr trans))))
+            (dotimes (i (length trans))
+              (let ((elt (aref trans i)))
+                (if (stringp elt)
+                    (if (= (length elt) 1)
+                        (quail-store-decode-map-key table (aref elt 0) key))
+                  (quail-store-decode-map-key table elt key)))))))
+    (if (> (length key) 1)
+       (dolist (elt (cdr map))
+         (quail-gen-decode-map1 (cdr elt) key table))
+      (dolist (elt (cdr map))
+       (quail-gen-decode-map1 (cdr elt) (format "%s%c" key (car elt))
+                                table)))))
+
+(put 'quail-decode-map 'char-table-extra-slots 0)
+
+;; Generate a halfly-cooked decode map (char-table) for the current
+;; Quail map.  An element for a character C is a key string or a list
+;; of a key strings to type to input C.  The lenth of key string is at
+;; most 2.  If it is 2, more keys may be required to input C.
+
+(defun quail-gen-decode-map ()
+  (let ((table (make-char-table 'quail-decode-map nil)))
+    (dolist (elt (cdr (quail-map)))
+      (quail-gen-decode-map1 (cdr elt) (string (car elt)) table))
+    table))
+
+;; Check if CHAR equals to TARGET while also trying to translate CHAR
+;; by translation-table-for-input.
+
+(defsubst quail-char-equal-p (char target)
+  (or (= char target)
+      (and translation-table-for-input
+          (setq char (aref translation-table-for-input char))
+          (= char target))))
+
+;; Helper function for quail-find-key.  Prepend key strings to type
+;; for inputting CHAR by the current input method to KEY-LIST and
+;; return the result.  MAP is an element of the current Quail map
+;; reached by typing keys in KEY.
+
+(defun quail-find-key1 (map key char key-list)
+  (let ((trans (car map))
+       (found-here nil))
+    (cond ((stringp trans)
+          (setq found-here
+                (and (= (length trans) 1)
+                     (quail-char-equal-p (aref trans 0) char))))
+         ((or (vectorp trans) (consp trans))
+          (if (consp trans)
+              (setq trans (cdr trans)))
+          (setq found-here
+                (catch 'tag
+                  (dotimes (i (length trans))
+                    (let ((target (aref trans i)))
+                      (if (integerp target)
+                          (if (quail-char-equal-p target char)
+                              (throw 'tag t))
+                        (if (and (= (length target) 1)
+                                 (quail-char-equal-p (aref target 0) char))
+                            (throw 'tag t))))))))
+           ((integerp trans)
+            (setq found-here (quail-char-equal-p trans char))))
+    (if found-here
+       (setq key-list (cons key key-list)))
+    (if (> (length key) 1)
+       (dolist (elt (cdr map))
+         (setq key-list
+               (quail-find-key1 (cdr elt) (format "%s%c" key (car elt))
+                                    char key-list))))
+    key-list))
+
+;; If non-nil, the value has the form (QUAIL-MAP . CODING-SYSTEM)
+;; where QUAIL-MAP is a quail-map of which decode map was generated
+;; while buffer-file-coding-system was CODING-SYSTEM.
+
+(defvar quail-decode-map-generated nil)
+
+(defun quail-find-key (char)
+  "Return a list of keys to type to input CHAR in the current input method.
+If CHAR is an ASCII character and can be input by typing itself, return t."
+  (let ((decode-map (or (and (or (not quail-decode-map-generated)
+                                (and (eq (car quail-decode-map-generated) (quail-map))
+                                     (eq (cdr quail-decode-map-generated)
+                                         (or buffer-file-coding-system t))))
+                            (quail-decode-map))
+                       (let ((map (quail-gen-decode-map)))
+                         (setq quail-decode-map-generated
+                               (cons (quail-map) (or buffer-file-coding-system t)))
+                         (setcar (nthcdr 10 quail-current-package) map)
+                         map)))
+       (key-list nil))
+    (if (consp decode-map)
+       (let ((str (string char)))
+         (mapc #'(lambda (elt)
+                   (if (string= str (car elt))
+                       (setq key-list (cons (cdr elt) key-list))))
+               (cdr decode-map)))
+      (let ((key-head (aref decode-map char)))
+       (if (stringp key-head)
+           (setq key-list (quail-find-key1 
+                           (quail-lookup-key key-head nil t)
+                           key-head char nil))
+         (mapc #'(lambda (elt)
+                   (setq key-list
+                         (quail-find-key1
+                          (quail-lookup-key elt nil t) elt char key-list)))
+               key-head))))
+    (or key-list
+       (and (< char 128)
+            (not (quail-lookup-key (string char) 1))))))
+
+(defun quail-show-key ()
+  "Show a list of key strings to type for inputting a character at point."
+  (interactive)
+  (or current-input-method
+      (error "No input method is activated"))
+  (or (assoc current-input-method quail-package-alist)
+      (error "The current input method does not use Quail"))
+  (let* ((char (following-char))
+        (key-list (quail-find-key char)))
+    (cond ((consp key-list)
+          (message "To input `%c', type \"%s\""
+                   char
+                   (mapconcat 'identity key-list "\", \"")))
+         ((eq key-list t)
+          (message "To input `%s', just type it"
+                   (single-key-description char)))
+         (t
+          (message "%c can't be input by the current input method" char)))))
+
+\f
 ;; Quail map generator from state transition table.
 
 (defun quail-map-from-table (table)
@@ -2673,7 +2895,7 @@ function `quail-install-map' (which see)."
         (translation-list nil)
         map)
     (while (> len 0)
-      (setq map (quail-lookup-key key len)
+      (setq map (quail-lookup-key key len t)
            len (1- len))
       (if map
          (let* ((def (quail-map-definition map))