emacs/sekka.el in sekka-0.8.2 vs emacs/sekka.el in sekka-0.8.3
- old
+ new
@@ -1,10 +1,10 @@
;; -*- coding: utf-8 -*-
;;
;; "sekka.el" is a client for Sekka server
;;
-;; Copyright (C) 2010 Kiyoka Nishiyama
+;; Copyright (C) 2010,2011 Kiyoka Nishiyama
;; This program was derived from sumibi.el and yc.el-4.0.13(auther: knak)
;;
;;
;; This file is part of Sekka
;;
@@ -23,10 +23,11 @@
;;
;;; Code:
(require 'cl)
(require 'http-get)
+(require 'popup)
;;;
;;;
;;; customize variables
;;;
@@ -53,10 +54,15 @@
(defcustom sekka-curl "curl"
"curlコマンドの絶対パスを設定する"
:type 'string
:group 'sekka)
+(defcustom sekka-no-proxy-hosts ""
+ "http proxyを使わないホスト名を指定する。複数指定する場合は、コンマで区切る。"
+ :type 'string
+ :group 'sekka)
+
(defcustom sekka-realtime-guide-running-seconds 30
"リアルタイムガイド表示の継続時間(秒数)・ゼロでガイド表示機能が無効になる"
:type 'integer
:group 'sekka)
@@ -65,11 +71,11 @@
:type 'integer
:group 'sekka)
(defcustom sekka-realtime-guide-interval 0.2
"リアルタイムガイド表示を更新する時間間隔"
- :type 'integer
+ :type 'float
:group 'sekka)
(defcustom sekka-roman-method "normal"
"ローマ字入力方式として,normal(通常ローマ字)か、AZIK(拡張ローマ字)のどちらの解釈を優先するか"
:type '(choice (const :tag "normal" "normal")
@@ -85,11 +91,22 @@
"キーボードの指定: 使っているキーボードはjp(日本語106キーボード)、en(英語usキーボード)のどちらか"
:type '(choice (const :tag "jp106-keyboard" "jp")
(const :tag "english(us)-keyboard" "en"))
:group 'sekka)
+(defcustom sekka-jisyo-filename "~/.sekka-jisyo"
+ "sekka-jisyoのファイル名を指定する"
+ :type 'string
+ :group 'sekka)
+(defcustom sekka-use-googleime t
+ "変換結果に、漢字のエントリ type=j が含まれていなかったら、自動的にGoogleIMEを APIを使って変換候補を取得する。
+non-nil で明示的に呼びだすまでGoogleIMEは起動しない。"
+ :type 'boolean
+ :group 'sekka)
+
+
(defface sekka-guide-face
'((((class color) (background light)) (:background "#E0E0E0" :foreground "#F03030")))
"リアルタイムガイドのフェイス(装飾、色などの指定)"
:group 'sekka)
@@ -123,10 +140,12 @@
(defvar sekka-select-mode-hook nil)
(defvar sekka-select-mode-end-hook nil)
(defconst sekka-login-name (user-login-name))
+(defconst sekka-tango-index 0)
+(defconst sekka-annotation-index 1)
(defconst sekka-kind-index 3)
(defconst sekka-id-index 4)
;;--- デバッグメッセージ出力
(defvar sekka-psudo-server nil) ; クライアント単体で仮想的にサーバーに接続しているようにしてテストするモード
@@ -162,10 +181,12 @@
(defvar sekka-henkan-separeter " ") ; fence mode separeter
(defvar sekka-cand-cur 0) ; カレント候補番号
(defvar sekka-cand-cur-backup 0) ; カレント候補番号(UNDO用に退避する変数)
(defvar sekka-cand-len nil) ; 候補数
(defvar sekka-last-fix "") ; 最後に確定した文字列
+(defvar sekka-last-roman "") ; 最後にsekka-serverにリクエストしたローマ字文字列
+(defvar sekka-select-operation-times 0) ; 選択操作回数
(defvar sekka-henkan-kouho-list nil) ; 変換結果リスト(サーバから帰ってきたデータそのもの)
;; その他
(defvar sekka-markers '()) ; 単語の開始、終了位置のpair。 次のような形式で保存する ( 1 . 2 )
@@ -269,17 +290,30 @@
;; ("limit" . 2)
;; ("method" . "normal")
;; )
(defun sekka-rest-request (func-name arg-alist)
(if sekka-psudo-server
- ;; クライアント単体で仮想的にサーバーに接続しているようにしてテストするモード
- "((\"変換\" nil \"へんかん\" j 0) (\"変化\" nil \"へんか\" j 1) (\"ヘンカン\" nil \"へんかん\" k 2) (\"へんかん\" nil \"へんかん\" h 3))"
- ;;"((\"変換\" nil \"へんかん\" j 0) (\"変化\" nil \"へんか\" j 1))"
+ (cond
+ ((string-equal func-name "henkan")
+ ;; クライアント単体で仮想的にサーバーに接続しているようにしてテストするモード
+ ;; result of /henkan
+ ;;"((\"変換\" nil \"へんかん\" j 0) (\"変化\" nil \"へんか\" j 1) (\"ヘンカン\" nil \"へんかん\" k 2) (\"へんかん\" nil \"へんかん\" h 3))")
+ "((\"ヨンモジジュクゴ\" nil \"よんもじじゅくご\" k 0) (\"よんもじじゅくご\" nil \"よんもじじゅくご\" h 1))")
+ ((string-equal func-name "googleime")
+ ;; result of /google_ime
+ ;; 1) よんもじじゅくご
+ "(\"四文字熟語\" \"4文字熟語\" \"4文字熟語\" \"よんもじじゅくご\" \"ヨンモジジュクゴ\")"
+ ;; 2) しょかいきどう
+ ;; "(\"初回起動\", \"諸快気堂\", \"諸開基堂\", \"しょかいきどう\", \"ショカイキドウ\")"
+ ))
;; 実際のサーバに接続する
(let ((command
(concat
sekka-curl " --silent --show-error "
+ (if (< 0 (length sekka-no-proxy-hosts))
+ (concat " --noproxy " sekka-no-proxy-hosts)
+ "")
(format " --max-time %d " sekka-server-timeout)
" --insecure "
" --header 'Content-Type: application/x-www-form-urlencoded' "
(format "%s%s " sekka-server-url func-name)
(sekka-construct-curl-argstr (cons
@@ -349,33 +383,57 @@
(tango . ,tango)))))
(sekka-debug-print (format "kakutei-result:%S\n" result))
(message result)
t))
+
;;
+;; GoogleImeAPIリクエストをサーバーに送る
+;;
+(defun sekka-googleime-request (yomi)
+ (sekka-debug-print (format "googleime yomi=[%s]\n" yomi))
+
+ ;;(message "Requesting to sekka server...")
+
+ (let ((result (sekka-rest-request "googleime" `(
+ (yomi . ,yomi)))))
+ (sekka-debug-print (format "googleime-result:%S\n" result))
+ (progn
+ (message nil)
+ (condition-case err
+ (read result)
+ (end-of-file
+ (progn
+ (message "Parse error for parsing result of Sekka Server.")
+ '()))))))
+
+
+;;
;; ユーザー語彙をサーバーに再度登録する。
;;
(defun sekka-register-userdict (&optional arg)
"ユーザー辞書をサーバーに再度アップロードする"
(interactive "P")
(sekka-register-userdict-internal))
;;
;; ユーザー語彙をサーバーに登録する。
-;;
-(defun sekka-register-userdict-internal ()
- (let* ((str (sekka-get-jisyo-str "~/.sekka-jisyo"))
+;; only-first が t の時は、1ブロック目だけを登録する
+(defun sekka-register-userdict-internal (&optional only-first)
+ (let* ((str (sekka-get-jisyo-str sekka-jisyo-filename))
(str-lst (sekka-divide-into-few-line str)))
(mapcar
(lambda (x)
;;(message "Requesting to sekka server...")
(sekka-debug-print (format "register [%s]\n" x))
(let ((result (sekka-rest-request "register" `((dict . ,x)))))
(sekka-debug-print (format "register-result:%S\n" result))
(message result)))
- str-lst)
+ (if only-first
+ (list (car str-lst))
+ str-lst))
t))
;;
;; ユーザー語彙をサーバーから全て削除する
@@ -425,31 +483,60 @@
)
result))
(reverse result))
'()))
+
+(defun sekka-file-existp (file)
+ "FILE が存在するかどうかをチェックする。 t か nil で結果を返す"
+ (let* ((file (or (car-safe file)
+ file))
+ (file (expand-file-name file)))
+ (file-exists-p file)))
+
+
(defun sekka-get-jisyo-str (file &optional nomsg)
"FILE を開いて Sekka辞書バッファを作り、バッファ1行1文字列のリストで返す"
- (when file
- (let* ((file (or (car-safe file)
- file))
- (file (expand-file-name file)))
- (if (not (file-exists-p file))
- (progn
- (message (format "Sekka辞書 %s が存在しません..." file))
- nil)
- (let ((str "")
- (buf-name (file-name-nondirectory file)))
- (save-excursion
- (find-file-read-only file)
- (setq str (with-current-buffer (get-buffer buf-name)
- (buffer-substring-no-properties (point-min) (point-max))))
- (message (format "Sekka辞書 %s を開いています...完了!" (file-name-nondirectory file)))
- (kill-buffer-if-not-modified (get-buffer buf-name)))
- str)))))
+ (if (sekka-file-existp file)
+ (let ((str "")
+ (buf-name (file-name-nondirectory file)))
+ (save-excursion
+ (find-file-read-only file)
+ (setq str (with-current-buffer (get-buffer buf-name)
+ (buffer-substring-no-properties (point-min) (point-max))))
+ (message (format "Sekka辞書 %s を開いています...完了!" (file-name-nondirectory file)))
+ (kill-buffer-if-not-modified (get-buffer buf-name)))
+ str)
+ (message (format "Sekka辞書 %s が存在しません..." file))))
+(defun sekka-add-new-word-to-jisyo (file yomi tango)
+ "FILE Sekka辞書ファイルと見做し、ファイルの先頭に「読み」と「単語」のペアを書き込む
+登録が成功したかどうかを t or nil で返す"
+ (if (sekka-file-existp file)
+ (let ((buf-name (file-name-nondirectory file))
+ (added nil))
+ (save-excursion
+ (find-file file)
+ (with-current-buffer (get-buffer buf-name)
+ (goto-char (point-min))
+ (let ((newstr (format "%s /%s/" yomi tango)))
+ (when (not (search-forward newstr nil t))
+ (insert newstr)
+ (insert "\n")
+ (save-buffer)
+ (setq added t)
+ )))
+ (kill-buffer-if-not-modified (get-buffer buf-name)))
+ added)
+ (progn
+ (message (format "Sekka辞書 %s が存在しません..." file))
+ nil)))
+
+
+
+
;; ポータブル文字列置換( EmacsとXEmacsの両方で動く )
(defun sekka-replace-regexp-in-string (regexp replace str)
(cond ((featurep 'xemacs)
(replace-in-string str regexp replace))
(t
@@ -610,13 +697,62 @@
(define-key sekka-select-mode-map "\C-u" 'sekka-select-hiragana)
(define-key sekka-select-mode-map "\C-i" 'sekka-select-katakana)
(define-key sekka-select-mode-map "\C-k" 'sekka-select-katakana)
(define-key sekka-select-mode-map "\C-l" 'sekka-select-hankaku)
(define-key sekka-select-mode-map "\C-e" 'sekka-select-zenkaku)
+(define-key sekka-select-mode-map "\C-r" 'sekka-add-new-word)
+(defvar sekka-popup-menu-keymap
+ (let ((map (make-sparse-keymap)))
+ (define-key map "\r" 'popup-select)
+ (define-key map "\C-f" 'popup-open)
+ (define-key map [right] 'popup-open)
+ (define-key map "\C-b" 'popup-close)
+ (define-key map [left] 'popup-close)
+ (define-key map "\C-n" 'popup-next)
+ (define-key map "\C-j" 'popup-next)
+ (define-key map [down] 'popup-next)
+ (define-key map "\C-p" 'popup-previous)
+ (define-key map [up] 'popup-previous)
+
+ (define-key map [f1] 'popup-help)
+ (define-key map (kbd "\C-?") 'popup-help)
+
+ (define-key map "\C-s" 'popup-isearch)
+ (define-key map "\C-g" 'popup-close)
+ map))
+
+;; 選択操作回数のインクリメント
+(defun sekka-select-operation-inc ()
+ (incf sekka-select-operation-times)
+ (when (< 3 sekka-select-operation-times)
+ (sekka-select-operation-reset)
+ (let* ((lst
+ (mapcar
+ (lambda (x)
+ (concat
+ (nth sekka-tango-index x)
+ " ; "
+ (nth sekka-annotation-index x)))
+ sekka-henkan-kouho-list))
+ (map (make-sparse-keymap))
+ (result
+ (popup-menu* lst
+ :scroll-bar t
+ :margin t
+ :keymap sekka-popup-menu-keymap)))
+ (let ((selected-word (car (split-string result " "))))
+ (setq sekka-cand-cur (sekka-find-by-tango selected-word))
+ (sekka-select-kakutei)))))
+
+
+;; 選択操作回数のリセット
+(defun sekka-select-operation-reset ()
+ (setq sekka-select-operation-times 0))
+
;; 変換を確定し入力されたキーを再入力する関数
(defun sekka-kakutei-and-self-insert (arg)
"候補選択を確定し、入力された文字を入力する"
(interactive "P")
(sekka-select-kakutei)
@@ -645,10 +781,11 @@
(kind (nth sekka-kind-index kouho)))
(when (eq 'j kind)
(sekka-kakutei-request key tango)))
(setq sekka-select-mode nil)
(run-hooks 'sekka-select-mode-end-hook)
+ (sekka-select-operation-reset)
(sekka-select-update-display)
(sekka-history-push))
;; 候補選択をキャンセルする
@@ -669,10 +806,11 @@
(interactive)
;; 前の候補に切りかえる
(decf sekka-cand-cur)
(when (> 0 sekka-cand-cur)
(setq sekka-cand-cur (- sekka-cand-len 1)))
+ (sekka-select-operation-inc)
(sekka-select-update-display))
;; 次の候補に進める
(defun sekka-select-next ()
"次の候補に進める"
@@ -680,42 +818,63 @@
;; 次の候補に切りかえる
(setq sekka-cand-cur
(if (< sekka-cand-cur (- sekka-cand-len 1))
(+ sekka-cand-cur 1)
0))
+ (sekka-select-operation-inc)
(sekka-select-update-display))
+;; 指定された tango のindex番号を返す
+(defun sekka-find-by-tango ( tango )
+ (let ((result-index nil))
+ (mapcar
+ (lambda (x)
+ (let ((_tango (nth sekka-tango-index x)))
+ (when (string-equal _tango tango)
+ (setq result-index (nth sekka-id-index x)))))
+ sekka-henkan-kouho-list)
+ (sekka-debug-print (format "sekka-find-by-tango: tango=%s result=%S \n" tango result-index))
+ result-index))
+
;; 指定された type の候補を抜き出す
(defun sekka-select-by-type-filter ( _type )
(let ((lst '()))
(mapcar
(lambda (x)
(let ((sym (nth sekka-kind-index x)))
(when (eq sym _type)
(push x lst))))
sekka-henkan-kouho-list)
- (sekka-debug-print (format "filterd-lst = %S" (reverse lst)))
+ (sekka-debug-print (format "filterd-lst = %S\n" (reverse lst)))
(car (reverse lst))))
+;; 指定された type の候補が存在するか調べる
+(defun sekka-include-typep ( _type )
+ (not (null (sekka-select-by-type-filter _type))))
+
;; 指定された type の候補に強制的に切りかえる
+;; 切りかえが成功したかどうかを t or nil で返す。
(defun sekka-select-by-type ( _type )
(let ((kouho (sekka-select-by-type-filter _type)))
(if (null kouho)
- (cond
- ((eq _type 'j)
- (message "Sekka: 漢字の候補はありません。"))
- ((eq _type 'h)
- (message "Sekka: ひらがなの候補はありません。"))
- ((eq _type 'k)
- (message "Sekka: カタカナの候補はありません。"))
- ((eq _type 'l)
- (message "Sekka: 半角の候補はありません。"))
- ((eq _type 'z)
- (message "Sekka: 全角の候補はありません。")))
+ (begin
+ (cond
+ ((eq _type 'j)
+ (message "Sekka: 漢字の候補はありません。"))
+ ((eq _type 'h)
+ (message "Sekka: ひらがなの候補はありません。"))
+ ((eq _type 'k)
+ (message "Sekka: カタカナの候補はありません。"))
+ ((eq _type 'l)
+ (message "Sekka: 半角の候補はありません。"))
+ ((eq _type 'z)
+ (message "Sekka: 全角の候補はありません。"))
+ nil))
(let ((num (nth sekka-id-index kouho)))
(setq sekka-cand-cur num)
- (sekka-select-update-display)))))
+ (sekka-select-update-display)
+ t))))
(defun sekka-select-kanji ()
"漢字候補に強制的に切りかえる"
(interactive)
(sekka-select-by-type 'j))
@@ -739,10 +898,69 @@
"半角候補に強制的に切りかえる"
(interactive)
(sekka-select-by-type 'z))
+(defun sekka-replace-kakutei-word (b e insert-word)
+ ;; UNDO抑制開始
+ (sekka-disable-undo)
+
+ (delete-region b e)
+
+ (insert insert-word)
+ (message (format "replaced by new word [%s]" insert-word))
+ ;; UNDO再開
+ (sekka-enable-undo))
+
+
+;; 登録語リストからユーザーに該当単語を選択してもらう
+(defun sekka-add-new-word-sub (yomi lst)
+ (let* ((etc "(自分で入力する)")
+ (lst (if (stringp lst)
+ (progn
+ (message lst) ;; サーバーから返ってきたエラーメッセージを表示
+ '())
+ lst))
+ (result (popup-menu*
+ (append lst `(,etc))
+ :margin t
+ :keymap sekka-popup-menu-keymap))
+ (b (copy-marker sekka-fence-start))
+ (e (copy-marker sekka-fence-end)))
+ (let ((tango
+ (if (string-equal result etc)
+ (save-current-buffer
+ (read-string (format "%sに対応する単語:" yomi)))
+ result)))
+ ;; 新しい単語で確定する
+ (sekka-replace-kakutei-word (marker-position b)
+ (marker-position e)
+ tango)
+ ;; .sekka-jisyoとサーバーの両方に新しい単語を登録する
+ (let ((added (sekka-add-new-word-to-jisyo sekka-jisyo-filename yomi tango)))
+ (if added
+ (progn
+ (sekka-register-userdict-internal t)
+ (message (format "Sekka辞書 %s に単語(%s /%s/)を保存しました!" sekka-jisyo-filename yomi tango)))
+ (message (format "Sekka辞書 %s に 単語(%s /%s/)を追加しませんでした(登録済)" sekka-jisyo-filename yomi tango)))))))
+
+
+(defun sekka-add-new-word ()
+ "変換候補のよみ(平仮名)に対応する新しい単語を追加する"
+ (interactive)
+ (setq case-fold-search nil)
+ (when (sekka-select-by-type 'h)
+ (let* ((kouho (nth sekka-cand-cur sekka-henkan-kouho-list))
+ (hiragana (car kouho)))
+ (sekka-debug-print (format "sekka-register-new-word: sekka-last-roman=[%s] hiragana=%s result=%S\n" sekka-last-roman hiragana (string-match-p "^[A-Z][^A-Z]+$" sekka-last-roman)))
+ (when (string-match-p "^[A-Z][^A-Z]+$" sekka-last-roman)
+ (sekka-select-kakutei)
+ (sekka-add-new-word-sub
+ hiragana
+ (sekka-googleime-request hiragana))))))
+
+
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 変換履歴操作関数
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defun sekka-history-gc ()
@@ -893,18 +1111,21 @@
(b (+ end gap))
(e end))
(when (sekka-henkan-region b e)
(if (eq (char-before b) ?/)
(setq b (- b 1)))
+ (setq sekka-last-roman (buffer-substring-no-properties b e))
(delete-region b e)
(goto-char b)
(insert (sekka-get-display-string))
(setq e (point))
(sekka-display-function b e nil)
(sekka-select-kakutei)
+ (when sekka-use-googleime
+ (when (not (sekka-include-typep 'j))
+ (sekka-add-new-word)))
)))))
-
((sekka-kanji (preceding-char))
;; カーソル直前が 全角で漢字以外 だったら候補選択モードに移行する。
;; また、最後に確定した文字列と同じかどうかも確認する。
@@ -1196,10 +1417,10 @@
;; input-method として登録する。
(set-language-info "Japanese" 'input-method "japanese-sekka")
(setq default-input-method "japanese-sekka")
(defconst sekka-version
- "0.8.2" ;;SEKKA-VERSION
+ "0.8.3" ;;SEKKA-VERSION
)
(defun sekka-version (&optional arg)
"入力モード変更"
(interactive "P")
(message sekka-version))