在 pyim 里面使用 rime 感觉很别扭:
-
每次都需要把所有的 key 发给 liberime-process-key 一遍,还要设置 menu/page_size: 200
-
中途修改输入不能同步,现在假设用户要输 王七蛋
wangqida 选“王” 发现少了个n, 补上 又要选一次“王”
这里写了一个把字符直接发给 rime, 然后显示返回结果的方法。 现在就只差 1 点:如果输入没有 context, 就看不到输入的 pinyin 不知道是我的rime 设置问题还是啥
;; Questions
;; 1. How to send Shift+Delete with liberime-process-key
;; 2. with liberime, there is no way to retrieve user input when liberime-get-context returns nil, eg. after typing "u"
;; how to set rime to treat u and i as legitimate input
;; how to enable rime to suport partial matching
;; eg "idegc ℃ 1" is in custom_phrase.txt, I have to type the whole code idegc to get the symbol
;; 3. can liberime do reverse search: get pinyin for phrase?
(require 'cl-lib)
(defvar rime-probe-list
'(evil-normal-state-p
rime-probe-program-mode
rime-probe-english-context))
(defvar rime-local-variable-list
'(input-method-function
deactivate-current-input-method-function
rime-last-punctuation))
(dolist (var rime-local-variable-list)
(defvar var nil)
(make-variable-buffer-local var)
(put var 'permanent-local t))
(defvar rime-function-keys
`(,@(mapcar (lambda (x)
`(,(char-to-string x) . ,x))
(number-sequence ?1 ?9))
;; Next PageDown
("M-n" . 65366)
;; Prior PageUp
("M-p" . 65365)
;; C-n Down
("C-n" . 65364)
;; Up
("C-p" . 65362)
;; Space
("SPC" . 32)
("RET" . 65293)
("C-d" . 65535)
("<deletechar>" . 65535)
;; Shift+Delete
("C-k" . (65505 65535))
("C-h" . 65288)
;; BackSpace
("DEL" . 65288)
;; Left
("C-b" . 65361)
;; Right
("C-f" . 65363)
;; Home
("C-a" . 65360)
;; End
("C-e" . 65367)))
(defun rime-probe-program-mode ()
"中文输入限制在字符串和 comment 中"
(interactive)
(when (derived-mode-p 'prog-mode)
(let* ((ppss (syntax-ppss (point))))
(not
(or (car (setq ppss (nthcdr 3 ppss)))
(car (setq ppss (cdr ppss)))
(nth 3 ppss))))))
(defun rime-probe-english-context ()
(or
;; 中文后面紧接1个空格切换到英文输入
;; \cC represents any character of category “C”, and according to “M-x describe-categories”
(looking-back "\\cc " (max (line-beginning-position) (- (point) 2)))
;; 英文,数字后保持英文输入
(looking-back "[a-zA-Z0-9]" (max (line-beginning-position) (1- (point))))))
(defun rime-english-mode-p ()
(unless (string-match " *temp*" (buffer-name))
(cl-some #'(lambda (x)
(if (functionp x) (funcall x) nil))
rime-probe-list)))
(defun rime-deactivate ()
(mapc 'kill-local-variable rime-local-variable-list))
(defun rime-activate (name)
(interactive)
(setq input-method-function 'rime-input-method
deactivate-current-input-method-function #'rime-deactivate)
(when (eq (selected-window)
(minibuffer-window))
(add-hook 'minibuffer-exit-hook 'rime-exit-from-minibuffer)))
(defun rime-exit-from-minibuffer ()
(deactivate-input-method)
(when (<= (minibuffer-depth) 1)
(remove-hook 'minibuffer-exit-hook 'rime-exit-from-minibuffer)))
(defun rime-input-method (key)
(if (or buffer-read-only
(not enable-multibyte-characters)
overriding-terminal-local-map
overriding-local-map)
(list key)
(let* ((rime-translating t)
(echo-keystrokes 0)
(help-char nil)
;; (overriding-terminal-local-map rime-map)
;; bind input-method-function to nil to prevent recursion.
(input-method-function nil)
(input-method-exit-on-first-char nil)
;; Hide the original `buffer-undo-list'.
(buffer-undo-list t)
;; preview string in buffer, see quail.el
(input-method-use-echo-area nil)
(inhibit-modification-hooks t)
(inhibit-quit t)
;; rime local variables
result)
(unwind-protect
(with-silent-modifications
(liberime-clear-composition)
;; Push back the last event on the event queue.
(push key unread-command-events)
(rime-start-translation)
(message "result %s type %s" result (type-of result))
(if (stringp result)
(mapcar 'identity result)
(append result nil)))))))
(defun rime-start-translation ()
(while rime-translating
(let* ((keyseq (read-key-sequence-vector nil nil nil t))
(key (aref keyseq (1- (length keyseq))))
(key-name (key-description keyseq))
keysym-num)
;; key processing based on key type
(cond
;; 1. alphabet
((and (= (length key-name) 1)
(string-match "[[:alpha:]]" key-name)
(not (rime-english-mode-p)))
(liberime-process-key key)
(setq rime-last-punctuation nil))
;; 2. same punctuation press twice to go to the next candidate
((and (= (length key-name) 1)
(string-match "[[:punct:]]" key-name)
(not (rime-english-mode-p)))
(liberime-process-key key)
(if (eq rime-last-punctuation key)
(liberime-select-candidate 1)
(liberime-select-candidate 0))
(setq rime-last-punctuation key))
;; 3. function key
((and (setq keysym-num
(cdr (assoc key-name rime-function-keys)))
(liberime-get-context))
(liberime-process-key keysym-num))
(t (setq result keyseq)))
;; continuation check
;; liberime-get-commit returns the result only after all being translated
(if (or result
(setq result (liberime-get-commit)))
(setq rime-translating nil)
;; update prompt and continue
(let* ((context (liberime-get-context))
(composition (alist-get 'composition context))
(composition-length (alist-get 'length composition))
(cursor-pos (alist-get 'cursor-pos composition))
(preedit (alist-get 'preedit composition))
(menu (alist-get 'menu context))
(highlighted-candidate-index (alist-get 'highlighted-candidate-index menu))
(last-page-p (alist-get 'last-page-p menu))
(num-candidates (alist-get 'num-candidates menu))
(page-no (alist-get 'page-no menu))
(candidates (alist-get 'candidates menu)))
(rime-prompt--refresh)))))
result)
(defun rime-prompt--format-prompt ()
(when preedit
(concat
(with-temp-buffer (insert preedit)
(backward-char (- composition-length cursor-pos))
(insert "|")
(buffer-string))
":"
;; candidates
(let (result)
(dolist (i (number-sequence 0 (1- num-candidates)))
;; 高亮当前选择的词条
(push (if (= i highlighted-candidate-index)
(format "[%d.%s]" (1+ i) (nth i candidates))
(format "%d.%s" (1+ i) (nth i candidates)))
result))
(concat
(mapconcat #'identity (reverse result) " ")
;; current page number
(if last-page-p
(format "(%s<)" (1+ page-no))
(format "(%s>)" (1+ page-no))))))))
(defun rime-prompt--refresh ()
(when (and (null unread-command-events)
(null unread-post-input-method-events))
(let ((prompt-str (rime-prompt--format-prompt)))
(if (eq (selected-window)
(minibuffer-window))
;; minibuffer 使用下一行显示候选词
(rime-prompt--minibuffer-message (concat "\n" prompt-str))
;; 普通 buffer
(let ((message-log-max nil))
(cond (t (message prompt-str))))))))
(defun rime-prompt--minibuffer-message (string)
"minibuffer 中需要将原来显示的信息和选词框整合在一起显示"
(message nil)
(let ((inhibit-quit t)
point-1)
(save-excursion
(insert string)
(setq point-1 (point)))
(sit-for 1000000)
(delete-region (point) point-1)
(when quit-flag
(setq quit-flag nil
unread-command-events '(7)))))
;; add word to ~/.emacs.d/rime/my_phrase.dict.yaml and sort
(defun rime-add-to-user-dict ())
;; add word to ~/.emacs.d/rime/custom_phrase.txt and sort:
;; auto add pinyin code, prompt for confirmation
(defun rime-add-to-custom-phrase ())
(defun rime-register-input-method ()
(register-input-method "rime" "euc-cn" 'rime-activate "中"))
(add-hook 'emacs-startup-hook 'rime-register-input-method)
(rime-register-input-method)
(provide 'rime)