summaryrefslogtreecommitdiff
path: root/lisp/international/quail.el
diff options
context:
space:
mode:
Diffstat (limited to 'lisp/international/quail.el')
-rw-r--r--lisp/international/quail.el268
1 files changed, 195 insertions, 73 deletions
diff --git a/lisp/international/quail.el b/lisp/international/quail.el
index 53901ceeaa9..945c970daed 100644
--- a/lisp/international/quail.el
+++ b/lisp/international/quail.el
@@ -81,6 +81,13 @@ See the documentation of `quail-package-alist' for the format.")
(defvar quail-current-translations nil
"Cons of indices and vector of possible translations of the current key.")
+(defvar quail-current-data nil
+ "Any Lisp object holding information of current translation status.
+When a key sequence is mapped to TRANS and TRANS is a cons
+of actual translation and some Lisp object to be refered
+for translating the longer key sequence, this variable is set
+to that Lisp object.")
+
;; A flag to control conversion region. Normally nil, but if set to
;; t, it means we must start the new conversion region if new key to
;; be translated is input.
@@ -185,27 +192,26 @@ Conversion keymap is a keymap used while conversion region is active
(defun quail-use-package (package-name &rest libraries)
"Start using Quail package PACKAGE-NAME.
The remaining arguments are libraries to be loaded before using the package."
- (while libraries
- (if (not (load (car libraries) t))
- (progn
- (with-output-to-temp-buffer "*Help*"
- (princ "Quail package \"")
- (princ package-name)
- (princ "\" can't be activated\n because library \"")
- (princ (car libraries))
- (princ "\" is not in `load-path'.
+ (let ((package (quail-package package-name)))
+ (if (null package)
+ ;; Perhaps we have not yet loaded necessary libraries.
+ (while libraries
+ (if (not (load (car libraries) t))
+ (progn
+ (with-output-to-temp-buffer "*Help*"
+ (princ "Quail package \"")
+ (princ package-name)
+ (princ "\" can't be activated\n because library \"")
+ (princ (car libraries))
+ (princ "\" is not in `load-path'.
The most common case is that you have not yet installed appropriate
libraries in LEIM (Libraries of Emacs Input Method) which is
distributed separately from Emacs.
-Installation of LEIM for Quail is very simple, just copy Quail
-packages (byte-compiled Emacs Lisp files) to somewhere in your
-`load-path'.
-
LEIM is available from the same ftp directory as Emacs."))
- (error "Can't use the Quail package `%s'" package-name))
- (setq libraries (cdr libraries))))
+ (error "Can't use the Quail package `%s'" package-name))
+ (setq libraries (cdr libraries))))))
(quail-select-package package-name)
(setq current-input-method-title (quail-title))
(quail-mode 1))
@@ -295,6 +301,7 @@ This map is activated while translation region is active.")
This map is activated while convesion region is active but translation
region is not active.")
+;;;###autoload
(defun quail-define-package (name language title
&optional guidance docstring translation-keys
forget-last-selection deterministic
@@ -393,8 +400,10 @@ vs. corresponding command to be called."
forget-last-selection deterministic kbd-translate show-layout
(if create-decode-map (list 'decode-map) nil)
maximum-shortest overlay-plist update-translation-function
- conversion-keymap)))
- (register-input-method language (list name 'quail-use-package))
+ conversion-keymap))
+ ;; Update TITLE field.
+ (let ((slot (assoc name input-method-alist)))
+ (if slot (setcar (nthcdr 4 slot) docstring))))
(quail-select-package name))
;; Quail minor mode handlers.
@@ -425,10 +434,9 @@ vs. corresponding command to be called."
(if (overlayp quail-conv-overlay)
(delete-overlay quail-conv-overlay)))
-;; While translating and converting, we enter the recursive edit and
-;; exit it frequently, which results in frequent and annoying change
-;; of and annoying in mode line. To avoid it, we use a modified
-;; mode-line-format.
+;; While translating and converting, we enter and exit the recursive
+;; edit frequently, which results in frequent and annoying change of
+;; mode line. To avoid it, we use a modified mode-line-format.
(defvar quail-mode-line-format nil)
;; Return a modified mode-line-format which doesn't show the recursive
@@ -658,12 +666,15 @@ the translation. These objects are transformed to cons cells in the
format \(INDEX . VECTOR), as described above."
(and (consp object)
(let ((translation (car object)))
- (or (integerp translation) (consp translation) (null translation)
+ (or (integerp translation) (null translation)
(vectorp translation) (stringp translation)
- (symbolp translation)))
+ (symbolp translation)
+ (and (consp translation) (not (vectorp (cdr translation))))))
(let ((alist (cdr object)))
- (or (listp alist) (symbolp alist)))))
+ (or (and (listp alist) (consp (car alist)))
+ (symbolp alist)))))
+;;;###autoload
(defmacro quail-define-rules (&rest rules)
"Define translation rules of the current Quail package.
Each argument is a list of KEY and TRANSLATION.
@@ -685,6 +696,7 @@ If TRANSLATION is a Quail map or a function symbol which returns a Quail map,
(setq l (cdr l)))
map)))
+;;;###autoload
(defun quail-install-map (map)
"Install the Quail map MAP in the current Quail package.
The installed map can be referred by the function `quail-map'."
@@ -694,14 +706,18 @@ The installed map can be referred by the function `quail-map'."
(error "Invalid Quail map `%s'" map))
(setcar (cdr (cdr quail-current-package)) map))
+;;;###autoload
(defun quail-defrule (key translation &optional name)
"Add one translation rule, KEY to TRANSLATION, in the current Quail package.
KEY is a string meaning a sequence of keystrokes to be translated.
-TRANSLATION is a character, a string, a vector, a Quail map, or a function.
+TRANSLATION is a character, a string, a vector, a Quail map,
+a function, or a cons.
It it is a character, it is the sole translation of KEY.
If it is a string, each character is a candidate for the translation.
If it is a vector, each element (string or character) is a candidate
for the translation.
+If it is a cons, the car is one of the above and the cdr is a function
+to call when translating KEY.
In these cases, a key specific Quail map is generated and assigned to KEY.
If TRANSLATION is a Quail map or a function symbol which returns a Quail map,
@@ -717,10 +733,12 @@ current Quail package."
(quail-defrule-internal key translation (quail-map)))
;; Define KEY as TRANS in a Quail map MAP.
+;;;###autoload
(defun quail-defrule-internal (key trans map)
(if (null (stringp key))
"Invalid Quail key `%s'" key)
(if (not (or (numberp trans) (stringp trans) (vectorp trans)
+ (consp trans)
(symbolp trans)
(quail-map-p trans)))
(error "Invalid Quail translation `%s'" trans))
@@ -729,6 +747,7 @@ current Quail package."
(let ((len (length key))
(idx 0)
ch entry)
+ ;; Make a map for registering TRANS if necessary.
(while (< idx len)
(if (null (consp map))
;; We come here, for example, when we try to define a rule
@@ -766,41 +785,43 @@ current Quail package."
(setcdr entry (append trans (cdr map)))))
(setcar map trans)))))
-(defun quail-get-translation (map key len)
- "Return the translation specified in Quail map MAP for KEY of length LEN.
+(defun quail-get-translation (def key len)
+ "Return the translation specified as DEF for KEY of length LEN.
The translation is either a character or a cons of the form (INDEX . VECTOR),
where VECTOR is a vector of candidates (character or string) for
the translation, and INDEX points into VECTOR to specify the currently
selected translation."
- (let ((def (car map)))
- (if (and def (symbolp def))
- ;; DEF is a symbol of a function which returns valid translation.
- (setq def (funcall def key len)))
- (cond
- ((or (integerp def) (consp def))
- def)
-
- ((null def)
- ;; No translation.
- nil)
-
- ((stringp def)
- ;; Each character in DEF is a candidate of translation. Reform
- ;; it as (INDEX . VECTOR).
- (setq def (string-to-vector def))
- ;; But if the length is 1, we don't need vector but a single
- ;; character as the translation.
- (if (= (length def) 1)
- (aref def 0)
- (cons 0 def)))
-
- ((vectorp def)
- ;; Each element (string or character) in DEF is a candidate of
- ;; translation. Reform it as (INDEX . VECTOR).
- (cons 0 def))
-
- (t
- (error "Invalid object in Quail map: %s" def)))))
+ (if (and def (symbolp def))
+ ;; DEF is a symbol of a function which returns valid translation.
+ (setq def (funcall def key len)))
+ (if (and (consp def) (not (vectorp (cdr def))))
+ (setq def (car def)))
+
+ (cond
+ ((or (integerp def) (consp def))
+ def)
+
+ ((null def)
+ ;; No translation.
+ nil)
+
+ ((stringp def)
+ ;; Each character in DEF is a candidate of translation. Reform
+ ;; it as (INDEX . VECTOR).
+ (setq def (string-to-vector def))
+ ;; But if the length is 1, we don't need vector but a single
+ ;; candidate as the translation.
+ (if (= (length def) 1)
+ (aref def 0)
+ (cons 0 def)))
+
+ ((vectorp def)
+ ;; Each element (string or character) in DEF is a candidate of
+ ;; translation. Reform it as (INDEX . VECTOR).
+ (cons 0 def))
+
+ (t
+ (error "Invalid object in Quail map: %s" def))))
(defun quail-lookup-key (key len)
"Lookup KEY of length LEN in the current Quail map and return the definition.
@@ -808,7 +829,7 @@ The returned value is a Quail map specific to KEY."
(let ((idx 0)
(map (quail-map))
(kbd-translate (quail-kbd-translate))
- slot ch translation)
+ slot ch translation def)
(while (and map (< idx len))
(setq ch (if kbd-translate (quail-keyboard-translate (aref key idx))
(aref key idx)))
@@ -819,12 +840,22 @@ The returned value is a Quail map specific to KEY."
(if (and (cdr slot) (symbolp (cdr slot)))
(setcdr slot (funcall (cdr slot) key idx)))
(setq map (cdr slot)))
- (if (and map (setq translation (quail-get-translation map key len)))
+ (setq def (car map))
+ (if (and map (setq translation (quail-get-translation def key len)))
(progn
- ;; We may have to reform car part of MAP.
- (if (not (equal (car map) translation))
- (setcar map translation))
- (if (consp translation)
+ (if (and (consp def) (not (vectorp (cdr def))))
+ (progn
+ (if (not (equal (car def) translation))
+ ;; We must reflect TRANSLATION to car part of DEF.
+ (setcar def translation))
+ (setq quail-current-data
+ (if (functionp (cdr def))
+ (funcall (cdr def))
+ (cdr def))))
+ (if (not (equal def translation))
+ ;; We must reflect TRANSLATION to car part of MAP.
+ (setcar map translation)))
+ (if (and (consp translation) (vectorp (cdr translation)))
(progn
(setq quail-current-translations translation)
(if (quail-forget-last-selection)
@@ -1003,6 +1034,8 @@ sequence counting from the head."
def ch)
(if map
(let ((def (car map)))
+ (if (and (consp def) (not (vectorp (cdr def))))
+ (setq def (car def)))
(setq quail-current-str
(if (consp def) (aref (cdr def) (car def)) def))
;; Return t only if we can terminate the current translation.
@@ -1024,6 +1057,8 @@ sequence counting from the head."
(quail-maximum-shortest)
(>= len 4)
(setq def (car (quail-lookup-key quail-current-key (- len 2))))
+ (if (and (consp def) (not (vectorp (cdr def))))
+ (setq def (car def)))
(quail-lookup-key (substring quail-current-key -2) 2))
;; Now the sequence is "...ABCD", which can be split into
;; "...AB" and "CD..." to get valid translation.
@@ -1239,10 +1274,10 @@ the bottommost ordinary window."
;; Delete the window for guidance buffer.
(if (or (null input-method-tersely-flag)
(not (eq (selected-window) (minibuffer-window))))
- (progn
- (setq win (get-buffer-window quail-guidance-buf))
- (set-window-dedicated-p win nil)
- (delete-window win))))))
+ (if (setq win (get-buffer-window quail-guidance-buf))
+ (progn
+ (set-window-dedicated-p win nil)
+ (delete-window win)))))))
(defun quail-update-guidance ()
"Update the Quail guidance buffer and completion buffer (if displayed now)."
@@ -1309,8 +1344,11 @@ the bottommost ordinary window."
(defun quail-show-translations ()
"Show the current possible translations."
- (let ((key quail-current-key)
- (map (quail-lookup-key quail-current-key (length quail-current-key))))
+ (let* ((key quail-current-key)
+ (map (quail-lookup-key quail-current-key (length quail-current-key)))
+ (def (car map)))
+ (if (and (consp def) (not (vectorp (cdr def))))
+ (setq def (car def)))
(save-excursion
(set-buffer quail-guidance-buf)
(erase-buffer)
@@ -1328,9 +1366,9 @@ the bottommost ordinary window."
(insert "]")))
;; Show list of translations.
- (if (consp (car map))
- (let* ((idx (car (car map)))
- (translations (cdr (car map)))
+ (if (and (not (quail-deterministic)) (consp def))
+ (let* ((idx (car def))
+ (translations (cdr def))
(from (* (/ idx 10) 10))
(to (min (+ from 10) (length translations))))
(indent-to 10)
@@ -1393,7 +1431,7 @@ All possible translations of the current key and whole possible longer keys
;; indentation INDENT.
(defun quail-completion-list-translations (map key indent)
(let ((translations
- (quail-get-translation map key (length key))))
+ (quail-get-translation (car map) key (length key))))
(if (integerp translations)
(insert "(1/1) 1." translations "\n")
;; We need only vector part.
@@ -1492,7 +1530,7 @@ key binding
(insert ch)
(let* ((map (cdr (assq ch (cdr (quail-map)))))
(translation (and map (quail-get-translation
- map (char-to-string ch) 1))))
+ (car map) (char-to-string ch) 1))))
(if (integerp translation)
(insert translation)
(if (consp translation)
@@ -1545,6 +1583,90 @@ key binding
(set-buffer-modified-p nil))
(display-buffer buf)))
+
+(defvar quail-directory-name "quail"
+ "Name of Quail directory which cotains Quail packages.
+This is a sub-directory of LEIM directory.")
+
+;;;###autoload
+(defun quail-update-leim-list-file (dirname)
+ "Update entries for Quail packages in LEIM list file of directory DIRNAME.
+LEIM is a library of Emacs input method."
+ (interactive "FDirectory of LEIM: ")
+ (setq dirname (file-name-as-directory (expand-file-name dirname)))
+ (let ((quail-dir (concat dirname quail-directory-name))
+ (filename (concat dirname leim-list-file-name))
+ list-buf pkg-list pkg-buf pos)
+ (if (not (file-exists-p quail-dir))
+ nil
+ (if (not (file-readable-p quail-dir))
+ (message "Can't write to file \"%s\"" filename)
+ (if (not (file-writable-p filename))
+ (message "Can't write to file \"%s\"" filename)
+ (setq list-buf (find-file-noselect filename))
+ (setq pkg-list (directory-files quail-dir 'full ".*\\.el$" 'nosort))
+ (message "Updating %s ..." filename)
+
+ ;; At first, clean up the file.
+ (save-excursion
+ (set-buffer list-buf)
+ (goto-char 1)
+
+ ;; Insert the correct header.
+ (if (looking-at (regexp-quote leim-list-header))
+ (goto-char (match-end 0))
+ (insert leim-list-header))
+ (setq pos (point))
+ (if (not (re-search-forward leim-list-entry-regexp nil t))
+ nil
+
+ ;; Remove garbages after the header.
+ (goto-char (match-beginning 0))
+ (if (< pos (point))
+ (delete-region pos (point)))
+
+ ;; Remove all entries for Quail.
+ (while (re-search-forward leim-list-entry-regexp nil 'move)
+ (goto-char (match-beginning 0))
+ (setq pos (point))
+ (let ((form (read list-buf)))
+ (if (equal (nth 3 form) ''quail-use-package)
+ (progn
+ (if (eolp) (forward-line 1))
+ (delete-region pos (point))))))))
+
+ ;; Insert entries for Quail.
+ (while pkg-list
+ (message "Checking %s ..." (car pkg-list))
+ (setq pkg-buf (find-file-noselect (car pkg-list)))
+ (save-excursion
+ (set-buffer pkg-buf)
+ (while (search-forward "(quail-define-package" nil t)
+ (goto-char (match-beginning 0))
+ (let ((form (read (current-buffer))))
+ (save-excursion
+ (set-buffer list-buf)
+ (insert (format "(register-input-method
+ %S %S '%s
+ %S %S
+ %S)\n" (nth 1 form) ; PACKAGE-NAME
+ (nth 2 form) ; LANGUAGE
+ 'quail-use-package ; ACTIVATE-FUNC
+ (nth 3 form) ; PACKAGE-TITLE
+ (progn ; PACKAGE-DESCRIPTION (one line)
+ (string-match ".*" (nth 5 form))
+ (match-string 0 (nth 5 form)))
+ (file-relative-name ; PACKAGE-FILENAME
+ (file-name-sans-extension (car pkg-list)) dirname)
+ ))))))
+ (kill-buffer pkg-buf)
+ (setq pkg-list (cdr pkg-list)))
+ (save-excursion
+ (set-buffer list-buf)
+ (setq buffer-file-coding-system 'iso-2022-7bit)
+ (save-buffer))
+ (kill-buffer list-buf)
+ (message "Updating %s ... done" (buffer-file-name list-buf)))))))
;;
(provide 'quail)