;;;; kakasi 用 ELisp
;;;;
;;;; AUTHOR:  小松弘幸 <komatsu@taiyaki.org>
;;;; LICENSE: GPL2
;;;;
;;;; $Id: kakasi.el,v 1.1.1.1 2002/08/25 14:24:47 komatsu Exp $

;; ■インストール
;; ・ダウンロード
;; このファイル (kakasi.el) の他に mell.el, mell-process.el が必要です.
;; mell.el は汎用ライブラリ, mell-process.el は外部プロセス起動用
;; ライブラリです. 最新版はそれぞれ
;; kakasi.el <http://www.taiyaki.org/elisp/kakasi/src/kakasi.el>
;; mell.el <http://www.taiyaki.org/elisp/mell/src/mell.el>
;; mell-process.el <http://www.taiyaki.org/elisp/mell/src/mell-process.el>
;; にあります.
;;
;; ・.emacs の設定
;; ~/elisp/kakasi/ 以下にファイルを置いた場合, 次の 1 行を .emacs などに
;; 追加してください. 既に書かれていれば必要ありません.
;; (setq load-path (cons (expand-file-name "~/elisp/kakasi") load-path))

;; ■ユーザ用コマンド
;; ・kakasi-wakati
;; リージョン内の文字列をわかち書きする.
;; リージョンの指定がない場合は段落を自動的に取得する.
;; 
;; ・kakasi-hiragana
;; リージョン内の文字列をひらがなにする.
;; リージョンの指定がない場合は段落を自動的に取得する.
;; 
;; ・kakasi-romaji
;; リージョン内の文字列をローマ字にする.
;; リージョンの指定がない場合は段落を自動的に取得する.
;; 
;; ・kakasi-wakati-hiragana
;; リージョン内の文字列をわかち書きしてから, ひらがなにする.
;; リージョンの指定がない場合は段落を自動的に取得する.
;; 
;; ・kakasi-wakati-romaji
;; リージョン内の文字列をわかち書きしてから, ローマ字にする.
;; リージョンの指定がない場合は段落を自動的に取得する.
;; 
;; ・kakasi
;; リージョン内の文字列を kakasi の入力とし, 出力結果と置き換える.
;; リージョンの指定がない場合は段落を自動的に取得する.
;; 
;; 最初の実行時に kakasi のオプションを指定する. 2 回目以降は指定した
;; オプションを使用する. オプションを変更する場合は, プリフィックスを
;; 指定して (C-u M-x kakasi) 実行する.
;; 
;; ・kakasi-wakati-string
;; ミニバッファで指定した文字列をわかち書きして, カーソル位置に挿入する.
;; リージョンが明示されている場合はリージョン内の文字を入力とする.
;; 
;; ・kakasi-hiragana-string
;; ミニバッファで指定した文字列をひらがなにして, カーソル位置に挿入する.
;; リージョンが明示されている場合はリージョン内の文字を入力とする.
;; 
;; ・kakasi-romaji-string
;; ミニバッファで指定した文字列をローマ字にして, カーソル位置に挿入する.
;; リージョンが明示されている場合はリージョン内の文字を入力とする.
;; 
;; ・kakasi-wakati-hiragana-string
;; ミニバッファで指定した文字列をわかち書きしてからひらがなにし,
;; カーソル位置に挿入する.
;; リージョンが明示されている場合はリージョン内の文字を入力とする.
;; 
;; ・kakasi-wakati-romaji-string
;; ミニバッファで指定した文字列をわかち書きしてからローマ字にし,
;; カーソル位置に挿入する.
;; リージョンが明示されている場合はリージョン内の文字を入力とする.
;; 
;; ・kakasi-string
;; ミニバッファで指定した文字列を kakasi の入力とし,
;; 出力結果をカーソル位置に挿入する.
;; リージョンが明示されている場合はリージョン内の文字を入力とする.
;; 
;; 最初の実行時に kakasi のオプションを指定する. 2 回目以降は指定した
;; オプションを使用する. オプションを変更する場合は, プリフィックスを
;; 指定して (C-u M-x kakasi-string) 実行する.


