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 Tamagotchy"
40 (defcustom egg-conversion-wrap-select nil
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 selectionmenu mode at N times
47 next/previous-candidate, if positive number N."
48 :group 'egg-conv :type 'integer)
50 (defcustom egg-conversion-fence-invisible nil
51 "*Make fence marks invisible, if non-NIL."
52 :group 'egg-conv :type 'boolean)
54 (defcustom egg-conversion-fence-open "|"
55 "*String of conversion fence start mark. (should not be null string)"
56 :group 'egg-conv :type '(string :valid-regexp ".+"))
58 (defcustom egg-conversion-fence-close "|"
59 "*String of conversion fence end mark. (should not be null string)"
60 :group 'egg-conv :type '(string :valid-regexp ".+"))
62 (defcustom egg-conversion-face nil
63 "*Face (or alist of languages and faces) of text in conversion fences."
66 (repeat :tag "Language-Face alist"
67 (cons :tag "Language-Face"
68 (choice :tag "Language"
73 (const :tag "Default" t)
74 (symbol :tag "Other"))
77 (defcustom egg-conversion-major-separator " "
78 "*Major clause seperator"
79 :group 'egg-conv :type 'string)
81 (defcustom egg-conversion-minor-separator "-"
82 "*Minor clause seperator"
83 :group 'egg-conv :type 'string)
85 (defcustom egg-startup-file ".eggrc"
86 "*Egg startup file name."
87 :group 'egg-conv :type 'string)
89 (defcustom egg-startup-file-search-path '("~")
90 "*List of directories to search for egg-startup-file (default .eggrc)."
91 :group 'egg-conv :type '(repeat string))
95 (no-rcfile "no egg-startup-file on %S")
96 (rcfile-error "error occured in egg-startup-file")
97 (candidate "candidates:")
98 (register-str "Chinese character:")
99 (register-yomi "word registration ``%s'' pronunciation:")
100 (registered "dictionary entry ``%s''(%s: %s) is registerd at %s"))
102 (no-rcfile "%S
\e$B>e$K
\e(B egg-startup-file
\e$B$,$"$j$^$;$s
\e(B")
103 (rcfile-error "egg-startup-file
\e$B$G%(%i!<$,$"$j$^$7$?
\e(B")
104 (candidate "
\e$B8uJd
\e(B:")
105 (register-str "
\e$B4A;z
\e(B:")
106 (register-yomi "
\e$B<-=qEPO?!X
\e(B%s
\e$B!Y
\e(B
\e$BFI$_
\e(B:")
107 (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"))
109 (no-rcfile "
\e$ATZ
\e(B %S
\e$AIOC;SP
\e(B egg-startup-file")
110 (rcfile-error "
\e$ATZ6AH!
\e(B egg-startup-file
\e$AJ1#,SP3v4m7"IzAK
\e(B")
111 (candidate "
\e$A:r29
\e(B:")
112 (register-str "
\e$A::WV
\e(B:")
113 (register-yomi "
\e$A4G5d5GB<!:
\e(B%s
\e$A!;
\e(B
\e$A6A7(
\e(B:")
114 (registered "
\e$A4G5dOnD?!:
\e(B%s
\e$A!;
\e(B(%s: %s)
\e$ARQ1;5GB<5=
\e(B %s
\e$AVPAK
\e(B"))
116 (no-rcfile "
\e$(GGc
\e(B %S
\e$(GD8JtH4
\e(B egg-startup-file")
117 (rcfile-error "
\e$(GGc{tL=
\e(B egg-startup-file
\e$(GUk!"H4Exrc`uFmD'
\e(B")
118 (register-str "
\e$(GiGGs
\e(B:")
119 (candidate "
\e$(GT7fP
\e(B:")
120 (register-yomi "
\e$(Gy0L(`trg!Z
\e(B%s
\e$(G![
\e(B
\e$(G{tNN
\e(B:")
121 (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"))
123 (no-rcfile "%S
\e$(C?!
\e(B egg-startup-file
\e$(C@L
\e(B
\e$(C>x@>4O4Y
\e(B")
124 (rcfile-error "egg-startup-file
\e$(C?!
\e(B
\e$(C?!7/0!
\e(B
\e$(C9_;}G_@>4O4Y
\e(B")
125 (candidate "
\e$(CHD:8
\e(B:")
126 (register-str "
\e$(CGQ@Z
\e(B:")
127 (register-yomi "
\e$(C;g@|5n7O!:
\e(B%s
\e$(C!;
\e(B
\e$(C569}
\e(B:")
128 (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"))))
131 ;; <backend-alist> ::= ( ( <language> ( <stage>... )... )... )
132 ;; <stage> ::= ( <backend> <backend-for-reconvert>... )
133 ;; <backend-for-reconvert> ::= <backend>
134 ;; <backend> ::= symbol
137 (defvar egg-conversion-backend-alist nil)
138 (make-variable-buffer-local 'egg-conversion-backend-alist)
139 (put 'egg-conversion-backend-alist 'permanent-local t)
141 (defun egg-set-conversion-backend (backend-alist &optional force)
142 (let (pair lang backend-set)
144 (setq lang (caar backend-alist)
145 backend-set (cdar backend-alist)
146 backend-alist (cdr backend-alist)
147 pair (assq lang egg-conversion-backend-alist))
150 (setq egg-conversion-backend-alist
151 (cons (cons lang backend-set) egg-conversion-backend-alist)))
153 (setcdr pair backend-set))))))
155 (defun egg-get-conversion-backend (language n use-default)
157 (cond ((null n) (setq n 0))
158 ((null (integerp n)) (setq n 1)))
159 (setq backend (nth (1+ n) (assq language egg-conversion-backend-alist)))
161 (and backend (cons 0 (mapcar 'copy-sequence backend)))
162 (and use-default (cons 0 egg-default-conversion-backend)))))
164 (defsubst egg-default-conversion-backend-p (backend)
165 (eq (cdr backend) egg-default-conversion-backend))
167 (defsubst egg-get-current-backend (backend)
168 (car (nth (car backend) (cdr backend))))
170 (defsubst egg-get-reconvert-backend (backend &optional n)
171 (cond ((null n) (setq n 0))
172 ((null (integerp n)) (setq n 1)))
173 (nth (1+ n) (nth (car backend) (cdr backend))))
175 (defmacro egg-bunsetsu-info () ''intangible)
177 (defsubst egg-get-bunsetsu-info (p &optional object)
178 (get-text-property p (egg-bunsetsu-info) object))
180 (defsubst egg-get-backend (p &optional object)
181 (get-text-property p 'egg-backend object))
183 (defsubst egg-get-language (p &optional object)
184 (get-text-property p 'egg-lang object))
186 (defsubst egg-get-bunsetsu-last (p &optional object)
187 (get-text-property p 'egg-bunsetsu-last object))
189 (defsubst egg-get-major-continue (p &optional object)
190 (get-text-property p 'egg-major-continue object))
192 (defsubst egg-get-char-size (p &optional object)
193 (get-text-property p 'egg-char-size object))
195 ;; <bunsetsu-info> ::= ( <backend> . <backend-dependent-info> )
197 (defsubst egg-bunsetsu-create (backend info)
200 (defsubst egg-bunsetsu-get-backend (bunsetsu)
202 (defsubst egg-bunsetsu-set-backend (bunsetsu backend)
203 (setcar bunsetsu backend))
205 (defsubst egg-bunsetsu-get-info (bunsetsu)
207 (defsubst egg-bunsetsu-set-info (bunsetsu info)
208 (setcdr bunsetsu info))
210 (defvar egg-finalize-backend-list nil)
212 (defun egg-set-finalize-backend (func-list)
213 (mapcar (lambda (func)
215 (null (memq func egg-finalize-backend-list)))
216 (setq egg-finalize-backend-list
217 (cons func egg-finalize-backend-list))))
220 (defmacro egg-define-backend-functions (list)
224 (let* ((func (car def))
226 (backend (car args)))
227 (cond ((eq backend 'bunsetsu)
228 (setq backend `(egg-bunsetsu-get-backend ,backend)))
229 ((eq backend 'bunsetsu-list)
230 (setq backend `(egg-bunsetsu-get-backend (car ,backend)))))
232 (let ((func (get ,backend ',func)))
234 (funcall func ,@args))))))
237 (egg-define-backend-functions
238 ((egg-start-conversion (backend source-string context))
239 (egg-get-bunsetsu-source (bunsetsu))
240 (egg-get-bunsetsu-converted (bunsetsu))
241 (egg-get-source-language (bunsetsu))
242 (egg-get-converted-language (bunsetsu))
243 (egg-major-bunsetsu-continue-p (bunsetsu))
244 (egg-list-candidates (bunsetsu-list prev-b next-b major))
245 (egg-decide-candidate (bunsetsu-list candidate-pos prev-b next-b))
246 (egg-change-bunsetsu-length (bunsetsu-list prev-b next-b length major))
247 (egg-bunsetsu-combinable-p (bunsetsu next-b))
248 (egg-end-conversion (bunsetsu-list abort))
249 (egg-word-inspection (bunsetsu))
250 (egg-word-registration (backend source converted))))
252 (defun egg-finalize-backend ()
253 (run-hooks 'egg-finalize-backend-list))
255 (setplist 'egg-conversion-backend-noconv
256 '(egg-start-conversion egg-start-conversion-noconv
257 egg-get-bunsetsu-source egg-get-bunsetsu-source-noconv
258 egg-get-bunsetsu-converted egg-get-bunsetsu-converted-noconv
259 egg-get-source-language egg-get-source-language-noconv
260 egg-get-converted-language egg-get-converted-language-noconv
261 egg-end-conversion egg-end-conversion-noconv))
263 (defun egg-start-conversion-noconv (backend yomi-string context)
264 (let ((string (copy-sequence yomi-string))
265 (language (egg-get-language 0 yomi-string)))
266 (set-text-properties 0 (length string) nil string)
267 (list (egg-bunsetsu-create backend (vector string language)))))
269 (defun egg-get-bunsetsu-source-noconv (bunsetsu)
270 (aref (egg-bunsetsu-get-info bunsetsu) 0))
271 (defun egg-get-bunsetsu-converted-noconv (bunsetsu)
272 (aref (egg-bunsetsu-get-info bunsetsu) 0))
273 (defun egg-get-source-language-noconv (bunsetsu)
274 (aref (egg-bunsetsu-get-info bunsetsu) 1))
275 (defun egg-get-converted-language-noconv (bunsetsu)
276 (aref (egg-bunsetsu-get-info bunsetsu) 1))
277 (defun egg-end-conversion-noconv (bunsetsu-list abort)
280 (defconst egg-default-conversion-backend '((egg-conversion-backend-noconv)))
282 (defun egg-convert-region (start end &optional context nth-backend)
283 (interactive "r\ni\nP")
284 (let ((source (buffer-substring start end))
285 backend backend-source-list converted converted-list
286 lang len s success abort)
290 (delete-region start end)
291 (let ((inhibit-read-only t))
292 (its-define-select-keys egg-conversion-map)
294 (setq s (copy-sequence egg-conversion-fence-open)
298 (set-text-properties 0 len (list 'read-only t
303 (put-text-property 0 len 'egg-context context s))
304 (if egg-conversion-fence-invisible
305 (put-text-property 0 len 'invisible t s))
307 (setq s (copy-sequence egg-conversion-fence-close)
309 (set-text-properties 0 len '(read-only t rear-nonsticky t egg-end t) s)
310 (if egg-conversion-fence-invisible
311 (put-text-property 0 len 'invisible t s))
316 (setq source (copy-sequence source))
317 (egg-separate-languages source)
318 (setq backend-source-list (egg-assign-backend source nth-backend))
319 (while (and (null abort) backend-source-list)
320 (setq backend (car (car backend-source-list))
321 lang (nth 1 (car backend-source-list))
322 source (nth 2 (car backend-source-list))
323 backend-source-list (cdr backend-source-list))
324 (condition-case error
326 (setq converted (egg-start-conversion
327 (egg-get-current-backend backend)
330 (egg-error "no conversion result"))
331 (setq converted-list (nconc converted-list
332 (list (cons backend converted)))
334 (or (egg-default-conversion-backend-p backend)
339 (delq t (mapcar (lambda (s)
340 (egg-default-conversion-backend-p
342 backend-source-list))))
343 (message "egg %s backend: %s"
344 (if (cdr lang) lang (car lang))
345 (nth (if (eq (car error) 'quit) 0 1) error))
350 (format "egg %s backend %s: continue? "
351 lang (nth (if (eq (car error) 'quit) 0 1) error)))
353 (setq backend (egg-get-conversion-backend nil 0 t)
354 converted (egg-start-conversion
355 (egg-get-current-backend backend)
357 converted-list (nconc converted-list
358 (list (cons backend converted)))
362 (delete-region start end)
363 (while converted-list
364 (egg-insert-bunsetsu-list (caar converted-list) (cdar converted-list)
365 (or (null (cdr converted-list)) 'continue))
366 (setq converted-list (cdr converted-list)))
369 (egg-abort-conversion))
371 (egg-exit-conversion)))))))
373 (defun egg-separate-languages (str &optional last-lang)
374 (let (lang last-chinese
375 (len (length str)) i j l)
376 ;; 1st pass -- mark undefined Chinese part
377 (if (or (eq last-lang 'Chinese-GB) (eq last-lang 'Chinese-CNS))
378 (setq last-chinese last-lang))
381 (setq j (egg-next-single-property-change i 'egg-lang str len))
382 (if (null (egg-get-language i str))
384 (setq c (egg-string-to-char-at str i)
385 cset (char-charset c))
387 ((eq cset 'chinese-sisheng)
388 (egg-string-match-charset 'chinese-sisheng str i)
389 (setq l (match-end 0)
392 ((setq l (egg-chinese-syllable str i))
396 (if (eq (string-match "[\0-\177\240-\377]+" str (1+ i)) (1+ i))
397 (setq j (match-end 0))
400 (eq (char-charset (egg-string-to-char-at str j))
402 (setq j (max (1+ i) (- j 6))))
404 ((eq cset 'composition)
405 (setq j (+ i (egg-char-bytes c))
406 lang (egg-charset-to-language
408 (car (decompose-composite-char c 'list))))))
410 (egg-string-match-charset cset str i)
411 (setq j (match-end 0)
412 lang (egg-charset-to-language cset))))
414 (put-text-property i j 'egg-lang lang str))))
416 ;; 2nd pass -- set language property
419 (setq lang (egg-get-language i str))
422 (setq lang (or last-lang
423 (egg-next-part-lang str i))))
424 ((equal lang 'Chinese)
425 (setq lang (or last-chinese
426 (egg-next-chinese-lang str i)))))
427 (setq last-lang lang)
428 (if (or (eq lang 'Chinese-GB) (eq lang 'Chinese-CNS))
429 (setq last-chinese lang))
431 i (egg-next-single-property-change i 'egg-lang str len))
432 (set-text-properties j i (list 'egg-lang lang) str))))
434 ;;; Should think again the interface to language-info-alist
435 (defun egg-charset-to-language (charset)
436 (let ((list language-info-alist))
438 (null (memq charset (assq 'charset (car list)))))
439 (setq list (cdr list)))
441 (intern (car (car list))))))
443 (defun egg-next-part-lang (str pos)
444 (let ((lang (get-text-property
445 (egg-next-single-property-change pos 'egg-lang str (length str))
447 (if (eq lang 'Chinese)
448 (egg-next-chinese-lang str pos)
451 egg-default-language))))
453 (defun egg-next-chinese-lang (str pos)
454 (let ((len (length str)) lang)
455 (while (and (< pos len) (null lang))
456 (setq pos (egg-next-single-property-change pos 'egg-lang str len)
457 lang (egg-get-language pos str))
458 (if (null (or (eq lang 'Chinese-GB)
459 (eq lang 'Chinese-CNS)))
463 ((eq its-current-language 'Chinese-GB) 'Chinese-GB)
464 ((eq its-current-language 'Chinese-CNS) 'Chinese-CNS)
465 ((eq egg-default-language 'Chinese-GB) 'Chinese-GB)
466 ((eq egg-default-language 'Chinese-CNS) 'Chinese-CNS)
470 ;; return value ::= ( (<backend> ( <lang>... ) <source string> )... )
472 (defun egg-assign-backend (source n)
473 (let ((len (length source))
475 j s lang backend retval)
477 (setq j (egg-next-single-property-change i 'egg-lang source len)
478 s (substring source i j)
479 lang (egg-get-language 0 s)
480 backend (egg-get-conversion-backend lang n t))
481 (set-text-properties 0 (- j i) (list 'egg-lang lang) s)
482 (setq retval (nconc retval (list (list backend (list lang) s)))
487 (if (or (egg-default-conversion-backend-p (car (car retval)))
488 (null (equal (car (car retval)) (car (nth 1 retval)))))
489 (setq retval (cdr retval))
490 (nconc (nth 1 (car retval)) (nth 1 (nth 1 retval)))
491 (setcar (nthcdr 2 (car retval))
492 (concat (nth 2 (car retval)) (nth 2 (nth 1 retval))))
493 (setcdr retval (cddr retval)))))))
495 (defun egg-search-file (filename path)
497 (if (file-name-directory filename)
498 (setq file (substitute-in-file-name (expand-file-name filename))
499 file (and (file-readable-p file) file))
500 (while (and (null file) path)
501 (if (stringp (car path))
502 (setq file (substitute-in-file-name
503 (expand-file-name filename (car path)))
504 file (and (file-exists-p file) file)))
505 (setq path (cdr path)))
508 (defvar egg-default-startup-file "eggrc"
509 "Egg startup file name (system default)")
511 (defun egg-load-startup-file (backend lang)
512 (let ((eggrc (or (egg-search-file egg-startup-file
513 egg-startup-file-search-path)
514 (egg-search-file egg-default-startup-file load-path))))
516 (condition-case error
517 (let ((egg-backend-type backend) (egg-language lang))
520 (message "%s: %s" (car error)
521 (mapconcat (lambda (s) (format "%S" s)) (cdr error) ", "))
522 (egg-error 'rcfile-error))
524 (egg-error 'rcfile-error)))
525 (egg-error 'no-rcfile egg-startup-file-search-path))))
527 (defun egg-get-conversion-face (lang)
528 (if (null (consp egg-conversion-face))
530 (cdr (or (assq lang egg-conversion-face)
531 (assq t egg-conversion-face)))))
533 (defvar egg-conversion-map
534 (let ((map (make-sparse-keymap))
537 (define-key map (vector i) 'egg-exit-conversion-unread-char)
539 (define-key map "\C-@" 'egg-decide-first-char)
540 (define-key map [?\C-\ ] 'egg-decide-first-char)
541 (define-key map "\C-a" 'egg-beginning-of-conversion-buffer)
542 (define-key map "\C-b" 'egg-backward-bunsetsu)
543 (define-key map "\C-c" 'egg-abort-conversion)
544 (define-key map "\C-e" 'egg-end-of-conversion-buffer)
545 (define-key map "\C-f" 'egg-forward-bunsetsu)
546 (define-key map "\C-h" 'egg-help-command)
547 (define-key map "\C-i" 'egg-shrink-bunsetsu-major)
548 (define-key map "\C-k" 'egg-decide-before-point)
549 ;; (define-key map "\C-l" 'egg-exit-conversion) ; Don't override C-L
550 (define-key map "\C-m" 'egg-exit-conversion)
551 (define-key map "\C-n" 'egg-next-candidate-major)
552 (define-key map "\C-o" 'egg-enlarge-bunsetsu-major)
553 (define-key map "\C-p" 'egg-previous-candidate-major)
554 (define-key map "\C-r" 'egg-reconvert-bunsetsu)
555 (define-key map "\C-t" 'egg-toroku-bunsetsu)
556 (define-key map "\C-v" 'egg-inspect-bunsetsu)
557 (define-key map "\M-i" 'egg-shrink-bunsetsu-minor)
558 (define-key map "\M-n" 'egg-next-candidate-minor)
559 (define-key map "\M-o" 'egg-enlarge-bunsetsu-minor)
560 (define-key map "\M-p" 'egg-previous-candidate-minor)
561 (define-key map "\M-r" 'egg-reconvert-bunsetsu-from-source)
562 (define-key map "\M-s" 'egg-select-candidate-major)
563 (define-key map "\M-v" 'egg-toggle-inspect-mode)
564 (define-key map "\M-z" 'egg-select-candidate-minor)
565 (define-key map "\e\C-s" 'egg-select-candidate-list-all-major)
566 (define-key map "\e\C-z" 'egg-select-candidate-list-all-minor)
567 (define-key map [return] 'egg-exit-conversion)
568 (define-key map [right] 'egg-forward-bunsetsu)
569 (define-key map [left] 'egg-backward-bunsetsu)
570 (define-key map " " 'egg-next-candidate)
571 (define-key map "/" 'egg-exit-conversion)
572 ;;;(define-key map "\M-h" 'egg-hiragana)
573 ;;;(define-key map "\M-k" 'egg-katakana)
574 ;;;(define-key map "\M-<" 'egg-hankaku)
575 ;;;(define-key map "\M->" 'egg-zenkaku)
577 "Keymap for EGG Conversion mode.")
579 (fset 'egg-conversion-map egg-conversion-map)
581 (defun egg-exit-conversion-unread-char ()
583 (setq unread-command-events (list last-command-event)
584 this-command 'egg-use-context)
585 (setq egg-context (egg-exit-conversion)))
587 (defun egg-make-bunsetsu (backend bunsetsu last)
588 (let* ((converted (copy-sequence (egg-get-bunsetsu-converted bunsetsu)))
589 (language (egg-get-converted-language bunsetsu))
590 (continue (and (null last) (egg-major-bunsetsu-continue-p bunsetsu)))
591 (face (egg-get-conversion-face language))
593 (setq len1 (length converted))
595 (setq converted (concat converted
597 egg-conversion-minor-separator
598 egg-conversion-major-separator))))
599 (setq len (length converted))
600 (set-text-properties 0 len
602 (egg-bunsetsu-info) bunsetsu
605 'egg-bunsetsu-last last
606 'egg-major-continue continue
607 'local-map 'egg-conversion-map)
610 (egg-set-face 0 len1 face converted))
613 (defun egg-insert-bunsetsu-list (backend bunsetsu-list &optional last)
614 (let ((len (length bunsetsu-list)))
619 (egg-make-bunsetsu backend b (and (= len 0) last)))
622 (defun egg-beginning-of-conversion-buffer (n)
626 (egg-end-of-conversion-buffer 1))
627 ((null (get-text-property (1- (point)) 'egg-start))
628 (goto-char (previous-single-property-change (point) 'egg-start)))))
630 (defun egg-end-of-conversion-buffer(n)
634 (egg-beginning-of-conversion-buffer 1))
636 (goto-char (next-single-property-change (point) 'egg-end))
639 (defun egg-backward-bunsetsu (n)
642 (null (get-text-property (1- (point)) 'egg-start)))
646 (signal 'beginning-of-buffer nil)))
648 (defun egg-forward-bunsetsu (n)
651 (null (get-text-property (point) 'egg-end)))
656 (signal 'end-of-buffer nil)))
658 (defun egg-get-bunsetsu-tail (b)
659 (nth (1- (length b)) b))
661 (defun egg-previous-bunsetsu-point (p &optional n obj lim)
664 (setq p (previous-single-property-change p (egg-bunsetsu-info) obj lim)
668 (defun egg-next-bunsetsu-point (p &optional n obj lim)
671 (setq p (egg-next-single-property-change p (egg-bunsetsu-info) obj lim)
675 (defun egg-get-previous-bunsetsu (p)
676 (and (null (egg-get-bunsetsu-last (1- p)))
677 (egg-get-bunsetsu-info (1- p))))
679 (defun egg-get-previous-major-bunsetsu (p)
681 (prev (egg-get-previous-bunsetsu p))
684 (setq bunsetsu (cons prev bunsetsu)
685 p (egg-previous-bunsetsu-point p)
686 prev (and (egg-get-major-continue (1- p))
687 (egg-get-bunsetsu-info (1- p)))))
690 (defun egg-get-next-bunsetsu (p)
691 (and (null (egg-get-bunsetsu-last p))
692 (egg-get-bunsetsu-info (egg-next-bunsetsu-point p))))
694 (defun egg-get-major-bunsetsu (p)
695 (let ((next (egg-get-bunsetsu-info p))
698 (setq bunsetsu (cons next bunsetsu)
699 p (egg-next-bunsetsu-point p)
700 next (and (egg-get-major-continue (1- p))
701 (egg-get-bunsetsu-info p))))
702 (nreverse bunsetsu)))
704 (defsubst egg-get-major-bunsetsu-source (list)
705 (mapconcat (lambda (b) (egg-get-bunsetsu-source b)) list ""))
707 (defvar egg-inspect-mode nil
708 "*Display clause information on candidate selection, if non-NIL.")
710 (defun egg-toggle-inspect-mode ()
712 (if (setq egg-inspect-mode (not egg-inspect-mode))
713 (egg-inspect-bunsetsu t)))
715 (defun egg-inspect-bunsetsu (&optional quiet)
717 (or (egg-word-inspection (egg-get-bunsetsu-info (point)))
721 (defvar egg-candidate-selection-info nil)
722 (make-variable-buffer-local 'egg-candidate-selection-info)
724 (defvar egg-candidate-selection-major t)
725 (make-variable-buffer-local 'egg-candidate-selection-major)
727 (defsubst egg-set-candsel-info (b prev-b next-b major)
728 (setq egg-candidate-selection-info (list b prev-b next-b major)))
730 (defsubst egg-candsel-last-bunsetsu () (car egg-candidate-selection-info))
731 (defsubst egg-candsel-last-prev-b () (nth 1 egg-candidate-selection-info))
732 (defsubst egg-candsel-last-next-b () (nth 2 egg-candidate-selection-info))
733 (defsubst egg-candsel-last-major () (nth 3 egg-candidate-selection-info))
735 (defun egg-major-bunsetsu-head-p (head bunsetsu)
736 (while (and head (eq (car head) (car bunsetsu)))
737 (setq head (cdr head)
738 bunsetsu (cdr bunsetsu)))
741 (defun egg-major-bunsetsu-tail-p (tail bunsetsu)
742 (egg-major-bunsetsu-head-p
743 tail (nthcdr (- (length bunsetsu) (length tail)) bunsetsu)))
745 (defun egg-get-candsel-target-major ()
746 (let ((bunsetsu (egg-get-major-bunsetsu (point)))
748 (setq prev-b (egg-get-previous-major-bunsetsu (point))
749 next (egg-next-bunsetsu-point (point) (length bunsetsu)))
751 ((and (egg-candsel-last-major)
752 (egg-major-bunsetsu-tail-p (egg-candsel-last-prev-b) prev-b)
753 (egg-major-bunsetsu-head-p (append (egg-candsel-last-bunsetsu)
754 (egg-candsel-last-next-b))
756 (setq bunsetsu (egg-candsel-last-bunsetsu)
757 prev-b (egg-candsel-last-prev-b)
758 next-b (egg-candsel-last-next-b))
759 (setq next (egg-next-bunsetsu-point (point) (length bunsetsu))))
760 ((null (egg-get-bunsetsu-last (1- next)))
761 (setq next-b (egg-get-major-bunsetsu next))))
762 (setq egg-candidate-selection-major t)
763 (list bunsetsu prev-b next-b next t)))
765 (defun egg-get-candsel-target-minor ()
766 (let* ((bunsetsu (list (egg-get-bunsetsu-info (point))))
767 (prev-b (egg-get-previous-bunsetsu (point)))
768 (next-b (egg-get-next-bunsetsu (point))))
769 (and prev-b (setq prev-b (list prev-b)))
770 (and next-b (setq next-b (list next-b)))
771 (setq egg-candidate-selection-major nil)
772 (list bunsetsu prev-b next-b (egg-next-bunsetsu-point (point)) nil)))
774 (defun egg-insert-new-bunsetsu (b prev-b next-b next end)
775 (let ((backend (egg-get-backend (point)))
777 (setq start (egg-previous-bunsetsu-point (point) (length prev-b)))
778 (setq end (or end (egg-next-bunsetsu-point next (length next-b))))
779 (setq last (egg-get-bunsetsu-last (1- end)))
780 (delete-region start end)
781 (egg-insert-bunsetsu-list backend (append prev-b (append b next-b)) last)
782 (goto-char (egg-next-bunsetsu-point start (length prev-b)))
784 (egg-inspect-bunsetsu t))))
786 (defun egg-next-candidate (n)
788 (if egg-candidate-selection-major
789 (egg-next-candidate-major n)
790 (egg-next-candidate-minor n)))
792 (defun egg-next-candidate-major (n)
794 (apply 'egg-next-candidate-internal n (egg-get-candsel-target-major)))
796 (defun egg-next-candidate-minor (n)
798 (apply 'egg-next-candidate-internal n (egg-get-candsel-target-minor)))
800 (defun egg-previous-candidate (n)
802 (if egg-candidate-selection-major
803 (egg-previous-candidate-major n)
804 (egg-previous-candidate-minor n)))
806 (defun egg-previous-candidate-major (n)
808 (apply 'egg-next-candidate-internal (- n) (egg-get-candsel-target-major)))
810 (defun egg-previous-candidate-minor (n)
812 (apply 'egg-next-candidate-internal (- n) (egg-get-candsel-target-minor)))
814 (defvar egg-candidate-select-counter 1)
816 (defun egg-next-candidate-internal (n b prev-b next-b next major)
817 (if (eq last-command (if major 'egg-candidate-major 'egg-candidate-minor))
818 (setq egg-candidate-select-counter (1+ egg-candidate-select-counter))
819 (setq egg-candidate-select-counter 1))
820 (if (= egg-candidate-select-counter egg-conversion-auto-candidate-menu)
821 (egg-select-candidate-internal nil b prev-b next-b next major)
822 (setq this-command (if major 'egg-candidate-major 'egg-candidate-minor))
823 (let ((inhibit-read-only t)
824 candidates nitem i beep)
825 (setq candidates (egg-list-candidates b prev-b next-b major))
826 (if (null candidates)
828 (setq i (+ n (car candidates))
829 nitem (length (cdr candidates)))
831 ((< i 0) ; go backward as if it is ring
833 (setq i (+ i nitem))))
835 (egg-conversion-wrap-select ; go backward as if it is ring
837 (setq i (- i nitem))))
838 (t ; don't go forward
841 (setq b (egg-decide-candidate b i prev-b next-b)
845 (egg-set-candsel-info b prev-b next-b major)
846 (egg-insert-new-bunsetsu b prev-b next-b next nil))
850 (defun egg-numbering-item (list)
852 (mapcar (lambda (item) (cons item (setq n (1+ n)))) list)))
854 (defun egg-select-candidate-major ()
856 (apply 'egg-select-candidate-internal nil (egg-get-candsel-target-major)))
858 (defun egg-select-candidate-minor ()
860 (apply 'egg-select-candidate-internal nil (egg-get-candsel-target-minor)))
862 (defun egg-select-candidate-list-all-major ()
864 (apply 'egg-select-candidate-internal t (egg-get-candsel-target-major)))
866 (defun egg-select-candidate-list-all-minor ()
868 (apply 'egg-select-candidate-internal t (egg-get-candsel-target-minor)))
870 (defun egg-select-candidate-internal (all b prev-b next-b next major)
871 (let ((inhibit-read-only t)
872 (prompt (egg-get-message 'candidate))
873 candidates item-list new i)
874 (setq candidates (egg-list-candidates b prev-b next-b major))
875 (if (null candidates)
877 (setq all (and all '(menudiag-list-all))
878 item-list (egg-numbering-item (cdr candidates))
879 i (menudiag-select (list 'menu prompt item-list)
880 (cons (nth (car candidates) item-list) all))
881 new (egg-decide-candidate b i prev-b next-b)
885 (egg-set-candsel-info new prev-b next-b major)
886 (egg-insert-new-bunsetsu new prev-b next-b next nil))))
888 (defun egg-separate-characters (str)
889 (let* ((v (egg-string-to-vector str))
891 (i 0) (j 0) m n (nchar 0))
893 (if (setq n (egg-chinese-syllable str j))
894 (setq m (egg-chars-in-period str j n))
895 (setq m 1 n (egg-char-bytes (aref v i))))
896 (put-text-property j (+ j n) 'egg-char-size n str)
897 (setq nchar (1+ nchar) i (+ i m) j (+ j n)))
900 (defun egg-enlarge-bunsetsu-major (n)
902 (egg-enlarge-bunsetsu-internal n t))
904 (defun egg-enlarge-bunsetsu-minor (n)
906 (egg-enlarge-bunsetsu-internal n nil))
908 (defun egg-shrink-bunsetsu-major (n)
910 (egg-enlarge-bunsetsu-internal (- n) t))
912 (defun egg-shrink-bunsetsu-minor (n)
914 (egg-enlarge-bunsetsu-internal (- n) nil))
916 (defun egg-enlarge-bunsetsu-internal (n major)
917 (let ((inhibit-read-only t)
918 b prev-b next-b s1 s1len s2 s2len nchar i last next end beep)
920 (setq b (egg-get-major-bunsetsu (point))
921 prev-b (egg-get-previous-major-bunsetsu (point)))
922 (setq b (list (egg-get-bunsetsu-info (point)))
923 prev-b (egg-get-previous-bunsetsu (point))
924 prev-b (and prev-b (list prev-b))))
925 (setq end (egg-next-bunsetsu-point (point) (length b))
926 last (egg-get-bunsetsu-last (1- end)))
928 (setq next-b (cons (egg-get-bunsetsu-info end) next-b)
929 last (egg-get-bunsetsu-last end)
930 end (egg-next-bunsetsu-point end)))
931 (setq next-b (nreverse next-b)
932 s1 (egg-get-major-bunsetsu-source b)
933 s2 (concat s1 (egg-get-major-bunsetsu-source next-b))
934 s1len (egg-separate-characters s1)
935 s2len (egg-separate-characters s2)
939 (setq beep t nchar (and (/= s1len 1) (egg-get-char-size 0 s1))))
941 (setq beep t nchar (and (/= s2len 0) (length s2))))
945 (setq nchar (+ nchar (egg-get-char-size nchar s2))
949 (setq next-b (nconc b next-b)
950 i (length (egg-get-bunsetsu-source (car next-b))))
952 (setq next-b (cdr next-b)
953 i (+ i (length (egg-get-bunsetsu-source (car next-b))))))
954 (setq next-b (prog1 (cdr next-b) (setcdr next-b nil))
955 next (egg-next-bunsetsu-point (point) (length b))
956 b (egg-change-bunsetsu-length b prev-b next-b nchar major))
959 (setq prev-b (nth 1 b)
962 (egg-insert-new-bunsetsu b prev-b next-b next (and next-b end)))))
966 (defun egg-reconvert-bunsetsu (n)
968 (egg-reconvert-bunsetsu-internal n 'egg-get-bunsetsu-converted))
970 (defun egg-reconvert-bunsetsu-from-source (n)
972 (egg-reconvert-bunsetsu-internal n 'egg-get-bunsetsu-source))
974 (defun egg-reconvert-bunsetsu-internal (n func)
975 (let* ((inhibit-read-only t)
976 (backend (egg-get-backend (point)))
977 (source (funcall func (egg-get-bunsetsu-info (point))))
978 (reconv-backend (egg-get-reconvert-backend backend n))
980 (last (egg-get-bunsetsu-last (point)))
982 (if (or (null reconv-backend)
983 (null (setq new (egg-start-conversion reconv-backend source nil))))
985 (delete-region p (egg-next-bunsetsu-point p))
986 (setq next-b (egg-get-bunsetsu-info (point)))
987 (if (and (equal (egg-get-backend p) backend)
988 (eq (egg-bunsetsu-get-backend next-b)
989 (egg-bunsetsu-get-backend (car new)))
990 (egg-bunsetsu-combinable-p (egg-get-bunsetsu-tail new) next-b))
992 (setq last (or (eq last t) 'continue)))
993 (egg-insert-bunsetsu-list backend new last)
995 (setq prev-b (egg-get-bunsetsu-info (1- p)))
998 (if (and (equal (egg-get-backend (1- p)) backend)
999 (eq (egg-bunsetsu-get-backend prev-b)
1000 (egg-bunsetsu-get-backend (car new)))
1001 (egg-bunsetsu-combinable-p prev-b (car new)))
1003 (setq last (or (eq last t) 'continue)))
1004 (setq backend (egg-get-backend (1- p)))
1005 (delete-region (egg-previous-bunsetsu-point p) p)
1006 (egg-insert-bunsetsu-list backend (list prev-b) last))))))
1008 (defun egg-decide-before-point ()
1010 (let* ((inhibit-read-only t)
1011 (start (if (get-text-property (1- (point)) 'egg-start)
1013 (previous-single-property-change (point) 'egg-start)))
1014 (end (if (get-text-property (point) 'egg-end)
1016 (next-single-property-change (point) 'egg-end)))
1017 (decided (buffer-substring start (point)))
1018 (undecided (buffer-substring (point) end))
1019 i len bunsetsu source context)
1021 (previous-single-property-change start 'egg-start nil (point-min))
1022 (next-single-property-change end 'egg-end nil (point-max)))
1024 len (length decided))
1026 (setq bunsetsu (nconc bunsetsu (list (egg-get-bunsetsu-info i decided)))
1027 i (egg-next-bunsetsu-point i 1 decided len))
1029 (egg-get-bunsetsu-last (1- i) decided))
1031 (apply 'insert (mapcar
1032 (lambda (b) (egg-get-bunsetsu-converted b))
1034 (setq context (cons (cons (egg-bunsetsu-get-backend (car bunsetsu))
1035 (egg-end-conversion bunsetsu nil))
1038 (setq len (length undecided))
1042 (run-hooks 'input-method-after-insert-chunk-hook)
1046 (setq bunsetsu (egg-get-bunsetsu-info i undecided)
1047 source (cons (egg-get-bunsetsu-source bunsetsu)
1049 (put-text-property 0 (length (car source))
1051 (egg-get-source-language bunsetsu)
1053 (setq i (egg-next-bunsetsu-point i 1 undecided len)))
1054 (its-restart (apply 'concat (nreverse source)) t t context))))
1056 (defun egg-decide-first-char ()
1058 (let* ((inhibit-read-only t)
1059 (start (if (get-text-property (1- (point)) 'egg-start)
1061 (previous-single-property-change (point) 'egg-start)))
1062 (end (if (get-text-property (point) 'egg-end)
1064 (next-single-property-change (point) 'egg-end)))
1065 (bunsetsu (egg-get-bunsetsu-info start)))
1067 (previous-single-property-change start 'egg-start nil (point-min))
1068 (next-single-property-change end 'egg-end nil (point-max)))
1069 (egg-end-conversion (list bunsetsu) nil)
1070 (insert (egg-string-to-char-at (egg-get-bunsetsu-converted bunsetsu) 0))))
1072 (defun egg-exit-conversion ()
1074 (if (egg-get-bunsetsu-info (point))
1076 (goto-char (next-single-property-change (point) 'egg-end))
1077 (egg-decide-before-point))))
1079 (defun egg-abort-conversion ()
1081 (let ((inhibit-read-only t)
1083 (goto-char (previous-single-property-change
1084 (if (get-text-property (1- (point)) 'egg-start)
1086 (previous-single-property-change (point) 'egg-start))
1087 'egg-start nil (point-min)))
1088 (setq source (get-text-property (point) 'egg-source)
1089 context (get-text-property (point) 'egg-context))
1090 (delete-region (point) (next-single-property-change
1091 (next-single-property-change (point) 'egg-end)
1092 'egg-end nil (point-max)))
1093 (its-restart source nil nil context)))
1095 (defun egg-toroku-bunsetsu ()
1098 (lang (egg-get-source-language (egg-get-bunsetsu-info p)))
1099 (egg-mode-hook (or (cdr (assq lang its-select-func-alist))
1100 (cdr (assq lang its-select-func-default-alist))))
1102 bunsetsu str yomi last)
1104 (setq bunsetsu (egg-get-bunsetsu-info p)
1105 str (concat str (egg-get-bunsetsu-converted bunsetsu))
1106 yomi (concat yomi (egg-get-bunsetsu-source bunsetsu))
1107 last (egg-get-bunsetsu-last p)
1108 p (egg-next-bunsetsu-point p)))
1110 (setq s (read-multilingual-string (egg-get-message 'register-str)
1111 str egg-last-method-name))
1112 (and (equal s "") (ding)))
1113 (egg-toroku-string s nil yomi lang (egg-bunsetsu-get-backend bunsetsu))))
1115 (defun egg-toroku-region (start end &optional nth-backend)
1116 (interactive "r\nP")
1117 (egg-toroku-string (buffer-substring start end) nil nil nil nil nth-backend))
1119 (defun egg-toroku-string (str &optional yomi guess lang backend nth-backend)
1120 (let (egg-mode-hook result)
1121 (if (= (length str) 0)
1122 (egg-error "Egg word registration: null string"))
1123 (egg-separate-languages str lang)
1124 (setq lang (egg-get-language 0 str)
1125 egg-mode-hook (or (cdr (assq lang its-select-func-alist))
1126 (cdr (assq lang its-select-func-default-alist))))
1127 (or yomi (setq yomi ""))
1128 (while (equal yomi "")
1129 (setq yomi (read-multilingual-string
1130 (format (egg-get-message 'register-yomi) str)
1131 guess egg-last-method-name))
1132 (and (equal yomi "") (ding)))
1133 (egg-separate-languages yomi lang)
1136 (setq backend (egg-assign-backend str nth-backend))
1138 (egg-error "Egg word registration: cannot decide backend"))
1139 (setq backend (egg-get-current-backend (caar backend)))))
1140 (setq result (egg-word-registration backend str yomi))
1142 (apply 'message (egg-get-message 'registered) str yomi result)
1145 (defun egg-conversion-mode ()
1146 "\\{egg-conversion-map}"
1147 ;; dummy function to get docstring
1150 (defun egg-help-command ()
1151 "Display documentation for EGG Conversion mode."
1153 (with-output-to-temp-buffer "*Help*"
1154 (princ "EGG Conversion mode:\n")
1155 (princ (documentation 'egg-conversion-mode))
1156 (help-setup-xref (cons #'help-xref-mode (current-buffer)) (interactive-p))))
1159 ;;; egg-cnv.el ends here.