;;; egg/anthy.el --- ANTHY Support (high level interface) in Egg ;;; Input Method Architecture ;; Copyright (C) 2002 The Free Software Initiative of Japan ;; 2015 Hiroki Sato ;; Author: NIIBE Yutaka ;; Keywords: mule, multilingual, input method ;; This file is part of EGG. ;; EGG is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation; either version 2, or (at your option) ;; any later version. ;; EGG is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with GNU Emacs; see the file COPYING. If not, write to the ;; Free Software Foundation, Inc., ;; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ;;; Commentary: ;;; Code: (require 'egg) (require 'egg-edep) (defgroup anthy-egg nil "Anthy interface for Tamago-tsunagi." :group 'egg) (defcustom anthy-egg-use-utf8 nil "*Use UTF-8 for anthy-agent and anthy-dic-tool." :group 'anthy-egg :type '(choice (const :tag "Use UTF8" t) (const :tag "No use UTF8" nil))) (defcustom anthy-egg-input-method-list '("japanese-egg-anthy") "*List of input method to use egg-anthy." :group 'anthy-egg :type '(repeat (string :format "Input method: %v\n" :size 0))) (defcustom anthy-egg-use-chinese-korean-server nil "*Use egg-anthy with Chinese and/or Korean servers." :group 'anthy-egg :type '(choice (const :tag "Use Anthy with Chinese and/or Korean servers" t) (const :tag "No use" nil))) (setplist 'anthy-egg-conversion-backend '(egg-start-conversion anthy-egg-convert egg-get-bunsetsu-source anthy-egg-get-bunsetsu-source egg-get-bunsetsu-converted anthy-egg-get-bunsetsu-converted egg-list-candidates anthy-egg-get-candidates egg-decide-candidate anthy-egg-select-candidate egg-change-bunsetsu-length anthy-egg-resize-segment egg-end-conversion anthy-egg-commit ;; egg-special-candidate anthy-egg-special-candidate egg-word-registration anthy-egg-word-registration ;; egg-get-source-language anthy-egg-get-source-language egg-get-converted-language anthy-egg-get-converted-language)) (defconst anthy-egg-backend-alist '((Japanese ((anthy-egg-conversion-backend))))) (egg-set-finalize-backend '(anthy-egg-finalize-backend)) (defvar anthy-egg-proc nil "Process of ANTHY helper agent.") (defvar anthy-egg-version nil) (defvar anthy-egg-el-version "20070419") (defvar anthy-egg-anthy-agent-version "") (defun anthy-egg-version () (interactive) (message "anthy-egg/anthyipc/anthy-agent: %s" anthy-egg-version) anthy-egg-version) ;; ::= ;; ::= (defvar anthy-egg-environment-pool nil "Environments for ANTHY kana-kanji conversion, to be used.") (defvar anthy-egg-environments-in-use nil "Environments for ANTHY kana-kanji conversion, in use.") ;; ;; ::= ;; [ ] (defsubst anthy-egg-make-bunsetsu (env source converted seg-no) (egg-bunsetsu-create 'anthy-egg-conversion-backend (vector env source converted nil 0 seg-no))) (defsubst anthy-egg-bunsetsu-get-env (b) (aref (egg-bunsetsu-get-info b) 0)) (defsubst anthy-egg-bunsetsu-get-source (b) (aref (egg-bunsetsu-get-info b) 1)) (defsubst anthy-egg-bunsetsu-get-converted (b) (aref (egg-bunsetsu-get-info b) 2)) (defsubst anthy-egg-bunsetsu-get-candidates (b) (aref (egg-bunsetsu-get-info b) 3)) (defsubst anthy-egg-bunsetsu-set-candidates (b z) (aset (egg-bunsetsu-get-info b) 3 z)) (defsubst anthy-egg-bunsetsu-get-candidate-pos (b) (aref (egg-bunsetsu-get-info b) 4)) (defsubst anthy-egg-bunsetsu-set-candidate-pos (b zp) (aset (egg-bunsetsu-get-info b) 4 zp)) (defsubst anthy-egg-bunsetsu-get-seg-no (b) (aref (egg-bunsetsu-get-info b) 5)) (defun anthy-egg-get-bunsetsu-source (b) (anthy-egg-bunsetsu-get-source b)) (defun anthy-egg-get-bunsetsu-converted (b) (let ((cands (anthy-egg-bunsetsu-get-candidates b))) (if cands (nth (anthy-egg-bunsetsu-get-candidate-pos b) cands) (anthy-egg-bunsetsu-get-converted b)))) (defun anthy-egg-get-source-language (b) 'Japanese) (defun anthy-egg-get-converted-language (b) 'Japanese) (defvar anthy-egg-agent-buffer-name " *anthy-egg*") ;; Getting new context-descriptor, and returns environment with 'inuse' bit (defun anthy-egg-new-environment () (if (null anthy-egg-proc) (let ((buf (get-buffer-create anthy-egg-agent-buffer-name)) (cs (if anthy-egg-use-utf8 'utf-8-dos 'euc-japan-dos)) (process-connection-type nil)) ; avoid using pty (setq anthy-egg-proc (apply 'start-process "anthy-egg-agent" buf "anthy-agent" (if anthy-egg-use-utf8 '("--egg" "--utf8") '("--egg")))) (set-process-query-on-exit-flag anthy-egg-proc nil) (set-process-coding-system anthy-egg-proc cs cs) (set-process-sentinel anthy-egg-proc 'anthy-egg-proc-sentinel) (set-marker-insertion-type (process-mark anthy-egg-proc) t) (with-current-buffer buf (erase-buffer) (buffer-disable-undo)) (anthyipc-get-greeting anthy-egg-proc))) ;; Patch http://www.freebsd.org/cgi/query-pr.cgi?pr=68617 (anthyipc-new-context anthy-egg-proc)) ;;; XXX: Don't kill buffer (for now) so that I can debug this program (defun anthy-egg-proc-sentinel (proc reason) ;; (kill-buffer (process-buffer proc)) (setq anthy-egg-proc nil anthy-egg-environments-in-use nil anthy-egg-environment-pool nil)) (defun anthy-egg-get-environment () "Return the ANTHY environment." (if anthy-egg-environment-pool (let ((env (car anthy-egg-environment-pool))) (setq anthy-egg-environment-pool (cdr anthy-egg-environment-pool)) (setq anthy-egg-environments-in-use (cons env anthy-egg-environments-in-use)) env) (let ((env (anthy-egg-new-environment))) (setq anthy-egg-environments-in-use (cons env anthy-egg-environments-in-use)) env))) ;; ;; Fake egg functions for UTF-8 ;; (defvar anthy-egg-force-anthy nil) (make-variable-buffer-local 'anthy-egg-force-anthy) (defmacro anthy-egg-utf8-p () `(and anthy-egg-use-utf8 (not anthy-egg-use-chinese-korean-server) (or (equal (egg-get-conversion-backend 'Japanese 0 nil) '(0 (anthy-egg-conversion-backend))) (and (not egg-conversion-backend-alist) (member default-input-method anthy-egg-input-method-list))))) (defadvice egg-toroku-bunsetsu (around force-anthy activate compile) "Advice for force-anthy." (if (anthy-egg-utf8-p) (let ((anthy-egg-force-anthy t)) ad-do-it) ad-do-it)) (defadvice egg-toroku-region (around force-anthy activate compile) "Advice for force-anthy." (if (anthy-egg-utf8-p) (let ((anthy-egg-force-anthy t)) ad-do-it) ad-do-it)) (defadvice egg-convert-region (around force-anthy activate compile) "Advice for force-anthy." (if (and (interactive-p) (anthy-egg-utf8-p)) (let ((anthy-egg-force-anthy t)) ad-do-it) ad-do-it)) (defadvice egg-separate-languages (around force-anthy activate compile) "Advice for force-anthy." (if (and anthy-egg-force-anthy (or (not last-lang) (eq last-lang 'Japanese))) (let ((len (length str))) (egg-remove-all-text-properties 0 len str) (put-text-property 0 len 'egg-lang 'Japanese str)) ad-do-it)) ;; ;; Returns list of bunsetsu ;; (defun anthy-egg-convert (backend yomi &optional context) "Convert YOMI string to kanji, and enter conversion mode. Return the list of bunsetsu." ;; Convert Katakana to Hiragana (when (eq last-command 'its-katakana) (setq yomi (japanese-hiragana yomi))) (let ((env (anthy-egg-get-environment))) (anthyipc-convert anthy-egg-proc env yomi))) ;; ;; Pool the context ;; (defun anthy-egg-commit (bunsetsu-list abort) (let ((env (anthy-egg-bunsetsu-get-env (car bunsetsu-list)))) (anthyipc-commit anthy-egg-proc env (if abort 1 0)) ;; Guard twice pool in egg-decide-before-point() ;; Add. Hideyuki SHIRAI at 2005-02-10 (unless (memq env anthy-egg-environment-pool) (setq anthy-egg-environment-pool (cons env anthy-egg-environment-pool))) (setq anthy-egg-environments-in-use (delq env anthy-egg-environments-in-use)) (anthy-egg-debug-check))) ;; ;; Returns ( ) ;; (defun anthy-egg-get-candidates (bunsetsu-list prev-bunsetsu next-bunsetsu major) (let ((bunsetsu (car bunsetsu-list))) (if (anthy-egg-bunsetsu-get-candidates bunsetsu) (cons (anthy-egg-bunsetsu-get-candidate-pos bunsetsu) (anthy-egg-bunsetsu-get-candidates bunsetsu)) (let* ((env (anthy-egg-bunsetsu-get-env bunsetsu)) (seg-no (anthy-egg-bunsetsu-get-seg-no bunsetsu)) (cands (anthyipc-get-candidates anthy-egg-proc env seg-no))) (cons (anthy-egg-bunsetsu-set-candidate-pos bunsetsu 0) (anthy-egg-bunsetsu-set-candidates bunsetsu cands)))))) ;; Returns list of list of bunsetsu (defun anthy-egg-select-candidate (bunsetsu-list candidate-pos prev-b next-b) (let* ((bunsetsu (car bunsetsu-list)) (candidate-list (anthy-egg-bunsetsu-get-candidates bunsetsu)) (candidate (nth candidate-pos candidate-list)) (env (anthy-egg-bunsetsu-get-env bunsetsu)) (seg-no (anthy-egg-bunsetsu-get-seg-no bunsetsu))) (anthy-egg-bunsetsu-set-candidate-pos bunsetsu candidate-pos) ;; Anthy doesn't have capability of changing another segment ;; at the selection of a segment. ;; So, just ignore the result of "SELECT-CANDIDATE" (anthyipc-select-candidate anthy-egg-proc env seg-no candidate-pos) (list (list bunsetsu)))) ;; Returns list of list of bunsetsu (defun anthy-egg-resize-segment (bunsetsu-list prev-b next-b len major) (let ((bunsetsu (car bunsetsu-list))) (let ((env (anthy-egg-bunsetsu-get-env bunsetsu)) (seg-no (anthy-egg-bunsetsu-get-seg-no bunsetsu)) (prevlen (length (anthy-egg-bunsetsu-get-source bunsetsu)))) (let ((r (anthyipc-resize-segment anthy-egg-proc env seg-no (if (< prevlen len) 0 1)))) ;; XXX: I don't know what this means, ;; but this works. Blame EGG. (list (list (car r)) nil (cdr r)))))) (defun anthy-egg-finalize-backend () (when anthy-egg-proc (delete-process anthy-egg-proc) (setq anthy-egg-proc nil))) ;; ;; Add. Hideyuki SHIRAI at 2005-02-10 (defvar anthy-egg-debug t "*Enable debug for egg-anthy.") (defvar anthy-egg-debug-depth 15 "*Display message when over this.") (defadvice egg-abort-conversion (before release-content activate) "Advice on anthy.el" (anthy-egg-pool-content)) (defadvice egg-decide-before-point (before release-content activate) "Advice on anthy.el" (anthy-egg-pool-content)) (defun anthy-egg-pool-content () "Used context move to `pool'." (let* ((bunsetsu (egg-get-bunsetsu-info (point))) (backend (car bunsetsu)) env) (when (eq backend 'anthy-egg-conversion-backend) (setq env (anthy-egg-bunsetsu-get-env bunsetsu)) ;; Guard twice pool in egg-decide-before-point() (unless (memq env anthy-egg-environment-pool) (setq anthy-egg-environment-pool (cons env anthy-egg-environment-pool))) (setq anthy-egg-environments-in-use (delq env anthy-egg-environments-in-use)) (anthy-egg-debug-check)))) (defun anthy-egg-debug-check () "Debug message." (when (and anthy-egg-debug (> (length anthy-egg-environments-in-use) anthy-egg-debug-depth)) (message "egg-anthy debug: in-use %d, pool %d with `%s' => `%s'." (length anthy-egg-environments-in-use) (length anthy-egg-environment-pool) (symbol-name last-command) (symbol-name this-command)) (sit-for 1.5))) (defun anthy-egg-recover () "Recover error Anthy." (interactive) (when anthy-egg-proc (delete-process anthy-egg-proc) (setq anthy-egg-proc nil)) (setq anthy-egg-environments-in-use nil) (setq anthy-egg-environment-pool nil)) (defun anthy-egg-special-candidate (bunsetsu prev-b next-b major type) "Suport Hiragana, Katakana." (let* ((head (car bunsetsu)) (backend (egg-bunsetsu-get-backend head)) (lang (get backend 'language)) source converted zenkouho-list kouho-list pos) ;; Japnese only (setq source (anthy-egg-get-bunsetsu-source head)) (cond ((eq type 'egg-hiragana) (setq converted source)) ((eq type 'egg-katakana) (setq converted (japanese-katakana source)))) (setq zenkouho-list (cdr (anthy-egg-get-candidates bunsetsu prev-b next-b major))) (setq pos (when (setq kouho-list (member converted zenkouho-list)) (- (length zenkouho-list) (length kouho-list)))) (when pos (anthy-egg-select-candidate bunsetsu pos prev-b next-b)))) ;; ;; Dictionary add ;; freq はいくつが良いのか? 1, 10, 100, 1000? 選択? (defvar anthy-egg-hinshi-menu '(("一般名詞" . NOUN) ("固有名詞" . PROPER_NOUN) ("形容詞" . ADJECTIVE) ("副詞" . ADV) ("動詞" . VERB)) "*Anthy の辞書登録用品詞.") (defvar anthy-egg-hinshi-proper-menu '("人名" "地名") "*Anthy の固有名詞") (defvar anthy-egg-hinshi-verb-menu '(("カ行五段活用" . "カ行五段") ("ガ行五段活用" . "ガ行五段") ("サ行五段活用" . "サ行五段") ("タ行五段活用" . "タ行五段") ("ナ行五段活用" . "ナ行五段") ("バ行五段活用" . "バ行五段") ("マ行五段活用" . "マ行五段") ("ラ行五段活用" . "ラ行五段") ("ワ行五段活用" . "ワ行五段")) "*Anthy の動詞活用型.") (defvar anthy-egg-dic-util-command "anthy-dic-tool") (defvar anthy-egg-dic-buffer-name " *anthy-egg-dic*") (defun anthy-egg-add-word-compose-paramlist (param) (let ((str "")) (while param (let* ((cur (car param)) (var (car cur)) (val (if (stringp (cdr cur)) (cdr cur) (if (cdr cur) "y" "n")))) (setq str (concat str var " = " val "\n"))) (setq param (cdr param))) str)) (defun anthy-egg-add-word (yomi freq word paramlist) (let ((buf (get-buffer-create anthy-egg-dic-buffer-name)) (cs (if anthy-egg-use-utf8 'utf-8-unix 'euc-japan-unix)) proc) (with-current-buffer buf (setq proc (apply 'start-process "anthy-egg-dic" buf anthy-egg-dic-util-command (if anthy-egg-use-utf8 '("--append" "--utf8") '("--append")))) (when proc (set-process-coding-system proc cs cs) (set-process-sentinel proc (lambda (proc event) (let ((buf (process-buffer proc))) (when (and (process-buffer proc) (buffer-name (process-buffer proc))) (kill-buffer (process-buffer proc)))))) (process-send-string proc (concat yomi " " (int-to-string freq) " " word "\n")) (process-send-string proc (anthy-egg-add-word-compose-paramlist paramlist)) (process-send-string proc "\n") (process-send-eof proc) t)))) (defun anthy-egg-hinshi-NOUN (kanji) (let ((res '(("品詞" . "名詞"))) (na (y-or-n-p (format " 「%sな」と言いますか? " kanji))) (sa (y-or-n-p (format " 「%sさ」と言いますか? " kanji))) (suru (y-or-n-p (format " 「%sする」と言いますか? " kanji))) (ind (y-or-n-p (format " 「%s」は単独で文節になりますか? " kanji))) (kaku (y-or-n-p (format " 「%sと」と言いますか? " kanji)))) (setq res (cons `("な接続" . ,na) res)) (setq res (cons `("さ接続" . ,sa) res)) (setq res (cons `("する接続" . ,suru) res)) (setq res (cons `("語幹のみで文節" . ,ind) res)) (setq res (cons `("格助詞接続" . ,kaku) res)) res)) (defun anthy-egg-hinshi-PROPER_NOUN (kanji) `(("品詞" . ,(menudiag-select (list 'menu (format "(%s) 活用系:" kanji) anthy-egg-hinshi-proper-menu))))) (defun anthy-egg-hinshi-PERSONAL (kanji) '(("品詞" . "人名"))) (defun anthy-egg-hinshi-PLACE (kanji) '(("品詞" . "地名"))) (defun anthy-egg-hinshi-ADJECTIVE (kanji) '(("品詞" . "形容詞"))) (defun anthy-egg-hinshi-ADV (kanji) (let ((res '(("品詞" . "副詞"))) (to (y-or-n-p (format " 「%sと」と言いますか? " kanji))) (taru (y-or-n-p (format " 「%sたる」と言いますか? " kanji))) (suru (y-or-n-p (format " 「%sする」と言いますか? " kanji))) (ind (y-or-n-p (format " 「%s」は単独で文節になりますか? " kanji)))) (setq res (cons `("と接続" . ,to) res)) (setq res (cons `("たる接続" . ,taru) res)) (setq res (cons `("する接続" . ,suru) res)) (setq res (cons `("語幹のみで文節" . ,ind) res)) res)) (defun anthy-egg-hinshi-VERB (kanji) (let* ((res '(("品詞" . "動詞"))) (key (menudiag-select (list 'menu (format "(%s) 活用系:" kanji) anthy-egg-hinshi-verb-menu))) (meishi (y-or-n-p (format "%s: 連体形を名詞化しますか? " kanji)))) (setq res (cons `("活用" . ,key) res)) (setq res (cons `("連用形の名詞化" . ,meishi) res)) res)) (defun anthy-egg-hinshi-select (kanji yomi) (let ((key (menudiag-select (list 'menu (concat kanji"(" yomi ") " "品詞:") anthy-egg-hinshi-menu)))) (cond ((symbolp key) (funcall (intern (concat "anthy-egg-hinshi-" (symbol-name key))) kanji)) ((stringp key) (cdr (assoc key anthy-egg-hinshi-menu)))))) (defun anthy-egg-word-registration-add (kanji yomi) (let (param freq) (setq param (nreverse (anthy-egg-hinshi-select kanji yomi))) (if (anthy-egg-add-word yomi 1000 kanji param) (list (cdr (car param)) "ユーザ辞書") (message "%s (%s) の登録に失敗しました" kanji yomi)))) (defun anthy-egg-word-registration (backend kanji yomi) "Register a word KANJI with a pronunciation YOMI." (if (or (null (eq (egg-get-language 0 kanji) (anthy-egg-get-converted-language backend))) (next-single-property-change 0 'egg-lang kanji) (null (eq (egg-get-language 0 yomi) (anthy-egg-get-source-language backend))) (next-single-property-change 0 'egg-lang yomi)) (egg-error "word registration: invalid character") (anthy-egg-word-registration-add kanji yomi))) ;;; setup (load "egg/anthyipc") (run-hooks 'anthy-egg-load-hook) ;;;###autoload (defun egg-activate-anthy (&rest arg) "Activate ANTHY backend of Tamago-tsunagi." (apply 'egg-mode (append arg anthy-egg-backend-alist))) ;;; egg/anthy.el ends here.