;; ■プログラミング用関数
;; ・(kakasi-function-current-word &optional point)
;; point の位置 (デフォルトはカーソル位置) にある単語と,
;; その単語が始まるポイントのコンスを返す.
;; (kakasi-function-current-word 20)
;; -> ("わかち" . 18)
;; 
;; ・(kakasi-function-current-word-hiragana &optional point)
;; point の位置 (デフォルトはカーソル位置) にある単語の読みと,
;; その単語が始まるポイントのコンスを返す.
;; 
;; ・(kakasi-function-current-word-romaji &optional point)
;; point の位置 (デフォルトはカーソル位置) にある単語のローマ字と,
;; その単語が始まるポイントのコンスを返す.
;; 
;; ・(kakasi-function-wakati-string string)
;; 文字列 string をわかち書きして, 単語のリストを返す.
;; 
;; ・(kakasi-function-wakati-region begin end)
;; リージョン begin, end 間の文字列をわかち書きして, 単語のリストを返す.
;; 
;; ・(kakasi-function-hiragana-string string)
;; 文字列 string をひらがなにして返す.
;; 
;; ・(kakasi-function-hiragana-region begin end)
;; リージョン begin, end 間の文字列をひらがなにして返す.
;; 
;; ・(kakasi-function-romaji-string string)
;; 文字列 string をローマ字にして返す.
;; 
;; ・(kakasi-function-romaji-region begin end)
;; リージョン begin, end 間の文字列をローマ字にして返す.
;; 
;; ・(kakasi-function-hiragana-alist-string string)
;; 文字列 string をわかち書きして, 各単語の読みと単語の連想リストを返す.
;; (kakasi-function-hiragana-alist-string "各単語の読み")
;; -> (("かくたんご" . "各単語") ("の" . "の") ("よみ" . "読み"))
;; 
;; ・(kakasi-function-hiragana-alist-region begin end)
;; リージョン begin, end 間の文字列をわかち書きして,
;; 各単語の読みと単語のコンスのリストを返す.
;; 
;; ・(kakasi-function-romaji-alist-string string)
;; 文字列 string をわかち書きして, 各単語のローマ字と単語の連想リストを返す.
;; (kakasi-function-romaji-alist-string "各単語の読み")
;; -> (("kakutango" . "各単語") ("no" . "の") ("yomi" . "読み"))
;; 
;; ・(kakasi-function-romaji-alist-region begin end)
;; リージョン begin, end 間の文字列をわかち書きして,
;; 各単語のローマ字と単語のコンスのリストを返す.


;; ■メーリングリスト
;; kakasi 限定のメーリングリストではありませんが, 小松が作成している 
;; ELisp 全般に関してのメーリングリストを QuickMLで作成しました.
;; 
;; 参加される方は, 以下のようなメールを送信してください. また Cc: に
;; 自分のメールアドレスを付けないでください. QuickML の仕様上, Cc: に
;; 参加者以外のメールアドレスがあると 参加できません.
;; 
;; | To: elisp@taiyaki.quickml.com
;; | From: (自分のアドレス)
;; | Cc: komatsu@taiyaki.org
;; | Subject: 参加します (お名前)
;; | 
;; | (本文にかるく自己紹介でもお願いします, 
;; |  何も書かれていないとシステム上, 登録できません.)

(require 'mell)
(require 'mell-process)

(defconst kakasi-process-alist-default
  '((command 		. "kakasi")
    (message-error-init . "Kakasi の初期化に失敗しました")
    (end-of-output-p-function . kakasi-end-of-output-p)
    (timeout-handler-function . kakasi-timeout-handler)
    (timeout-second           . 2)
    ))

(defconst kakasi-process-alist
  (append kakasi-process-alist-default
	  '((process            . "kakasi")
	    (buffer  		. " *kakasi*")
	    (args-list          . nil)
	    (init-function      . kakasi-init)
	    (exit-function      . kakasi-exit)
	    )))

(defconst kakasi-wakati-process-alist
  (append kakasi-process-alist-default
	  '((process 		. "kakasi-wakati")
	    (buffer  		. " *kakasi-wakati*")
	    (args-list          . ("-u" "-w"))
	    (init-function      . kakasi-wakati-init)
	    (exit-function      . kakasi-wakati-exit)
	    )))

(defconst kakasi-hiragana-process-alist
  (append kakasi-process-alist-default
	  '((process 		. "kakasi-hiragana")
	    (buffer  		. " *kakasi-hiragana*")
	    (args-list          . ("-u" "-JH" "-KH"))
	    (init-function      . kakasi-hiragana-init)
	    (exit-function      . kakasi-hiragana-exit)
	    )))

