完美集成 liberime

在 pyim 里面使用 rime 感觉很别扭:

  1. 每次都需要把所有的 key 发给 liberime-process-key 一遍,还要设置 menu/page_size: 200

  2. 中途修改输入不能同步,现在假设用户要输 王七蛋

    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)
2 个赞

今天看你折腾 liberime, 我借鉴了一下,也有一些思路优化 pyim 对 liberime 的集成 虽然还有限制,但不需要手工设置 page_size 了。那时候手残,把 liberime-search 函数删除了,现在又 pr 添加,尴尬了。

1 个赞

tumashu你好,词库考虑用sqlite替换hash表吗?在windows上pyim的内存占用一般要到300MB。

可以试试regcache, 这个后端比较省内存,sqlite集成后不知道速度怎么样,速度慢的话就不行

(defvar pyim-dcache-backend 'pyim-dhashcache

"词库后端引擎.负责缓冲词库并提供搜索词的算法.

可选项为 pyim-dhashcache' 或 pyim-dregcache’.

前者搜索单词速度很快,消耗内存多. 后者搜索单词速度较快,消耗内存少.

`pyim-dregcache’ 速度和词库大小成正比. 当词库接近100M大小时,

在六年历史的笔记本上会有一秒的延迟. 这时建议换用 `pyim-dhashcache’.")

国内某最大的公有云的vpc上的vswtich是sqlite做的后端,速度没问题。具体到输入法上,能否很好的利用sql来查询就不了解了。是否建个表,加上合适的index,速度是否可以飞起来。

用sqlite有两个方面考虑,一个是速度,一个是资源占有率。

1 个赞

一个新思路, :+1:

我也觉得用 sqlite 速度应该没问题。forge 现在是用的 sqlite,没有感觉卡顿,内存表现也满意。不过我并没有输入法的经验,@tumashu 可以参考参考。

呆神,求一个debian10下编译好的liberime [email protected]

另外,pyim里的popup背景和字体颜色在哪自定义的。

这个帖子可以关了,直接用狗哥的 GitHub - DogLooksGood/emacs-rime: RIME ㄓ in Emacs

试了下,不如呆神的pyim好使,可能我习惯了

只要安装好依赖,就可以自动编译 liberime 啊, 给你的编译好的,依赖没有也没法用呀

pyim-page 和 pyim-page-border