;;; 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>
;; 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:
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))
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)
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)
(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
(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)))
(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)
(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)))
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
(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)
(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)
;; 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
(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))))
;; 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.
(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))
(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
(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)))
(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)
(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))