(defconst kakasi-romaji-process-alist
  (append kakasi-process-alist-default
	  '((process 		. "kakasi-romaji")
	    (buffer  		. " *kakasi-romaji*")
	    (args-list          . ("-u" "-Ha" "-Ka" "-Ja" "-Ea" "-ka"))
	    (init-function      . kakasi-romaji-init)
	    (exit-function      . kakasi-romaji-exit)
	    )))

(defconst kakasi-wakati-hiragana-process-alist
  (append kakasi-process-alist-default
	  '((process 		. "kakasi-wakati-hiragana")
	    (buffer  		. " *kakasi-wakati-hiragana*")
	    (args-list          . ("-u" "-s" "-JH" "-KH"))
	    (init-function      . kakasi-wakati-hiragana-init)
	    (exit-function      . kakasi-wakati-hiragana-exit)
	    )))

(defconst kakasi-wakati-romaji-process-alist
  (append kakasi-process-alist-default
	  '((process 		. "kakasi-wakati-romaji")
	    (buffer  		. " *kakasi-wakati-romaji*")
	    (args-list          . ("-u" "-s" "-Ha" "-Ka" "-Ja" "-Ea" "-ka"))
	    (init-function      . kakasi-wakati-romaji-init)
	    (exit-function      . kakasi-wakati-romaji-exit)
	    )))

(defconst kakasi-end-of-output-delimiter " \0\n")
(defcustom kakasi-surrounding-length 20  "調査する周辺の文字数")

