1 ;;; egg-cnv.el --- Conversion Backend in Egg Input Method Architecture
3 ;; Copyright (C) 1999,2000 PFU LIMITED
5 ;; Author: NIIBE Yutaka <gniibe@chroot.org>
6 ;; KATAYAMA Yoshio <kate@pfu.co.jp>
8 ;; Maintainer: TOMURA Satoru <tomura@etl.go.jp>
10 ;; Keywords: mule, multilingual, input method
12 ;; This file is part of EGG.
14 ;; EGG is free software; you can redistribute it and/or modify
15 ;; it under the terms of the GNU General Public License as published by
16 ;; the Free Software Foundation; either version 2, or (at your option)
19 ;; EGG is distributed in the hope that it will be useful,
20 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
21 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 ;; GNU General Public License for more details.
24 ;; You should have received a copy of the GNU General Public License
25 ;; along with GNU Emacs; see the file COPYING. If not, write to the
26 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
27 ;; Boston, MA 02111-1307, USA.
36 (defgroup egg-conv nil
37 "Conversion Backend Interface of Tamago 4."
40 (defcustom egg-conversion-wrap-select t
41 "*Candidate selection wraps around to first candidate, if non-NIL.
42 Otherwise stop at the last candidate."
43 :group 'egg-conv :type 'boolean)
45 (defcustom egg-conversion-auto-candidate-menu 0
46 "*Automatically enter the candidate selection mode at N times
47 next/previous-candidate, if positive number N."
48 :group 'egg-conv :type 'integer)
50 (defcustom egg-conversion-auto-candidate-menu-show-all nil
51 "*Enter show all candiate mode when automatic candidate selection
53 :group 'egg-conv :type 'boolean)
55 (defcustom egg-conversion-sort-by-converted-string nil
56 "*Sort candidate list by converted string on candidate selection
58 :group 'egg-conv :type 'boolean)
60 (defcustom egg-conversion-fence-invisible nil
61 "*Make fence marks invisible, if non-NIL."
62 :group 'egg-conv :type 'boolean)
64 (defcustom egg-conversion-fence-open "|"
65 "*String of conversion fence start mark. (should not be null string)"
66 :group 'egg-conv :type '(string :valid-regexp ".+"))
68 (defcustom egg-conversion-fence-close "|"
69 "*String of conversion fence end mark. (should not be null string)"
70 :group 'egg-conv :type '(string :valid-regexp ".+"))
72 (defcustom egg-conversion-face nil
73 "*Face (or alist of languages and faces) of text in conversion fences."
76 (repeat :tag "Language-Face alist"
77 (cons :tag "Language-Face"
78 (choice :tag "Language"
83 (const :tag "Default" t)
84 (symbol :tag "Other"))
87 (defcustom egg-conversion-major-separator " "
88 "*Major clause seperator"
89 :group 'egg-conv :type 'string)
91 (defcustom egg-conversion-minor-separator "-"
92 "*Minor clause seperator"
93 :group 'egg-conv :type 'string)
95 (defcustom egg-startup-file ".eggrc"
96 "*Egg startup file name."
97 :group 'egg-conv :type 'string)
99 (defcustom egg-startup-file-search-path '("~")
100 "*List of directories to search for egg-startup-file (default .eggrc)."
101 :group 'egg-conv :type '(repeat string))
105 (no-rcfile "no egg-startup-file on %S")
106 (rcfile-error "error occured in egg-startup-file")
107 (candidate "candidates:")
108 (register-str "Chinese character:")
109 (register-yomi "word registration ``%s'' pronunciation:")
110 (registered "dictionary entry ``%s''(%s: %s) is registerd at %s"))
112 (no-rcfile "%S
\e$B>e$K
\e(B egg-startup-file
\e$B$,$"$j$^$;$s
\e(B")
113 (rcfile-error "egg-startup-file
\e$B$G%(%i!<$,$"$j$^$7$?
\e(B")
114 (candidate "
\e$B8uJd
\e(B:")
115 (register-str "
\e$B4A;z
\e(B:")
116 (register-yomi "
\e$B<-=qEPO?!X
\e(B%s
\e$B!Y
\e(B
\e$BFI$_
\e(B:")
117 (registered "
\e$B<-=q9`L\!X
\e(B%s
\e$B!Y
\e(B(%s: %s)
\e$B$r
\e(B %s
\e$B$KEPO?$7$^$7$?
\e(B"))
119 (no-rcfile "
\e$ATZ
\e(B %S
\e$AIOC;SP
\e(B egg-startup-file")
120 (rcfile-error "
\e$ATZ6AH!
\e(B egg-startup-file
\e$AJ1#,SP3v4m7"IzAK
\e(B")
121 (candidate "
\e$A:r29
\e(B:")
122 (register-str "
\e$A::WV
\e(B:")
123 (register-yomi "
\e$A4G5d5GB<!:
\e(B%s
\e$A!;
\e(B
\e$A6A7(
\e(B:")
124 (registered "
\e$A4G5dOnD?!:
\e(B%s
\e$A!;
\e(B(%s: %s)
\e$ARQ1;5GB<5=
\e(B %s
\e$AVPAK
\e(B"))
126 (no-rcfile "
\e$(GGc
\e(B %S
\e$(GD8JtH4
\e(B egg-startup-file")
127 (rcfile-error "
\e$(GGc{tL=
\e(B egg-startup-file
\e$(GUk!"H4Exrc`uFmD'
\e(B")
128 (register-str "
\e$(GiGGs
\e(B:")
129 (candidate "
\e$(GT7fP
\e(B:")
130 (register-yomi "
\e$(Gy0L(`trg!Z
\e(B%s
\e$(G![
\e(B
\e$(G{tNN
\e(B:")
131 (registered "
\e$(Gy0L(bzFx!Z
\e(B%s
\e$(G![
\e(B(%s: %s)
\e$(GDX]7`trgL/
\e(B %s
\e$(GDcD'
\e(B"))
133 (no-rcfile "%S
\e$(C?!
\e(B egg-startup-file
\e$(C@L
\e(B
\e$(C>x@>4O4Y
\e(B")
134 (rcfile-error "egg-startup-file
\e$(C?!
\e(B
\e$(C?!7/0!
\e(B
\e$(C9_;}G_@>4O4Y
\e(B")
135 (candidate "
\e$(CHD:8
\e(B:")
136 (register-str "
\e$(CGQ@Z
\e(B:")
137 (register-yomi "
\e$(C;g@|5n7O!:
\e(B%s
\e$(C!;
\e(B
\e$(C569}
\e(B:")
138 (registered "
\e$(C;g@|GW8q!:
\e(B%s
\e$(C!;
\e(B(%s: %s)
\e$(C@;
\e(B %s
\e$(C?!
\e(B
\e$(C5n7OG_@>4O4Y
\e(B"))))
141 ;; <backend-alist> ::= ( ( <language> ( <stage>... )... )... )
142 ;; <stage> ::= ( <backend> <backend-for-reconvert>... )
143 ;; <backend-for-reconvert> ::= <backend>
144 ;; <backend> ::= symbol
147 (defvar egg-conversion-backend-alist nil)
148 (make-variable-buffer-local 'egg-conversion-backend-alist)
149 (put 'egg-conversion-backend-alist 'permanent-local t)
151 (defun egg-set-conversion-backend (backend-alist &optional force)
152 (let (pair lang backend-set)
154 (setq lang (caar backend-alist)
155 backend-set (cdar backend-alist)
156 backend-alist (cdr backend-alist)
157 pair (assq lang egg-conversion-backend-alist))
160 (setq egg-conversion-backend-alist
161 (cons (cons lang backend-set) egg-conversion-backend-alist)))
163 (setcdr pair backend-set))))))
165 (defun egg-get-conversion-backend (language n use-default)
167 (cond ((null n) (setq n 0))
168 ((null (integerp n)) (setq n 1)))
169 (setq backend (nth (1+ n) (assq language egg-conversion-backend-alist)))
171 (and backend (cons 0 (mapcar 'copy-sequence backend)))
172 (and use-default (cons 0 egg-default-conversion-backend)))))
174 (defsubst egg-default-conversion-backend-p (backend)
175 (eq (cdr backend) egg-default-conversion-backend))
177 (defsubst egg-get-current-backend (backend)
178 (car (nth (car backend) (cdr backend))))
180 (defsubst egg-get-reconvert-backend (backend &optional n)
181 (cond ((null n) (setq n 0))
182 ((null (integerp n)) (setq n 1)))
183 (nth (1+ n) (nth (car backend) (cdr backend))))
185 (defmacro egg-bunsetsu-info () ''intangible)
187 (defsubst egg-get-bunsetsu-info (p &optional object)
188 (get-text-property p (egg-bunsetsu-info) object))
190 (defsubst egg-get-backend (p &optional object)
191 (get-text-property p 'egg-backend object))
193 (defsubst egg-get-language (p &optional object)
194 (get-text-property p 'egg-lang object))
196 (defsubst egg-get-bunsetsu-last (p &optional object)
197 (get-text-property p 'egg-bunsetsu-last object))
199 (defsubst egg-get-major-continue (p &optional object)
200 (get-text-property p 'egg-major-continue object))
202 (defsubst egg-get-char-size (p &optional object)
203 (get-text-property p 'egg-char-size object))
205 ;; <bunsetsu-info> ::= ( <backend> . <backend-dependent-info> )
207 (defsubst egg-bunsetsu-create (backend info)
210 (defsubst egg-bunsetsu-get-backend (bunsetsu)
212 (defsubst egg-bunsetsu-set-backend (bunsetsu backend)
213 (setcar bunsetsu backend))
215 (defsubst egg-bunsetsu-get-info (bunsetsu)
217 (defsubst egg-bunsetsu-set-info (bunsetsu info)
218 (setcdr bunsetsu info))
220 (defun egg-conversion-fence-p ()
221 (and (egg-get-backend (point))
222 (get-text-property (point) 'read-only)))
224 (defvar egg-finalize-backend-list nil)
226 (defun egg-set-finalize-backend (func-list)
227 (mapcar (lambda (func)
229 (null (memq func egg-finalize-backend-list)))
230 (setq egg-finalize-backend-list
231 (cons func egg-finalize-backend-list))))
234 (defmacro egg-define-backend-functions (list)
238 (let* ((func (car def))
240 (backend (car args)))
241 (cond ((eq backend 'bunsetsu)
242 (setq backend `(egg-bunsetsu-get-backend ,backend)))
243 ((eq backend 'bunsetsu-list)
244 (setq backend `(egg-bunsetsu-get-backend (car ,backend)))))
246 (let ((func (get ,backend ',func)))
248 (funcall func ,@args))))))
251 (egg-define-backend-functions
252 ((egg-start-conversion (backend source-string context))
253 (egg-get-bunsetsu-source (bunsetsu))
254 (egg-get-bunsetsu-converted (bunsetsu))
255 (egg-get-source-language (bunsetsu))
256 (egg-get-converted-language (bunsetsu))
257 (egg-major-bunsetsu-continue-p (bunsetsu))
258 (egg-list-candidates (bunsetsu-list prev-b next-b major))
259 (egg-decide-candidate (bunsetsu-list candidate-pos prev-b next-b))
260 (egg-special-candidate (bunsetsu-list prev-b next-b major type))
261 (egg-change-bunsetsu-length (bunsetsu-list prev-b next-b length major))
262 (egg-bunsetsu-combinable-p (bunsetsu next-b))
263 (egg-end-conversion (bunsetsu-list abort))
264 (egg-word-inspection (bunsetsu))
265 (egg-word-registration (backend source converted))))
267 (defun egg-finalize-backend ()
268 (run-hooks 'egg-finalize-backend-list))
270 (setplist 'egg-conversion-backend-noconv
271 '(egg-start-conversion egg-start-conversion-noconv
272 egg-get-bunsetsu-source egg-get-bunsetsu-source-noconv
273 egg-get-bunsetsu-converted egg-get-bunsetsu-converted-noconv
274 egg-get-source-language egg-get-source-language-noconv
275 egg-get-converted-language egg-get-converted-language-noconv
276 egg-end-conversion egg-end-conversion-noconv))
278 (defun egg-start-conversion-noconv (backend yomi-string context)
279 (let ((string (copy-sequence yomi-string))
280 (language (egg-get-language 0 yomi-string)))
281 (egg-remove-all-text-properties 0 (length string) string)
282 (list (egg-bunsetsu-create backend (vector string language)))))
284 (defun egg-get-bunsetsu-source-noconv (bunsetsu)
285 (aref (egg-bunsetsu-get-info bunsetsu) 0))
286 (defun egg-get-bunsetsu-converted-noconv (bunsetsu)
287 (aref (egg-bunsetsu-get-info bunsetsu) 0))
288 (defun egg-get-source-language-noconv (bunsetsu)
289 (aref (egg-bunsetsu-get-info bunsetsu) 1))
290 (defun egg-get-converted-language-noconv (bunsetsu)
291 (aref (egg-bunsetsu-get-info bunsetsu) 1))
292 (defun egg-end-conversion-noconv (bunsetsu-list abort)
295 (defconst egg-default-conversion-backend '((egg-conversion-backend-noconv)))
297 (defun egg-convert-region (start end &optional context nth-backend)
298 (interactive "r\ni\nP")
299 (let ((source (buffer-substring start end))
300 backend backend-source-list converted converted-list
301 lang len s success abort)
305 (delete-region start end)
306 (let ((inhibit-read-only t))
307 (its-define-select-keys egg-conversion-map)
309 (setq s (copy-sequence egg-conversion-fence-open)
313 (set-text-properties 0 len (list 'read-only t
318 (put-text-property 0 len 'egg-context context s))
319 (if egg-conversion-fence-invisible
320 (put-text-property 0 len 'invisible t s))
322 (setq s (copy-sequence egg-conversion-fence-close)
324 (set-text-properties 0 len '(read-only t rear-nonsticky t egg-end t) s)
325 (if egg-conversion-fence-invisible
326 (put-text-property 0 len 'invisible t s))
331 (setq source (copy-sequence source))
332 (egg-separate-languages source)
333 (setq backend-source-list (egg-assign-backend source nth-backend))
334 (while (and (null abort) backend-source-list)
335 (setq backend (car (car backend-source-list))
336 lang (nth 1 (car backend-source-list))
337 source (nth 2 (car backend-source-list))
338 backend-source-list (cdr backend-source-list))
339 (condition-case error
341 (setq converted (egg-start-conversion
342 (egg-get-current-backend backend)
345 (egg-error "no conversion result"))
346 (setq converted-list (nconc converted-list
347 (list (cons backend converted)))
349 (or (egg-default-conversion-backend-p backend)
354 (delq t (mapcar (lambda (s)
355 (egg-default-conversion-backend-p
357 backend-source-list))))
358 (message "egg %s backend: %s"
359 (if (cdr lang) lang (car lang))
360 (nth (if (eq (car error) 'quit) 0 1) error))
365 (format "egg %s backend %s: continue? "
366 lang (nth (if (eq (car error) 'quit) 0 1) error)))
368 (setq backend (egg-get-conversion-backend nil 0 t)
369 converted (egg-start-conversion
370 (egg-get-current-backend backend)
372 converted-list (nconc converted-list
373 (list (cons backend converted)))
377 (delete-region start end)
378 (while converted-list
379 (egg-insert-bunsetsu-list (caar converted-list) (cdar converted-list)
380 (or (null (cdr converted-list)) 'continue))
381 (setq converted-list (cdr converted-list)))
384 (egg-abort-conversion))
386 (egg-exit-conversion)))))))
388 (defun egg-separate-languages (str &optional last-lang)
389 (let (lang last-chinese
390 (len (length str)) i j l)
391 ;; 1st pass -- mark undefined Chinese part
392 (if (or (eq last-lang 'Chinese-GB) (eq last-lang 'Chinese-CNS))
393 (setq last-chinese last-lang))
396 (setq j (egg-next-single-property-change i 'egg-lang str len))
397 (if (null (egg-get-language i str))
399 (setq c (egg-string-to-char-at str i)
400 cset (char-charset c))
402 ((eq cset 'chinese-sisheng)
403 (egg-string-match-charset 'chinese-sisheng str i)
404 (setq l (match-end 0)
407 ((setq l (egg-chinese-syllable str i))
411 (if (eq (string-match "[\0-\177\240-\377]+" str (1+ i)) (1+ i))
412 (setq j (match-end 0))
415 (eq (char-charset (egg-string-to-char-at str j))
417 (setq j (max (1+ i) (- j 6))))
419 ((eq cset 'composition)
420 (setq j (+ i (egg-char-bytes c))
421 lang (egg-charset-to-language
423 (car (decompose-composite-char c 'list))))))
425 (egg-string-match-charset cset str i)
426 (setq j (match-end 0)
427 lang (egg-charset-to-language cset))))
429 (put-text-property i j 'egg-lang lang str))))
431 ;; 2nd pass -- set language property
434 (setq lang (egg-get-language i str))
437 (setq lang (or last-lang
438 (egg-next-part-lang str i))))
439 ((equal lang 'Chinese)
440 (setq lang (or last-chinese
441 (egg-next-chinese-lang str i)))))
442 (setq last-lang lang)
443 (if (or (eq lang 'Chinese-GB) (eq lang 'Chinese-CNS))
444 (setq last-chinese lang))
446 i (egg-next-single-property-change i 'egg-lang str len))
447 (egg-remove-all-text-properties j i str)
448 (put-text-property j i 'egg-lang lang str))))
450 ;;; Should think again the interface to language-info-alist
451 (defun egg-charset-to-language (charset)
452 (let ((list language-info-alist))
454 (null (memq charset (assq 'charset (car list)))))
455 (setq list (cdr list)))
457 (intern (car (car list))))))
459 (defun egg-next-part-lang (str pos)
460 (let ((lang (get-text-property
461 (egg-next-single-property-change pos 'egg-lang str (length str))
463 (if (eq lang 'Chinese)
464 (egg-next-chinese-lang str pos)
467 egg-default-language))))
469 (defun egg-next-chinese-lang (str pos)
470 (let ((len (length str)) lang)
471 (while (and (< pos len) (null lang))
472 (setq pos (egg-next-single-property-change pos 'egg-lang str len)
473 lang (egg-get-language pos str))
474 (if (null (or (eq lang 'Chinese-GB)
475 (eq lang 'Chinese-CNS)))
479 ((eq its-current-language 'Chinese-GB) 'Chinese-GB)
480 ((eq its-current-language 'Chinese-CNS) 'Chinese-CNS)
481 ((eq egg-default-language 'Chinese-GB) 'Chinese-GB)
482 ((eq egg-default-language 'Chinese-CNS) 'Chinese-CNS)
486 ;; return value ::= ( (<backend> ( <lang>... ) <source string> )... )
488 (defun egg-assign-backend (source n)
489 (let ((len (length source))
491 j s lang backend retval)
493 (setq j (egg-next-single-property-change i 'egg-lang source len)
494 s (substring source i j)
495 lang (egg-get-language 0 s)
496 backend (egg-get-conversion-backend lang n t))
497 (egg-remove-all-text-properties 0 (- j i) s)
498 (put-text-property 0 (- j i) 'egg-lang lang s)
499 (setq retval (nconc retval (list (list backend (list lang) s)))
504 (if (or (egg-default-conversion-backend-p (car (car retval)))
505 (null (equal (car (car retval)) (car (nth 1 retval)))))
506 (setq retval (cdr retval))
507 (nconc (nth 1 (car retval)) (nth 1 (nth 1 retval)))
508 (setcar (nthcdr 2 (car retval))
509 (concat (nth 2 (car retval)) (nth 2 (nth 1 retval))))
510 (setcdr retval (cddr retval)))))))
512 (defun egg-search-file (filename path)
514 (if (file-name-directory filename)
515 (setq file (substitute-in-file-name (expand-file-name filename))
516 file (and (file-readable-p file) file))
517 (while (and (null file) path)
518 (if (stringp (car path))
519 (setq file (substitute-in-file-name
520 (expand-file-name filename (car path)))
521 file (and (file-exists-p file) file)))
522 (setq path (cdr path)))
525 (defvar egg-default-startup-file "eggrc"
526 "Egg startup file name (system default)")
528 (defun egg-load-startup-file (backend lang)
529 (let ((eggrc (or (egg-search-file egg-startup-file
530 egg-startup-file-search-path)
531 (egg-search-file egg-default-startup-file load-path))))
533 (condition-case error
534 (let ((egg-backend-type backend) (egg-language lang))
537 (message "%s: %s" (car error)
538 (mapconcat (lambda (s) (format "%S" s)) (cdr error) ", "))
539 (egg-error 'rcfile-error))
541 (egg-error 'rcfile-error)))
542 (egg-error 'no-rcfile egg-startup-file-search-path))))
544 (defun egg-get-conversion-face (lang)
545 (if (null (consp egg-conversion-face))
547 (cdr (or (assq lang egg-conversion-face)
548 (assq t egg-conversion-face)))))
550 (defvar egg-conversion-map
551 (let ((map (make-sparse-keymap))
554 (define-key map (vector i) 'egg-exit-conversion-unread-char)
556 (define-key map "\C-@" 'egg-decide-first-char)
557 (define-key map [?\C-\ ] 'egg-decide-first-char)
558 (define-key map "\C-a" 'egg-beginning-of-conversion-buffer)
559 (define-key map "\C-b" 'egg-backward-bunsetsu)
560 (define-key map "\C-c" 'egg-abort-conversion)
561 (define-key map "\C-e" 'egg-end-of-conversion-buffer)
562 (define-key map "\C-f" 'egg-forward-bunsetsu)
563 (define-key map "\C-h" 'egg-help-command)
564 (define-key map "\C-i" 'egg-shrink-bunsetsu-major)
565 (define-key map "\C-k" 'egg-decide-before-point)
566 ;; (define-key map "\C-l" 'egg-exit-conversion) ; Don't override C-L
567 (define-key map "\C-m" 'egg-exit-conversion)
568 (define-key map "\C-n" 'egg-next-candidate-major)
569 (define-key map "\C-o" 'egg-enlarge-bunsetsu-major)
570 (define-key map "\C-p" 'egg-previous-candidate-major)
571 (define-key map "\C-r" 'egg-reconvert-bunsetsu)
572 (define-key map "\C-t" 'egg-toroku-bunsetsu)
573 (define-key map "\C-v" 'egg-inspect-bunsetsu)
574 (define-key map "\M-i" 'egg-shrink-bunsetsu-minor)
575 (define-key map "\M-n" 'egg-next-candidate-minor)
576 (define-key map "\M-o" 'egg-enlarge-bunsetsu-minor)
577 (define-key map "\M-p" 'egg-previous-candidate-minor)
578 (define-key map "\M-r" 'egg-reconvert-bunsetsu-from-source)
579 (define-key map "\M-s" 'egg-select-candidate-major)
580 (define-key map "\M-v" 'egg-toggle-inspect-mode)
581 (define-key map "\M-z" 'egg-select-candidate-minor)
582 (define-key map "\e\C-s" 'egg-select-candidate-list-all-major)
583 (define-key map "\e\C-z" 'egg-select-candidate-list-all-minor)
584 (define-key map [return] 'egg-exit-conversion)
585 (define-key map [right] 'egg-forward-bunsetsu)
586 (define-key map [left] 'egg-backward-bunsetsu)
587 (define-key map [up] 'egg-previous-candidate)
588 (define-key map [down] 'egg-next-candidate)
589 (define-key map [backspace] 'egg-abort-conversion)
590 (define-key map [clear] 'egg-abort-conversion)
591 (define-key map [delete] 'egg-abort-conversion)
592 (define-key map " " 'egg-next-candidate)
593 (define-key map "/" 'egg-exit-conversion)
594 (define-key map "\M-h" 'egg-hiragana)
595 (define-key map "\M-k" 'egg-katakana)
596 (define-key map "\M-P" 'egg-pinyin)
597 (define-key map "\M-Z" 'egg-zhuyin)
598 (define-key map "\M-H" 'egg-hangul)
600 "Keymap for EGG Conversion mode.")
601 (fset 'egg-conversion-map egg-conversion-map)
603 (defvar egg-conversion-mode nil)
604 (make-variable-buffer-local 'egg-conversion-mode)
605 (put 'egg-conversion-mode 'permanent-local t)
607 (or (assq 'egg-conversion-mode egg-sub-mode-map-alist)
608 (setq egg-sub-mode-map-alist (cons
609 '(egg-conversion-mode . egg-conversion-map)
610 egg-sub-mode-map-alist)))
612 (defun egg-conversion-enter/leave-fence (&optional old new)
613 (setq egg-conversion-mode (egg-conversion-fence-p)))
615 (add-hook 'egg-enter/leave-fence-hook 'egg-conversion-enter/leave-fence)
617 (defun egg-exit-conversion-unread-char ()
619 (setq egg-context (egg-exit-conversion)
620 unread-command-events (list last-command-event)
621 this-command 'egg-use-context))
623 (defun egg-make-bunsetsu (backend bunsetsu last)
624 (let* ((converted (copy-sequence (egg-get-bunsetsu-converted bunsetsu)))
625 (language (egg-get-converted-language bunsetsu))
626 (continue (and (null last) (egg-major-bunsetsu-continue-p bunsetsu)))
627 (face (egg-get-conversion-face language))
629 (setq len1 (length converted))
631 (setq converted (concat converted
633 egg-conversion-minor-separator
634 egg-conversion-major-separator))))
635 (setq len (length converted))
636 (egg-remove-all-text-properties 0 len converted)
637 (add-text-properties 0 len
639 (egg-bunsetsu-info) bunsetsu
642 'egg-bunsetsu-last last
643 'egg-major-continue continue
644 'point-entered 'egg-enter/leave-fence
645 'point-left 'egg-enter/leave-fence
646 'modification-hooks '(egg-modify-fence))
649 (egg-set-face 0 len1 face converted))
652 (defun egg-insert-bunsetsu-list (backend bunsetsu-list &optional last before)
653 (let ((len (length bunsetsu-list)))
654 (funcall (if before 'insert-before-markers 'insert)
658 (egg-make-bunsetsu backend b (and (= len 0) last)))
659 bunsetsu-list nil))))
661 (defun egg-beginning-of-conversion-buffer (n)
665 (egg-end-of-conversion-buffer 1))
666 ((null (get-text-property (1- (point)) 'egg-start))
667 (goto-char (previous-single-property-change (point) 'egg-start)))))
669 (defun egg-end-of-conversion-buffer(n)
673 (egg-beginning-of-conversion-buffer 1))
675 (goto-char (next-single-property-change (point) 'egg-end))
678 (defun egg-backward-bunsetsu (n)
681 (null (get-text-property (1- (point)) 'egg-start)))
685 (signal 'beginning-of-buffer nil)))
687 (defun egg-forward-bunsetsu (n)
690 (null (get-text-property (point) 'egg-end)))
695 (signal 'end-of-buffer nil)))
697 (defun egg-get-bunsetsu-tail (b)
698 (nth (1- (length b)) b))
700 (defun egg-previous-bunsetsu-point (p &optional n obj lim)
703 (setq p (previous-single-property-change p (egg-bunsetsu-info) obj lim)
707 (defun egg-next-bunsetsu-point (p &optional n obj lim)
710 (setq p (egg-next-single-property-change p (egg-bunsetsu-info) obj lim)
714 (defun egg-get-previous-bunsetsu (p)
715 (and (null (egg-get-bunsetsu-last (1- p)))
716 (egg-get-bunsetsu-info (1- p))))
718 (defun egg-get-previous-major-bunsetsu (p)
719 (let ((prev (egg-get-previous-bunsetsu p))
722 (setq bunsetsu (cons prev bunsetsu)
723 p (egg-previous-bunsetsu-point p)
724 prev (and (egg-get-major-continue (1- p))
725 (egg-get-bunsetsu-info (1- p)))))
728 (defun egg-get-next-bunsetsu (p)
729 (and (null (egg-get-bunsetsu-last p))
730 (egg-get-bunsetsu-info (egg-next-bunsetsu-point p))))
732 (defun egg-get-major-bunsetsu (p)
733 (let ((next (egg-get-bunsetsu-info p))
736 (setq bunsetsu (cons next bunsetsu)
737 p (egg-next-bunsetsu-point p)
738 next (and (egg-get-major-continue (1- p))
739 (egg-get-bunsetsu-info p))))
740 (nreverse bunsetsu)))
742 (defsubst egg-get-major-bunsetsu-source (list)
743 (mapconcat 'egg-get-bunsetsu-source list nil))
745 (defsubst egg-get-major-bunsetsu-converted (list)
746 (mapconcat 'egg-get-bunsetsu-converted list nil))
748 (defvar egg-inspect-mode nil
749 "*Display clause information on candidate selection, if non-NIL.")
751 (defun egg-toggle-inspect-mode ()
753 (if (setq egg-inspect-mode (not egg-inspect-mode))
754 (egg-inspect-bunsetsu t)))
756 (defun egg-inspect-bunsetsu (&optional quiet)
758 (or (egg-word-inspection (egg-get-bunsetsu-info (point)))
762 (defvar egg-candidate-selection-info nil)
763 (make-variable-buffer-local 'egg-candidate-selection-info)
765 (defvar egg-candidate-selection-major t)
766 (make-variable-buffer-local 'egg-candidate-selection-major)
768 (defsubst egg-set-candsel-info (b major)
769 (setq egg-candidate-selection-info (list (car b) (cadr b) (caddr b) major)))
771 (defsubst egg-candsel-last-bunsetsu () (car egg-candidate-selection-info))
772 (defsubst egg-candsel-last-prev-b () (nth 1 egg-candidate-selection-info))
773 (defsubst egg-candsel-last-next-b () (nth 2 egg-candidate-selection-info))
774 (defsubst egg-candsel-last-major () (nth 3 egg-candidate-selection-info))
776 (defun egg-major-bunsetsu-head-p (head bunsetsu)
777 (while (and head (eq (car head) (car bunsetsu)))
778 (setq head (cdr head)
779 bunsetsu (cdr bunsetsu)))
782 (defun egg-major-bunsetsu-tail-p (tail bunsetsu)
783 (egg-major-bunsetsu-head-p
784 tail (nthcdr (- (length bunsetsu) (length tail)) bunsetsu)))
786 (defun egg-get-candsel-target-major ()
787 (let ((bunsetsu (egg-get-major-bunsetsu (point)))
788 (prev-b (egg-get-previous-major-bunsetsu (point)))
791 ((and (egg-candsel-last-major)
792 (egg-major-bunsetsu-tail-p (egg-candsel-last-prev-b) prev-b)
793 (egg-major-bunsetsu-head-p (append (egg-candsel-last-bunsetsu)
794 (egg-candsel-last-next-b))
796 (setq bunsetsu (egg-candsel-last-bunsetsu)
797 prev-b (egg-candsel-last-prev-b)
798 next-b (egg-candsel-last-next-b)))
799 ((null (egg-get-bunsetsu-last
800 (egg-next-bunsetsu-point (point) (1- (length bunsetsu)))))
801 (setq next-b (egg-get-major-bunsetsu
802 (egg-next-bunsetsu-point (point) (length bunsetsu))))))
803 (setq egg-candidate-selection-major t)
804 (list bunsetsu prev-b next-b t)))
806 (defun egg-get-candsel-target-minor ()
807 (let* ((bunsetsu (list (egg-get-bunsetsu-info (point))))
808 (prev-b (egg-get-previous-bunsetsu (point)))
809 (next-b (egg-get-next-bunsetsu (point))))
810 (setq egg-candidate-selection-major nil)
811 (list bunsetsu (and prev-b (list prev-b)) (and next-b (list next-b)) nil)))
813 (defun egg-check-candsel-target (b prev-b next-b major)
815 (and (egg-major-bunsetsu-tail-p
816 prev-b (egg-get-previous-major-bunsetsu (point)))
817 (let* ((cur-b (egg-get-major-bunsetsu (point)))
818 (next-p (egg-next-bunsetsu-point (point) (length cur-b))))
819 (egg-major-bunsetsu-head-p
821 (append cur-b (and (null (egg-get-bunsetsu-last (1- next-p)))
822 (egg-get-major-bunsetsu next-p))))))
823 (and (eq (egg-get-bunsetsu-info (point)) (car b))
824 (eq (egg-get-previous-bunsetsu (point)) (car prev-b))
825 (eq (egg-get-next-bunsetsu (point)) (car next-b)))))
827 (defun egg-insert-new-bunsetsu (b tail new-b)
828 (let* ((backend (egg-get-backend (point)))
829 (start (egg-previous-bunsetsu-point (point) (length (cadr new-b))))
830 (end (egg-next-bunsetsu-point (point) (+ (length b) (length tail))))
831 (last (egg-get-bunsetsu-last (1- end)))
832 (insert-before (buffer-has-markers-at end)))
834 ((buffer-has-markers-at end)
835 (delete-region start end)
836 (egg-insert-bunsetsu-list backend
837 (append (cadr new-b) (car new-b) (caddr new-b))
839 ((buffer-has-markers-at (egg-next-bunsetsu-point (point) (length b)))
840 (delete-region start end)
841 (egg-insert-bunsetsu-list backend (append (cadr new-b) (car new-b))
843 (egg-insert-bunsetsu-list backend (caddr new-b) last))
844 ((buffer-has-markers-at (point))
845 (delete-region start end)
846 (egg-insert-bunsetsu-list backend (cadr new-b) nil t)
847 (egg-insert-bunsetsu-list backend (append (car new-b) (caddr new-b))
850 (delete-region start end)
851 (egg-insert-bunsetsu-list backend
852 (append (cadr new-b) (car new-b) (caddr new-b))
854 (goto-char (egg-next-bunsetsu-point start (length (cadr new-b))))
856 (egg-inspect-bunsetsu t))))
858 (defun egg-next-candidate (n)
860 (if egg-candidate-selection-major
861 (egg-next-candidate-major n)
862 (egg-next-candidate-minor n)))
864 (defun egg-next-candidate-major (n)
866 (apply 'egg-next-candidate-internal n (egg-get-candsel-target-major)))
868 (defun egg-next-candidate-minor (n)
870 (apply 'egg-next-candidate-internal n (egg-get-candsel-target-minor)))
872 (defun egg-previous-candidate (n)
874 (if egg-candidate-selection-major
875 (egg-previous-candidate-major n)
876 (egg-previous-candidate-minor n)))
878 (defun egg-previous-candidate-major (n)
880 (apply 'egg-next-candidate-internal (- n) (egg-get-candsel-target-major)))
882 (defun egg-previous-candidate-minor (n)
884 (apply 'egg-next-candidate-internal (- n) (egg-get-candsel-target-minor)))
886 (defvar egg-candidate-select-counter 1)
887 (make-variable-buffer-local 'egg-candidate-select-counter)
889 (defun egg-next-candidate-internal (n b prev-b next-b major)
890 (if (eq last-command (if major 'egg-candidate-major 'egg-candidate-minor))
891 (setq egg-candidate-select-counter (1+ egg-candidate-select-counter))
892 (setq egg-candidate-select-counter 1))
893 (if (= egg-candidate-select-counter egg-conversion-auto-candidate-menu)
894 (egg-select-candidate-internal
895 nil egg-conversion-auto-candidate-menu-show-all
896 b prev-b next-b major)
897 (setq this-command (if major 'egg-candidate-major 'egg-candidate-minor))
898 (let ((inhibit-read-only t)
899 new-b candidates nitem i beep)
900 (setq candidates (egg-list-candidates b prev-b next-b major))
901 (if (null candidates)
903 (setq i (+ n (car candidates))
904 nitem (length (cdr candidates)))
906 ((< i 0) ; go backward as if it is ring
909 (setq i (+ i nitem))))
911 (egg-conversion-wrap-select ; go backward as if it is ring
912 (setq i (% i nitem)))
913 (t ; don't go forward
916 (setq new-b (egg-decide-candidate b i prev-b next-b))
917 (egg-set-candsel-info new-b major)
918 (egg-insert-new-bunsetsu b (caddr new-b) new-b))
922 (defun egg-numbering-item (list)
924 (mapcar (lambda (item) (cons item (setq n (1+ n)))) list)))
926 (defun egg-sort-item (list sort)
927 (if (eq (null sort) (null egg-conversion-sort-by-converted-string))
929 (sort list (lambda (a b) (string< (car a) (car b))))))
931 (defun egg-select-candidate-major (sort)
933 (apply 'egg-select-candidate-internal sort nil (egg-get-candsel-target-major)))
935 (defun egg-select-candidate-minor (sort)
937 (apply 'egg-select-candidate-internal sort nil (egg-get-candsel-target-minor)))
939 (defun egg-select-candidate-list-all-major (sort)
941 (apply 'egg-select-candidate-internal sort t (egg-get-candsel-target-major)))
943 (defun egg-select-candidate-list-all-minor (sort)
945 (apply 'egg-select-candidate-internal sort t (egg-get-candsel-target-minor)))
947 (defun egg-select-candidate-internal (sort all b prev-b next-b major)
948 (let ((prompt (egg-get-message 'candidate))
949 new-b candidates pos clist item-list i)
950 (setq candidates (egg-list-candidates b prev-b next-b major))
951 (if (null candidates)
953 (setq pos (car candidates)
954 clist (cdr candidates)
955 item-list (egg-sort-item (egg-numbering-item clist) sort)
956 i (menudiag-select (list 'menu prompt item-list)
958 (list (assq (nth pos clist) item-list))))
959 (if (or (null (egg-conversion-fence-p))
960 (null (egg-check-candsel-target b prev-b next-b major)))
961 (error "Fence was already modified")
962 (let ((inhibit-read-only t))
963 (setq new-b (egg-decide-candidate b i prev-b next-b))
964 (egg-set-candsel-info new-b major)
965 (egg-insert-new-bunsetsu b (caddr new-b) new-b))))))
967 (defun egg-hiragana (&optional minor)
970 (apply 'egg-special-convert this-command (egg-get-candsel-target-major))
971 (apply 'egg-special-convert this-command (egg-get-candsel-target-minor))))
973 (defalias 'egg-katakana 'egg-hiragana)
974 (defalias 'egg-pinyin 'egg-hiragana)
975 (defalias 'egg-zhuyin 'egg-hiragana)
976 (defalias 'egg-hangul 'egg-hiragana)
978 (defun egg-special-convert (type b prev-b next-b major)
979 (let ((inhibit-read-only t)
980 (new-b (egg-special-candidate b prev-b next-b major type)))
983 (egg-set-candsel-info new-b major)
984 (egg-insert-new-bunsetsu b (caddr new-b) new-b))))
986 (defun egg-separate-characters (str)
987 (let* ((v (egg-string-to-vector str))
989 (i 0) (j 0) m n (nchar 0))
991 (if (setq n (egg-chinese-syllable str j))
992 (setq m (egg-chars-in-period str j n))
993 (setq m 1 n (egg-char-bytes (aref v i))))
994 (put-text-property j (+ j n) 'egg-char-size n str)
995 (setq nchar (1+ nchar) i (+ i m) j (+ j n)))
998 (defun egg-enlarge-bunsetsu-major (n)
1000 (egg-enlarge-bunsetsu-internal n t))
1002 (defun egg-enlarge-bunsetsu-minor (n)
1004 (egg-enlarge-bunsetsu-internal n nil))
1006 (defun egg-shrink-bunsetsu-major (n)
1008 (egg-enlarge-bunsetsu-internal (- n) t))
1010 (defun egg-shrink-bunsetsu-minor (n)
1012 (egg-enlarge-bunsetsu-internal (- n) nil))
1014 (defun egg-enlarge-bunsetsu-internal (n major)
1015 (let ((inhibit-read-only t)
1016 b prev-b next-b new-b s1 s1len s2 s2len nchar i last end beep)
1018 (setq b (egg-get-major-bunsetsu (point))
1019 prev-b (egg-get-previous-major-bunsetsu (point)))
1020 (setq b (list (egg-get-bunsetsu-info (point)))
1021 prev-b (egg-get-previous-bunsetsu (point))
1022 prev-b (and prev-b (list prev-b))))
1023 (setq end (egg-next-bunsetsu-point (point) (length b))
1024 last (egg-get-bunsetsu-last (1- end)))
1026 (setq next-b (cons (egg-get-bunsetsu-info end) next-b)
1027 last (egg-get-bunsetsu-last end)
1028 end (egg-next-bunsetsu-point end)))
1029 (setq next-b (nreverse next-b)
1030 s1 (egg-get-major-bunsetsu-source b)
1031 s2 (concat s1 (egg-get-major-bunsetsu-source next-b))
1032 s1len (egg-separate-characters s1)
1033 s2len (egg-separate-characters s2)
1037 (setq beep t nchar (and (/= s1len 1) (egg-get-char-size 0 s1))))
1039 (setq beep t nchar (and (/= s2len s1len) (length s2))))
1043 (setq nchar (+ nchar (egg-get-char-size nchar s2))
1046 (setq next-b (nconc b next-b)
1047 i (length (egg-get-bunsetsu-source (car next-b))))
1049 (setq next-b (cdr next-b)
1050 i (+ i (length (egg-get-bunsetsu-source (car next-b))))))
1051 (setq next-b (prog1 (cdr next-b) (setcdr next-b nil))
1052 new-b (egg-change-bunsetsu-length b prev-b next-b nchar major))
1055 (egg-insert-new-bunsetsu b (and (caddr new-b) next-b) new-b)))
1059 (defun egg-reconvert-bunsetsu (n)
1061 (egg-reconvert-bunsetsu-internal n 'egg-get-bunsetsu-converted))
1063 (defun egg-reconvert-bunsetsu-from-source (n)
1065 (egg-reconvert-bunsetsu-internal n 'egg-get-bunsetsu-source))
1067 (defun egg-reconvert-bunsetsu-internal (n func)
1068 (let* ((inhibit-read-only t)
1069 (backend (egg-get-backend (point)))
1070 (source (funcall func (egg-get-bunsetsu-info (point))))
1071 (reconv-backend (egg-get-reconvert-backend backend n))
1073 (last (egg-get-bunsetsu-last (point)))
1075 (if (or (null reconv-backend)
1076 (null (setq new (egg-start-conversion reconv-backend source nil))))
1078 (delete-region p (egg-next-bunsetsu-point p))
1079 (setq next-b (egg-get-bunsetsu-info (point)))
1080 (if (and (equal (egg-get-backend p) backend)
1081 (eq (egg-bunsetsu-get-backend next-b)
1082 (egg-bunsetsu-get-backend (car new)))
1083 (egg-bunsetsu-combinable-p (egg-get-bunsetsu-tail new) next-b))
1085 (setq last (or (eq last t) 'continue)))
1086 (egg-insert-bunsetsu-list backend new last)
1088 (setq prev-b (egg-get-bunsetsu-info (1- p)))
1091 (if (and (equal (egg-get-backend (1- p)) backend)
1092 (eq (egg-bunsetsu-get-backend prev-b)
1093 (egg-bunsetsu-get-backend (car new)))
1094 (egg-bunsetsu-combinable-p prev-b (car new)))
1096 (setq last (or (eq last t) 'continue)))
1097 (setq backend (egg-get-backend (1- p)))
1098 (delete-region (egg-previous-bunsetsu-point p) p)
1099 (egg-insert-bunsetsu-list backend (list prev-b) last))))))
1101 (defun egg-decide-before-point ()
1103 (let* ((inhibit-read-only t)
1104 (start (if (get-text-property (1- (point)) 'egg-start)
1106 (previous-single-property-change (point) 'egg-start)))
1107 (end (if (get-text-property (point) 'egg-end)
1109 (next-single-property-change (point) 'egg-end)))
1110 (decided (buffer-substring start (point)))
1111 (undecided (buffer-substring (point) end))
1112 i len bunsetsu source context)
1114 (previous-single-property-change start 'egg-start nil (point-min))
1115 (next-single-property-change end 'egg-end nil (point-max)))
1117 len (length decided))
1119 (setq bunsetsu (nconc bunsetsu (list (egg-get-bunsetsu-info i decided)))
1120 i (egg-next-bunsetsu-point i 1 decided len))
1122 (egg-get-bunsetsu-last (1- i) decided))
1124 (insert (mapconcat 'egg-get-bunsetsu-converted bunsetsu nil))
1125 (setq context (cons (cons (egg-bunsetsu-get-backend (car bunsetsu))
1126 (egg-end-conversion bunsetsu nil))
1129 (setq len (length undecided))
1133 (run-hooks 'input-method-after-insert-chunk-hook)
1137 (setq bunsetsu (egg-get-bunsetsu-info i undecided)
1138 source (cons (egg-get-bunsetsu-source bunsetsu)
1140 (put-text-property 0 (length (car source))
1142 (egg-get-source-language bunsetsu)
1144 (setq i (egg-next-bunsetsu-point i 1 undecided len)))
1145 (its-restart (apply 'concat (nreverse source)) t t context))))
1147 (defun egg-decide-first-char ()
1149 (let* ((inhibit-read-only t)
1150 (start (if (get-text-property (1- (point)) 'egg-start)
1152 (previous-single-property-change (point) 'egg-start)))
1153 (end (if (get-text-property (point) 'egg-end)
1155 (next-single-property-change (point) 'egg-end)))
1156 (bunsetsu (egg-get-bunsetsu-info start)))
1158 (previous-single-property-change start 'egg-start nil (point-min))
1159 (next-single-property-change end 'egg-end nil (point-max)))
1160 (egg-end-conversion (list bunsetsu) nil)
1161 (insert (egg-string-to-char-at (egg-get-bunsetsu-converted bunsetsu) 0))))
1163 (defun egg-exit-conversion ()
1165 (if (egg-conversion-fence-p)
1167 (goto-char (next-single-property-change (point) 'egg-end))
1168 (egg-decide-before-point))))
1170 (defun egg-abort-conversion ()
1172 (let ((inhibit-read-only t)
1174 (goto-char (previous-single-property-change
1175 (if (get-text-property (1- (point)) 'egg-start)
1177 (previous-single-property-change (point) 'egg-start))
1178 'egg-start nil (point-min)))
1179 (setq source (get-text-property (point) 'egg-source)
1180 context (get-text-property (point) 'egg-context))
1181 (delete-region (point) (next-single-property-change
1182 (next-single-property-change (point) 'egg-end)
1183 'egg-end nil (point-max)))
1184 (its-restart source nil nil context)))
1186 (defun egg-toroku-bunsetsu ()
1189 (lang (egg-get-source-language (egg-get-bunsetsu-info p)))
1190 (egg-mode-hook (or (cdr (assq lang its-select-func-alist))
1191 (cdr (assq lang its-select-func-default-alist))))
1193 bunsetsu str yomi last)
1195 (setq bunsetsu (egg-get-bunsetsu-info p)
1196 str (concat str (egg-get-bunsetsu-converted bunsetsu))
1197 yomi (concat yomi (egg-get-bunsetsu-source bunsetsu))
1198 last (egg-get-bunsetsu-last p)
1199 p (egg-next-bunsetsu-point p)))
1201 (setq s (read-multilingual-string (egg-get-message 'register-str)
1202 str egg-last-method-name))
1203 (and (equal s "") (ding)))
1204 (egg-toroku-string s nil yomi lang (egg-bunsetsu-get-backend bunsetsu))))
1206 (defun egg-toroku-region (start end &optional nth-backend)
1207 (interactive "r\nP")
1208 (egg-toroku-string (buffer-substring start end) nil nil nil nil nth-backend))
1210 (defun egg-toroku-string (str &optional yomi guess lang backend nth-backend)
1211 (let (egg-mode-hook result)
1212 (if (= (length str) 0)
1213 (egg-error "Egg word registration: null string"))
1214 (egg-separate-languages str lang)
1215 (setq lang (egg-get-language 0 str)
1216 egg-mode-hook (or (cdr (assq lang its-select-func-alist))
1217 (cdr (assq lang its-select-func-default-alist))))
1218 (or yomi (setq yomi ""))
1219 (while (equal yomi "")
1220 (setq yomi (read-multilingual-string
1221 (format (egg-get-message 'register-yomi) str)
1222 guess egg-last-method-name))
1223 (and (equal yomi "") (ding)))
1224 (egg-separate-languages yomi lang)
1227 (setq backend (egg-assign-backend str nth-backend))
1229 (egg-error "Egg word registration: cannot decide backend"))
1230 (setq backend (egg-get-current-backend (caar backend)))))
1231 (setq result (egg-word-registration backend str yomi))
1233 (apply 'message (egg-get-message 'registered) str yomi result)
1236 (defun egg-conversion-mode ()
1237 "\\{egg-conversion-map}"
1238 ;; dummy function to get docstring
1241 (defun egg-help-command ()
1242 "Display documentation for EGG Conversion mode."
1244 (with-output-to-temp-buffer "*Help*"
1245 (princ "EGG Conversion mode:\n")
1246 (princ (documentation 'egg-conversion-mode))
1247 (help-setup-xref (cons #'help-xref-mode (current-buffer)) (interactive-p))))
1250 ;;; egg-cnv.el ends here.