Sync up with egg-980712.
[elisp/egg.git] / egg-cnv.el
1 ;;; egg-cnv.el --- Conversion Backend in Egg Input Method Architecture
2
3 ;; Copyright (C) 1997, 1998 Mule Project,
4 ;; Powered by Electrotechnical Laboratory, JAPAN.
5 ;; Project Leader: Satoru Tomura <tomura@etl.go.jp>
6
7 ;; Author: NIIBE Yutaka <gniibe@mri.co.jp>
8 ;;         KATAYAMA Yoshio <kate@pfu.co.jp>
9 ;; Maintainer: NIIBE Yutaka <gniibe@mri.co.jp>
10 ;; Keywords: mule, multilingual, input method
11
12 ;; This file is part of EGG.
13
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)
17 ;; any later version.
18
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.
23
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.
28
29 ;;; Commentary:
30
31
32 ;;; Code:
33
34 (require 'egg-edep)
35
36 (defvar egg-current-language)
37 (make-variable-buffer-local 'egg-current-language)
38 (put 'egg-current-language 'permanent-local t)
39
40 (defsubst egg-bunsetsu-info () 'intangible)
41
42 (defun egg-get-bunsetsu-info (p &optional object)
43   (let ((bunsetsu-info (get-text-property p (egg-bunsetsu-info) object)))
44     (if bunsetsu-info
45         (setq egg-conversion-backend (get-text-property p 'egg-backend object)
46               egg-current-language (get-text-property p 'egg-lang object)))
47     bunsetsu-info))
48 ;;
49
50 (defconst egg-conversion-backend-null
51   [ egg-init-null
52
53       egg-start-conversion-null
54       egg-get-bunsetsu-converted-null
55       egg-get-bunsetsu-source-null
56       egg-list-candidates-null
57           egg-get-number-of-candidates-null
58           egg-get-current-candidate-number-null
59           egg-get-all-candidates-null
60           egg-decide-candidate-null
61       egg-change-bunsetsu-length-null
62     egg-end-conversion-null
63     nil
64
65     egg-fini-null
66  ])
67
68 (defun egg-init-null ()
69  )
70
71 (defun egg-start-conversion-null (yomi-string language)
72   (list yomi-string))
73 (defun egg-get-bunsetsu-converted-null (bunsetsu-info)
74   bunsetsu-info)
75 (defun egg-get-bunsetsu-source-null (bunsetsu-info)
76   bunsetsu-info)
77 (defun egg-list-candidates-null (bunsetsu-info prev-bunsetsu-info)
78   1)
79 (defun egg-get-number-of-candidates-null (bunsetsu-info)
80   1)
81 (defun egg-get-current-candidate-number-null (bunsetsu-info)
82   0)
83 (defun egg-get-all-candidates-null (bunsetsu-info)
84   (list bunsetsu-info))
85 (defun egg-decide-candidate-null (bunsetsu-info candidate-pos)
86   bunsetsu-info)
87 (defun egg-change-bunsetsu-length-null (b0 b1 b2 len)
88   (let ((s (concat b1 b2)))
89     (set-text-properties 0 (length s) nil s)
90     (if (= len (length s))
91         (list s)
92       (list (substring s 0 len) (substring s len)))))
93 (defun egg-end-conversion-null (bunsetsu-info-list abort)
94   nil)
95 (defun egg-fini-null (language)
96   nil)
97
98 (defvar egg-conversion-backend nil)
99
100 (defun egg-initialize-backend (language)
101   (funcall (aref egg-conversion-backend 0)))
102
103 (defun egg-start-conversion (yomi-string language)
104   (funcall (aref egg-conversion-backend 1) yomi-string language))
105 (defun egg-get-bunsetsu-converted (bunsetsu-info)
106   (funcall (aref egg-conversion-backend 2) bunsetsu-info))
107 (defun egg-get-bunsetsu-source (bunsetsu-info)
108   (funcall (aref egg-conversion-backend 3) bunsetsu-info))
109 (defun egg-list-candidates (bunsetsu-info prev-bunsetsu-info)
110   (funcall (aref egg-conversion-backend 4) bunsetsu-info prev-bunsetsu-info))
111 (defun egg-get-number-of-candidates (bunsetsu-info)
112   (funcall (aref egg-conversion-backend 5) bunsetsu-info))
113 (defun egg-get-current-candidate-number (bunsetsu-info)
114   (funcall (aref egg-conversion-backend 6) bunsetsu-info))
115 (defun egg-get-all-candidates (bunsetsu-info)
116   (funcall (aref egg-conversion-backend 7) bunsetsu-info))
117 (defun egg-decide-candidate (bunsetsu-info candidate-pos)
118   (funcall (aref egg-conversion-backend 8) bunsetsu-info candidate-pos))
119 (defun egg-change-bunsetsu-length (b0 b1 b2 len)
120   (funcall (aref egg-conversion-backend 9) b0 b1 b2 len))
121 (defun egg-end-conversion (bunsetsu-info-list abort)
122   (funcall (aref egg-conversion-backend 10) bunsetsu-info-list abort))
123 (defun egg-start-reverse-conversion (yomi-string language)
124   (if (aref egg-conversion-backend 11)
125       (funcall (aref egg-conversion-backend 11) yomi-string language)
126     (beep)))
127
128 (defun egg-finalize-backend ()
129   (aref egg-conversion-backend 12))
130 \f
131 (defvar egg-conversion-open  "|"  "*\e$B%U%'%s%9$N;OE@$r<($9J8;zNs\e(B (1 \e$BJ8;z0J>e\e(B)")
132 (defvar egg-conversion-close "|"  "*\e$B%U%'%s%9$N=*E@$r<($9J8;zNs\e(B (1 \e$BJ8;z0J>e\e(B)")
133 (defvar egg-conversion-face  nil  "*\e$B%U%'%s%9I=<($KMQ$$$k\e(B face \e$B$^$?$O\e(B nil")
134 (defvar egg-conversion-invisible nil)
135 (defvar egg-conversion-separator " ")
136
137 (defun egg-get-conversion-face ()
138   (let ((face (and (listp egg-conversion-face)
139                    (or (assq egg-current-language egg-conversion-face)
140                        (assq t egg-conversion-face)))))
141     (if face (cdr face) egg-conversion-face)))
142
143 (defvar egg-start-conversion-failure-hook
144   '(egg-start-conversion-failure-fallback)
145   "Hook which runs on failure of conversion.")
146
147 ;; SAIGO no shudan
148 (defun egg-start-conversion-failure-fallback (language)
149   (setq egg-conversion-backend egg-conversion-backend-null))
150
151 ;;
152 (defun egg-convert-region (start end)
153   (interactive "r")
154   (let ((source (buffer-substring start end))
155         (no-prop-source (buffer-substring-no-properties start end))
156         len result i j s)
157     (if (>= start end)
158         ;; nothing to do
159         nil
160       (delete-region start end)
161       (let ((inhibit-read-only t))
162         (its-define-select-keys egg-conversion-map)
163         (goto-char start)
164         (setq s (copy-sequence egg-conversion-open)
165               len (length s))
166         (set-text-properties 0 len
167                              (list
168                               'read-only t
169                               'egg-start t
170                               'egg-source source)
171                              s)
172         (if egg-conversion-invisible
173             (put-text-property 0 len 'invisible t s))
174         (insert s)
175         (setq start (point)
176               s (copy-sequence egg-conversion-close)
177               len (length s))
178         (set-text-properties 0 len
179                              '(read-only t rear-nonsticky t egg-end t)
180                              s)
181         (if egg-conversion-invisible
182             (put-text-property 0 len 'invisible t s))
183         (insert s)
184         (goto-char start)
185         (egg-separate-languages (copy-sequence source))
186         (setq i 0
187               len (length source))
188         (while (< i len)
189           (setq egg-current-language (get-text-property i 'egg-lang source)
190                 j (egg-next-single-property-change i 'egg-lang source len))
191           (let (bunsetsu-info-list)
192             (while (null bunsetsu-info-list)
193               (condition-case err
194                   (setq bunsetsu-info-list (egg-start-conversion
195                                             (substring no-prop-source i j)
196                                             egg-current-language))
197                 ;; Don't catch programming error
198                 (lang-not-supported
199                  (message "Language not supported: %s" egg-current-language)
200                  (ding)
201                  (setq bunsetsu-info-list
202                        (egg-start-conversion-null
203                         (substring no-prop-source i j)
204                         egg-current-language)))
205                 (file-error             
206                  (message "Error on %s backend: %s"
207                           egg-current-language (nth 1 err))
208                  (ding)
209                  (sit-for 1)
210                  (run-hook-with-args-until-success
211                   'egg-start-conversion-failure-hook egg-current-language))))
212             (egg-insert-bunsetsu-list bunsetsu-info-list
213                                       (if (< j len) 'contine t)))
214           (setq i j))
215         (goto-char start)))))
216
217 (defconst egg-chinese-sisheng-regexp
218   (concat "[" (list (make-char 'chinese-sisheng 32))
219           "-" (list (make-char 'chinese-sisheng 127))
220           "]+"))
221
222 (defun egg-separate-languages (str &optional last-lang)
223   (let (lang last-chinese
224         (len (length str)) i j l)
225     ;; 1st pass -- mark undefined Chinese part
226     (if (or (eq last-lang 'Chinese-GB) (eq last-lang 'Chinese-CNS))
227         (setq last-chinese last-lang))
228     (setq i 0)
229     (while (< i len)
230       (setq j (egg-next-single-property-change i 'egg-lang str len))
231       (if (get-text-property i 'egg-lang str)
232           nil
233         (setq c (egg-string-to-char-at str i)
234               cset (char-charset c))
235         (cond
236          ((eq cset 'chinese-sisheng)
237           (string-match egg-chinese-sisheng-regexp str i)
238           (setq l (match-end 0)
239                 j (min j l)
240                 lang 'Chinese))
241          ((setq l (egg-chinese-syllable str i))
242           (setq j (+ i l)
243                 lang 'Chinese))
244          ((eq cset 'ascii)
245           (if (eq (string-match "[\0-\177\240-\377]+" str (1+ i)) (1+ i))
246               (setq j (match-end 0))
247             (setq j (1+ i)))
248           (if (and (< j len)
249                    (eq (char-charset (egg-string-to-char-at str j))
250                        'chinese-sisheng))
251               (setq j (max (1+ i) (- j 6))))
252           (setq lang nil))
253          ((eq cset 'composition)
254           (setq j (+ i (egg-char-bytes c))
255                 lang (egg-charset-to-language
256                       (char-charset
257                        (car (decompose-composite-char c 'list))))))
258          (t
259           (string-match (concat "[" (list (make-char cset 32 32))
260                                 "-" (list (make-char cset 127 127))
261                                 "]+")
262                         str i)
263           (setq j (match-end 0)
264                 lang (egg-charset-to-language cset))))
265         (if lang
266             (put-text-property i j 'egg-lang lang str)))
267       (setq i j))
268     ;; 2nd pass -- set language property
269     (setq i 0)
270     (while (< i len)
271       (setq lang (get-text-property i 'egg-lang str))
272       (cond
273        ((null lang)
274         (setq lang (or last-lang
275                        (egg-next-part-lang str i))))
276        ((equal lang 'Chinese)
277         (setq lang (or last-chinese
278                        (egg-next-chinese-lang str i)))))
279       (setq last-lang lang)
280       (if (or (eq lang 'Chinese-GB) (eq lang 'Chinese-CNS))
281           (setq last-chinese lang))
282       (setq j i
283             i (egg-next-single-property-change i 'egg-lang str len))
284       (set-text-properties j i (list 'egg-lang lang) str))))
285
286 ;;; Should think again the interface to language-info-alist
287 (defun egg-charset-to-language (charset)
288   (let ((list language-info-alist))
289     (while (and list
290                 (null (memq charset (assq 'charset (car list)))))
291       (setq list (cdr list)))
292     (if list
293         (intern (car (car list))))))
294
295 (defun egg-next-part-lang (str pos)
296   (let ((lang (get-text-property
297                (egg-next-single-property-change pos 'egg-lang str (length str))
298                'egg-lang str)))
299     (if (eq lang 'Chinese)
300         (egg-next-chinese-lang str pos)
301       (or lang
302           its-current-language
303           egg-default-language))))
304
305 (defun egg-next-chinese-lang (str pos)
306   (let ((len (length str)) lang)
307     (while (and (< pos len) (null lang))
308       (setq pos (egg-next-single-property-change pos 'egg-lang str len)
309             lang (get-text-property pos 'egg-lang str))
310       (if (null (or (eq lang 'Chinese-GB)
311                     (eq lang 'Chinese-CNS)))
312           (setq lang nil)))
313     (cond
314      (lang lang)
315      ((eq its-current-language 'Chinese-GB)  'Chinese-GB)
316      ((eq its-current-language 'Chinese-CNS) 'Chinese-CNS)
317      ((eq egg-default-language 'Chinese-GB)  'Chinese-GB)
318      ((eq egg-default-language 'Chinese-CNS) 'Chinese-CNS)
319      (t 'Chinese-GB))))
320 \f
321 (defvar egg-conversion-map
322   (let ((map (make-sparse-keymap))
323         (i 33))
324     (while (< i 127)
325       (define-key map (vector i) 'egg-exit-conversion-unread-char)
326       (setq i (1+ i)))
327     (define-key map "\C-@" 'egg-decide-first-char)
328     (define-key map [?\C-\ ] 'egg-decide-first-char)
329     (define-key map "\C-a"   'egg-beginning-of-conversion-buffer)
330     (define-key map "\C-b"   'egg-backward-bunsetsu)
331     (define-key map "\C-c"   'egg-abort-conversion)
332     (define-key map "\C-e"   'egg-end-of-conversion-buffer)
333     (define-key map "\C-f"   'egg-forward-bunsetsu)
334     (define-key map "\C-h"   'egg-help-command)
335     (define-key map "\C-i"   'egg-shrink-bunsetsu)
336     (define-key map "\C-k"   'egg-decide-before-point)
337 ;;    (define-key map "\C-l"   'egg-exit-conversion)  ; Don't override C-L
338     (define-key map "\C-m"   'egg-exit-conversion)
339     (define-key map "\C-n"   'egg-next-candidate)
340     (define-key map "\C-o"   'egg-enlarge-bunsetsu)
341     (define-key map "\C-p"   'egg-previous-candidate)
342     (define-key map "\C-r"   'egg-reverse-convert-bunsetu)
343     (define-key map "\M-r"   'egg-reconvert-bunsetsu)
344     (define-key map "\M-s"   'egg-select-candidate)
345     (define-key map [return] 'egg-exit-conversion)
346     (define-key map [right]  'egg-forward-bunsetsu)
347     (define-key map [left]   'egg-backward-bunsetsu)
348     (define-key map " "      'egg-next-candidate)
349     (define-key map "/"      'egg-exit-conversion)
350     map)
351   "Keymap for EGG Conversion mode.")
352
353 (fset 'egg-conversion-map egg-conversion-map)
354
355 (defun egg-exit-conversion-unread-char ()
356   (interactive)
357   (setq unread-command-events (list last-command-event))
358   (egg-exit-conversion))
359
360 (defun egg-make-bunsetsu (bunsetsu-info last)
361   (let ((bunsetsu (copy-sequence (egg-get-bunsetsu-converted bunsetsu-info)))
362         len len1)
363     (setq len1 (length bunsetsu))
364     (if (null (eq last t))
365         (setq bunsetsu (concat bunsetsu egg-conversion-separator)))
366     (setq len (length bunsetsu))
367     (set-text-properties 0 len
368                          (list 'read-only          t
369                                (egg-bunsetsu-info) bunsetsu-info
370                                'egg-backend        egg-conversion-backend
371                                'egg-lang           egg-current-language
372                                'egg-bunsetsu-last  last
373                                'local-map          'egg-conversion-map)
374                          bunsetsu)
375     (if egg-conversion-face
376         (egg-set-face 0 len1 (egg-get-conversion-face) bunsetsu))
377     bunsetsu))
378
379 (defun egg-insert-bunsetsu-list (bunsetsu-info-list &optional last)
380   (let ((l bunsetsu-info-list)
381         bunsetsu-info bunsetsu)
382     (while l
383       (setq bunsetsu-info (car l)
384             l (cdr l)
385             bunsetsu (cons (egg-make-bunsetsu bunsetsu-info
386                                               (and (null l) last))
387                            bunsetsu)))
388     (apply 'insert (nreverse bunsetsu)))) ; XXX: Should avoid apply and reverse
389
390 (defun egg-beginning-of-conversion-buffer (n)
391   (interactive "p")
392   (cond
393    ((<= n 0)
394     (egg-end-of-conversion-buffer 1))
395    ((null (get-text-property (1- (point)) 'egg-start))
396     (goto-char (previous-single-property-change (1- (point)) 'egg-start)))))
397
398 (defun egg-end-of-conversion-buffer(n)
399   (interactive "p")
400   (cond
401    ((<= n 0)
402     (egg-beginning-of-conversion-buffer 1))
403    (t
404     (goto-char (next-single-property-change (point) 'egg-end))
405     (backward-char))))
406
407 (defun egg-backward-bunsetsu (n)
408   (interactive "p")
409   (let (start)
410     (while (and (null start) (> n 0))
411       (backward-char)
412       (if (setq start (get-text-property (point) 'egg-start))
413           (forward-char)
414         (setq n (1- n))))
415     (if (> n 0)
416         (signal 'beginning-of-buffer nil))))
417
418 (defun egg-forward-bunsetsu (n)
419   (interactive "p")
420   (let (end)
421     (while (and (null end) (> n 0))
422       (forward-char)
423       (if (setq end (get-text-property (point) 'egg-end))
424           (backward-char)
425         (setq n (1- n))))
426     (if (> n 0)
427         (signal 'end-of-buffer nil))))
428
429 (defun egg-get-previous-bunsetsu (p)
430   (and (null (get-text-property (1- p) 'egg-start))
431        (null (get-text-property (1- p) 'egg-bunsetsu-last))
432        (egg-get-bunsetsu-info (- p 2))))
433
434 (defun egg-separate-characters (str)
435   (let* ((v (egg-string-to-vector str))
436          (len (length v))
437          (i 0) (j 0) m n (nchar 0))
438     (while (< i len)
439       (if (setq n (egg-chinese-syllable str j))
440           (setq m (egg-chars-in-period str j n))
441         (setq m 1 n (egg-char-bytes (aref v i))))
442       (put-text-property j (+ j n) 'egg-char-size n str)
443       (setq nchar (1+ nchar) i (+ i m) j (+ j n)))
444     nchar))
445
446 (defun egg-shrink-bunsetsu (n)
447   (interactive "p")
448   (egg-enlarge-bunsetsu (- n)))
449
450 (defun egg-enlarge-bunsetsu (n)
451   (interactive "p")
452   (let* ((inhibit-read-only t)
453          (b0 (egg-get-previous-bunsetsu (point)))
454          (b1 (egg-get-bunsetsu-info (point)))
455          (s1 (egg-get-bunsetsu-source b1))
456          (s1len (egg-separate-characters s1))
457          (s2len 0)
458          (chrs (length s1))
459          (last (get-text-property (point) 'egg-bunsetsu-last))
460          b2 s2 source bunsetsu-info-list beep)
461     (if (not last)
462         (let ((p2 (save-excursion (forward-char) (point))))
463           (setq b2 (egg-get-bunsetsu-info p2)
464                 s2 (egg-get-bunsetsu-source b2)
465                 s2len (egg-separate-characters s2)
466                 last (get-text-property p2 'egg-bunsetsu-last))))
467     (setq source (concat s1 s2))
468     (cond
469      ((<= n (- s1len))
470       (setq beep t chrs (get-text-property 0 'egg-char-size source)))
471      ((> n s2len)
472       (setq beep t chrs (length source)))
473      ((< n 0)
474       (while (< n 0)
475         (setq chrs (- chrs (get-text-property (1- chrs) 'egg-char-size source))
476               n (1+ n))))
477      (t
478       (while (> n 0)
479         (setq chrs (+ chrs (get-text-property chrs 'egg-char-size source))
480               n (1- n)))))
481     (setq bunsetsu-info-list (egg-change-bunsetsu-length b0 b1 b2 chrs))
482     (delete-region (point)
483                    (progn (forward-char) (if b2 (forward-char)) (point)))
484     (let ((p (point)))
485       (egg-insert-bunsetsu-list bunsetsu-info-list last)
486       (goto-char p))
487     (if beep
488         (ding))))
489
490 (defvar egg-conversion-wrap-select nil
491   "*Candidate selection wraps around to first candidate, if non-nil.
492 Otherwise stop at the last candidate.")
493
494 (defun egg-next-candidate (n)
495   (interactive "p")
496   (let ((inhibit-read-only t)
497         (last (get-text-property (point) 'egg-bunsetsu-last))
498         (b (egg-get-bunsetsu-info (point)))
499         new i max+ p beep)
500     (setq max+ (egg-get-number-of-candidates b))
501     (if (null max+)
502         (let ((prev-b (egg-get-previous-bunsetsu (point))))
503           (setq i (egg-list-candidates b prev-b)) ; there is a case I=/=0
504           (if (or (> n 1) (< n 0))      ; with N=/=1, start with I
505               (setq i (+ n i))          ; or else (N==1),
506             (setq i (if (= i 0) 1 0)))  ;   I:=1 when I was 0, or else I:=0
507           (setq max+ (egg-get-number-of-candidates b)))
508       (setq i (egg-get-current-candidate-number b))
509       (setq i (+ n i)))
510     (if (null max+)
511       (setq beep t)
512      (cond
513       ((< i 0)                          ; go backward as if it is ring
514        (while (< i 0)
515          (setq i (+ i max+))))
516       ((< i max+))                      ; OK
517       (egg-conversion-wrap-select       ; go backward as if it is ring
518        (while (>= i max+)
519          (setq i (- i max+))))
520       ((setq i (1- max+)                ; don't go forward 
521              beep t)))
522       (setq new (egg-decide-candidate b i))
523       (setq p (point))
524       (delete-region p (progn (forward-char) (point)))
525       (insert (egg-make-bunsetsu new last))
526       (goto-char p))
527     (if beep
528         (ding))))
529
530 (defun egg-previous-candidate (n)
531   (interactive "p")
532   (egg-next-candidate (- n)))
533
534 (defun egg-reconvert-bunsetsu-internal (n func)
535   (let ((inhibit-read-only t)
536         (p (point))
537         source last bunsetsu-list)
538     (if (<= n 0)
539         (beep)
540       (while (and (null last) (> n 0))
541         (setq source (concat source
542                              (egg-get-bunsetsu-converted
543                               (egg-get-bunsetsu-info (point))))
544               last (get-text-property (point) 'egg-bunsetsu-last)
545               n (1- n))
546         (forward-char))
547       (cond
548        ((> n 0)
549         (beep))
550        ((setq bunsetsu-list (funcall func source egg-current-language))
551         (delete-region p (point))
552         (egg-insert-bunsetsu-list bunsetsu-list (if (eq last t) t 'contine))
553         (goto-char p)
554         (if (egg-get-previous-bunsetsu p)
555             (progn
556               (backward-char)
557               (put-text-property (point) p 'egg-bunsetsu-last 'contine)
558               (forward-char))))))))
559
560 (defun egg-reverse-convert-bunsetu (n)
561   (interactive "p")
562   (egg-reconvert-bunsetsu-internal n 'egg-start-reverse-conversion))
563
564 (defun egg-reconvert-bunsetsu (n)
565   (interactive "p")
566   (egg-reconvert-bunsetsu-internal n 'egg-start-conversion))
567
568 (defun egg-decide-before-point ()
569   (interactive)
570   (let ((inhibit-read-only t)
571         start end len decided undecided bunsetsu source)
572     (setq start (if (get-text-property (1- (point)) 'egg-start)
573                     (point)
574                   (previous-single-property-change (point) 'egg-start))
575           end (if (get-text-property (point) 'egg-end)
576                   (point)
577                 (next-single-property-change (point) 'egg-end))
578           decided (buffer-substring start (point))
579           undecided (buffer-substring (point) end))
580     (delete-region (- start (length egg-conversion-open))
581                    (+ end (length egg-conversion-close)))
582     (setq i 0
583           len (length decided))
584     (while (< i len)
585       (setq bunsetsu (cons (egg-get-bunsetsu-info i decided) bunsetsu)
586             i (egg-next-single-property-change
587                i (egg-bunsetsu-info) decided len))
588       (if (or (= i len)
589               (get-text-property (1- i) 'egg-bunsetsu-last decided))
590           (progn
591             (setq bunsetsu (nreverse bunsetsu))
592             (apply 'insert (mapcar (lambda (b) (egg-get-bunsetsu-converted b))
593                                    bunsetsu))
594             (egg-end-conversion bunsetsu nil)
595             (setq bunsetsu nil))))
596     (setq len (length undecided))
597     (if (= len 0)
598         (progn
599           (egg-do-auto-fill)
600           (run-hooks 'input-method-after-insert-chunk-hook))
601       (setq i 0)
602       (while (< i len)
603         (setq source (cons (egg-get-bunsetsu-source
604                             (egg-get-bunsetsu-info i undecided))
605                            source)
606               i (egg-next-single-property-change
607                  i (egg-bunsetsu-info) undecided len)))
608       (its-restart (apply 'concat (nreverse source)) t))))
609
610 (defun egg-exit-conversion ()
611   (interactive)
612   (goto-char (next-single-property-change (point) 'egg-end))
613   (egg-decide-before-point))
614
615 (defun egg-abort-conversion ()
616   (interactive)
617   (let ((inhibit-read-only t) source)
618     (goto-char (- (if (get-text-property (1- (point)) 'egg-start)
619                       (point)
620                     (previous-single-property-change (point) 'egg-start))
621                   (length egg-conversion-open)))
622     (setq source (get-text-property (point) 'egg-source))
623     (delete-region (point) (+ (next-single-property-change (point) 'egg-end)
624                               (length egg-conversion-close)))
625     (its-restart source)
626     (its-end-of-input-buffer)))
627
628 (defun egg-select-candidate ()
629   (interactive)
630   (let ((inhibit-read-only t)
631         (last (get-text-property (point) 'egg-bunsetsu-last))
632         (b (egg-get-bunsetsu-info (point)))
633         (in-loop t)
634         new i max+ p)
635     (setq max+ (egg-get-number-of-candidates b))
636     (if (null max+)
637         (let ((prev-b (egg-get-previous-bunsetsu (point))))
638           (setq i (egg-list-candidates b prev-b))
639           (setq max+ (egg-get-number-of-candidates b)))
640       (setq i (egg-get-current-candidate-number b)))
641     (let (candidate-list candidate l)
642       (if (null max+)
643           ;; fake 1 candidate
644           (menudiag-select (list 'menu "\e$B8uJd\e(B:"
645                                  (list (egg-get-bunsetsu-converted b))
646                                  (list (egg-get-bunsetsu-converted b))))
647         (setq candidate-list (egg-get-all-candidates b)
648               l candidate-list
649               candidate (menudiag-select (list 'menu "\e$B8uJd\e(B:" l)
650                                          (list (nth i l))))
651         (setq i 0)
652         (while in-loop
653           (if (eq candidate (car l))
654               (setq in-loop nil)
655             (setq l (cdr l)
656                   i (1+ i))))
657         (setq new (egg-decide-candidate b i))
658         (setq p (point))
659         (delete-region p (progn (forward-char) (point)))
660         (insert (egg-make-bunsetsu new last))
661         (goto-char p)))))
662
663 (defun egg-conversion-mode ()
664   "\\{egg-conversion-map}"
665   ;; dummy function to get docstring
666   )
667
668 (defun egg-help-command ()
669   "Display documentation for EGG Conversion mode."
670   (interactive)
671   (with-output-to-temp-buffer "*Help*"
672     (princ "EGG Conversion mode:\n")
673     (princ (documentation 'egg-conversion-mode))
674     (help-setup-xref (cons #'help-xref-mode (current-buffer)) (interactive-p))))
675
676 (provide 'egg-cnv)
677 ;;; egg-cnv.el ends here.