(defvar kakasi-command-current-args-list nil "現在の kakasi オプション.
 kakasi を再起動するかどうかの判定に使用する")
(defvar kakasi-current-args nil 
  "kakasi コマンドを実行した時の, 前回のオプション.
次回実行時のデフォルト値")


;; ------------------------------------------------------------

(defun kakasi-wakati-init (&optional forcep)
  (mell-process-command-init kakasi-wakati-process-alist forcep))
(defun kakasi-wakati-exit ()
  (mell-process-exit kakasi-wakati-process-alist))
(defun kakasi-wakati-send-string (string &optional function)
  (kakasi-send-string string function kakasi-wakati-process-alist))

(defun kakasi-hiragana-init (&optional forcep)
  (mell-process-command-init kakasi-hiragana-process-alist forcep))
(defun kakasi-hiragana-exit ()
  (mell-process-exit kakasi-hiragana-process-alist))
(defun kakasi-hiragana-send-string (string &optional function)
  (kakasi-send-string string function kakasi-hiragana-process-alist))

(defun kakasi-romaji-init (&optional forcep)
  (mell-process-command-init kakasi-romaji-process-alist forcep))
(defun kakasi-romaji-exit ()
  (mell-process-exit kakasi-romaji-process-alist))
(defun kakasi-romaji-send-string (string &optional function)
  (kakasi-send-string string function kakasi-romaji-process-alist))

(defun kakasi-wakati-hiragana-init (&optional forcep)
  (mell-process-command-init kakasi-wakati-hiragana-process-alist forcep))
(defun kakasi-wakati-hiragana-exit ()
  (mell-process-exit kakasi-wakati-hiragana-process-alist))
(defun kakasi-wakati-hiragana-send-string (string &optional function)
  (kakasi-send-string string function kakasi-wakati-hiragana-process-alist))

(defun kakasi-wakati-romaji-init (&optional forcep)
  (mell-process-command-init kakasi-wakati-romaji-process-alist forcep))
(defun kakasi-wakati-romaji-exit ()
  (mell-process-exit kakasi-wakati-romaji-process-alist))
(defun kakasi-wakati-romaji-send-string (string &optional function)
  (kakasi-send-string string function kakasi-wakati-romaji-process-alist))

(defun kakasi-init (&optional forcep)
  (mell-process-command-init kakasi-process-alist forcep))
(defun kakasi-exit ()
  (mell-process-exit kakasi-process-alist))
(defun kakasi-end-of-output-p ()
  (string=
   (buffer-substring 
    (max 1 (- (point-max) (length kakasi-end-of-output-delimiter)))
    (point-max))
   kakasi-end-of-output-delimiter)
  )
(defun kakasi-timeout-handler ()
  (concat (buffer-string) kakasi-end-of-output-delimiter)
  )

(defun kakasi-send-string (string &optional function process-alist)
  (substring 
   (mell-process-send-string (or process-alist kakasi-process-alist)
			     (concat string kakasi-end-of-output-delimiter))
   0 (- (length kakasi-end-of-output-delimiter)))
  )

;; ------------------------------------------------------------

(defun kakasi-command-wakati (string)
  (kakasi-wakati-send-string string))

(defun kakasi-command-hiragana (string)
  (kakasi-hiragana-send-string string))

(defun kakasi-command-romaji (string)
  (kakasi-romaji-send-string string))

(defun kakasi-command-wakati-hiragana (string)
  (kakasi-wakati-hiragana-send-string string))

(defun kakasi-command-wakati-romaji (string)
  (kakasi-wakati-romaji-send-string string))

(defun kakasi-command (string &optional args-list)
  (if (and args-list
	   (not (equal args-list kakasi-command-current-args-list)))
      (progn (kakasi-exit)
	     (setq kakasi-command-current-args-list args-list)))
  (setcdr (assoc 'args-list kakasi-process-alist) args-list)
  (kakasi-send-string string))

;; ------------------------------------------------------------

(defun kakasi-get-word (command-function &optional point)
  "Return a cons of a word on the POINT and a beginning point of the word."
;  "POINT 位置上に存在する単語と, その単語が始まるポイントのコンスを返す."
  ;; Inelegant... 
  (save-excursion
    (save-match-data
      (if point (goto-char point) (setq point (point)))
      (let* ((skip-length (min point (1+ kakasi-surrounding-length)))
	     (base-point (+ (- point skip-length)
			    ;; For EOB, Spaces and Tabs
			    (if (looking-at "\\([ \t\n]\\|$\\)")
				(skip-chars-backward " \t\n") 1)))
	     (string (concat 
		      (buffer-substring 
		       (max (- point kakasi-surrounding-length) (point-min))
		       (min (+ point kakasi-surrounding-length) (point-max)))
		      " "))
	     (word-list (split-string (funcall command-function string)))
	     (length (mell-match-count-string
		      "[ \t\n]" (substring string 0 skip-length)))
	     )
	(while (and (car word-list)
		    (< (+ length (length (car word-list)))
		       skip-length))
	  (setq length (+ length (length (car word-list))))
	  (setq word-list (cdr word-list)))
	(cons (car word-list) (+ base-point length))
      ))))

(defun kakasi-make-alist (var-list val-list)
  (let ((i 0))
    (mapcar '(lambda (var) 
	       (prog1 (cons var (nth i val-list))
		 (setq i (1+ i))))
	    var-list)))

;; ------------------------------------------------------------

;;;; kakasi-function-current-word
(defun kakasi-function-current-word (&optional point)
  (kakasi-get-word 'kakasi-command-wakati point))

;;;; kakasi-function-current-word-hiragana
(defun kakasi-function-current-word-hiragana (&optional point)
  (let ((word-cons (kakasi-function-current-word point)))
    (cons (kakasi-command-hiragana (car word-cons))
	  (cdr word-cons))
    ))

;;;; kakasi-function-current-word-romaji
(defun kakasi-function-current-word-romaji (&optional point)
  (let ((word-cons (kakasi-function-current-word point)))
    (cons (kakasi-command-romaji (car word-cons))
	  (cdr word-cons))
    ))

;;;; kakasi-function-wakati-string
(defun kakasi-function-wakati-string (string)
  (split-string (kakasi-command-wakati string)))

;;;; kakasi-function-wakati-region
(defun kakasi-function-wakati-region (begin end)
  (kakasi-function-wakati-string (buffer-substring begin end)))

;;;; kakasi-function-wakati-hiragana-string
(defun kakasi-function-wakati-hiragana-string (string)
  (split-string (kakasi-command-wakati-hiragana string)))

;;;; kakasi-function-wakati-hiragana-region
(defun kakasi-function-wakati-hiragana-region (begin end)
  (kakasi-function-wakati-hiragana-string (buffer-substring begin end)))

;;;; kakasi-function-wakati-romaji-string
(defun kakasi-function-wakati-romaji-string (string)
  (split-string (kakasi-command-wakati-romaji string)))

;;;; kakasi-function-wakati-romaji-region
(defun kakasi-function-wakati-romaji-region (begin end)
  (kakasi-function-wakati-romaji-string (buffer-substring begin end)))

;;;; kakasi-function-hiragana-string
(defun kakasi-function-hiragana-string (string)
  (kakasi-command-hiragana string))

;;;; kakasi-function-hiragana-region
(defun kakasi-function-hiragana-region (begin end)
  (kakasi-function-hiragana-string (buffer-substring begin end)))
  
;;;; kakasi-function-romaji-string
(defun kakasi-function-romaji-string (string)
  (kakasi-command-romaji string))
  
;;;; kakasi-function-romaji-region
(defun kakasi-function-romaji-region (begin end)
  (kakasi-function-romaji-string (buffer-substring begin end)))
  
;;;; kakasi-function-hiragana-alist-string
(defun kakasi-function-hiragana-alist-string (string)
  (kakasi-make-alist (kakasi-function-wakati-hiragana-string string)
		     (kakasi-function-wakati-string          string)))

;;;; kakasi-function-hiragana-alist-region
(defun kakasi-function-hiragana-alist-region (begin end)
  (kakasi-make-alist (kakasi-function-wakati-hiragana-region begin end)
		     (kakasi-function-wakati-region          begin end)))

;;;; kakasi-function-romaji-alist-string
(defun kakasi-function-romaji-alist-string (string)
  (kakasi-make-alist (kakasi-function-wakati-romaji-string string)
		     (kakasi-function-wakati-string        string)))

;;;; kakasi-function-romaji-alist-region
(defun kakasi-function-romaji-alist-region (begin end)
  (kakasi-make-alist (kakasi-function-wakati-romaji-region begin end)
		     (kakasi-function-wakati-region        begin end)))

;; ------------------------------------------------------------

;;;; kakasi
(defun kakasi (&optional arg)
  (interactive "P")
  (cond ((null kakasi-current-args)
	 (setq kakasi-current-args (read-string "Kakasi options: ")))
	((/= (prefix-numeric-value arg) 1)
	 (setq kakasi-current-args 
	       (read-string "Kakasi options: " kakasi-current-args)))
	)
  (insert (mell-call-function-region-or-paragraph
	   'kakasi-command (list (list kakasi-current-args))
	   )))

;;;; kakasi-wakati
(defun kakasi-wakati ()
  (interactive)
  (insert (mell-call-function-region-or-paragraph
	   'kakasi-command-wakati
	   )))

;;;; kakasi-hiragana
(defun kakasi-hiragana ()
  (interactive)
  (insert (mell-call-function-region-or-paragraph
	   'kakasi-command-hiragana
	   )))

;;;; kakasi-romaji
(defun kakasi-romaji ()
  (interactive)
  (insert (mell-call-function-region-or-paragraph
	   'kakasi-command-romaji
	   )))

;;;; kakasi-wakati-romaji
(defun kakasi-wakati-romaji ()
  (interactive)
  (insert (mell-call-function-region-or-paragraph
	   'kakasi-command-wakati-romaji
	   )))

;;;; kakasi-wakati-hiragana
(defun kakasi-wakati-hiragana ()
  (interactive)
  (insert (mell-call-function-region-or-paragraph
	   'kakasi-command-wakati-hiragana
	   )))

;;;; kakasi-string
(defun kakasi-string (&optional arg)
  (interactive "P")
  (cond ((null kakasi-current-args)
	 (setq kakasi-current-args (read-string "Kakasi options: ")))
	((/= (prefix-numeric-value arg) 1)
	 (setq kakasi-current-args 
	       (read-string "Kakasi options: " kakasi-current-args)))
	)
  (insert (mell-call-function-region-or-string
	   'kakasi-command (list (list kakasi-current-args))
	   )))

;;;; kakasi-wakati-string
(defun kakasi-wakati-string ()
  (interactive)
  (insert (mell-call-function-region-or-string
	   'kakasi-command-wakati
	   )))

;;;; kakasi-hiragana-string
(defun kakasi-hiragana-string ()
  (interactive)
  (insert (mell-call-function-region-or-string
	   'kakasi-command-hiragana
	   )))

;;;; kakasi-romaji-string
(defun kakasi-romaji-string ()
  (interactive)
  (insert (mell-call-function-region-or-string
	   'kakasi-command-romaji
	   )))

;;;; kakasi-wakati-romaji-string
(defun kakasi-wakati-romaji-string ()
  (interactive)
  (insert (mell-call-function-region-or-string
	   'kakasi-command-wakati-romaji
	   )))

;;;; kakasi-wakati-hiragana-string
(defun kakasi-wakati-hiragana-string ()
  (interactive)
  (insert (mell-call-function-region-or-string
	   'kakasi-command-wakati-hiragana
	   )))


(provide 'kakasi)
