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