(require 'cl-lib) | |
(require 'popup nil t) | |
(defface rime-prompt | |
'((t (:foreground "#ffff00" :background "gray44"))) | |
"选词框") | |
(defvar rime-title "中") | |
(defvar rime-probe-list | |
'(rime-probe-evil-normal-mode | |
rime-probe-program-mode | |
rime-probe-cn-en-switch)) | |
(defvar rime-local-variable-list | |
'(input-method-function | |
deactivate-current-input-method-function | |
rime-exhibit-timer | |
rime-translating | |
rime-output | |
rime-context)) | |
(dolist (var rime-local-variable-list) | |
(defvar var nil) | |
(make-variable-buffer-local var) | |
(put var 'permanent-local t)) | |
(defvar rime-auxiliary-map | |
(let ((map (make-sparse-keymap))) | |
(dolist (i (number-sequence 32 255)) | |
(define-key map (char-to-string i) 'rime-self-insert-command)) | |
(define-key map "\C-d" 'rime-input-delete-forward) | |
(define-key map "\C-h" 'rime-input-delete-backward) | |
(define-key map "\C-f" 'rime-input-forward-point) | |
(define-key map "\C-b" 'rime-input-backward-point) | |
(define-key map "\C-e" 'rime-input-end-of-line) | |
(define-key map "\C-a" 'rime-input-beginning-of-line) | |
(define-key map "\C-n" 'rime-prompt--next-word) | |
(define-key map "\C-p" 'rime-prompt--previous-word) | |
(define-key map "\M-n" 'rime-prompt--next-page) | |
(define-key map "\M-p" 'rime-prompt--previous-page) | |
map) | |
"serves as a lookup table, not used as a mode map") | |
(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-evil-normal-mode () | |
(evil-normal-state-p)) | |
(defun rime-probe-cn-en-switch () | |
(unless (exwm-mode-buffer-p) | |
(let ((str-1 (rime-char-before-to-string 0)) | |
(str-2 (rime-char-before-to-string 1))) | |
;; exclude exwm mode buffer | |
(when str-1 | |
(if (rime-string-match-p " " str-1) | |
;; 中文后面紧接1个空格切换到英文输入 | |
(rime-string-match-p "\\cc" str-2) | |
;; 输入数字或英文标点等不经过输入法转换的字符 | |
;; 或强制输入英文字符后切换到英文输入 | |
(and (not (rime-string-match-p "\\cc" str-1)) | |
;; exclue punctuation | |
(not (looking-back "[[:punct:]]+")) | |
rime-context)))))) | |
(defun rime-english-context-p () | |
(cl-some #'(lambda (x) | |
(if (functionp x) (funcall x) nil)) | |
rime-probe-list)) | |
(defun rime-register-input-method () | |
(register-input-method "rime" "euc-cn" 'rime-activate rime-title)) | |
(defun rime-activate (name) | |
(interactive) | |
(mapc 'make-local-variable rime-local-variable-list) | |
(setq input-method-function 'rime-input-method) | |
(setq deactivate-current-input-method-function #'rime-deactivate) | |
(when (eq (selected-window) (minibuffer-window)) | |
(add-hook 'minibuffer-exit-hook 'rime-exit-from-minibuffer))) | |
(defun rime-deactivate () | |
(mapc 'kill-local-variable rime-local-variable-list)) | |
(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) | |
(with-silent-modifications | |
(unwind-protect | |
(let ((input-string (rime-start-translation key))) | |
(when (and (stringp input-string) | |
(> (length input-string) 0)) | |
(mapcar 'identity input-string))))))) | |
(defun rime-start-translation (key) | |
(let* ((echo-keystrokes 0) | |
(help-char nil) | |
(overriding-terminal-local-map rime-auxiliary-map) | |
(input-method-function nil) | |
(input-method-exit-on-first-char nil) | |
(buffer-undo-list t) | |
(input-method-use-echo-area nil) | |
(modified-p (buffer-modified-p)) | |
(inhibit-modification-hooks t) | |
(inhibit-quit t) | |
last-command-event last-command this-command result) | |
(setq rime-translating t) | |
(liberime-clear-composition) | |
;; Push back the last event on the event queue. | |
(and key (push key unread-command-events)) | |
(while rime-translating | |
(let* ((keyseq (read-key-sequence-vector nil nil nil t)) | |
(rime-cmd (lookup-key rime-auxiliary-map keyseq))) | |
(if (and (not (rime-english-context-p)) | |
(if rime-context | |
;; ignore command other than self-insert when composition is empty | |
(eq rime-cmd 'rime-self-insert-command) | |
(commandp rime-cmd))) | |
(progn | |
(set-buffer-modified-p modified-p) | |
(setq current-input-method-title rime-title) | |
(setq last-command-event (aref keyseq (1- (length keyseq))) | |
last-command this-command | |
this-command rime-cmd) | |
(condition-case-unless-debug err | |
(progn | |
(call-interactively rime-cmd) | |
(rime-prompt--refresh)) | |
(error (message "Input method error: %S" err) | |
(beep))) | |
(setq result rime-output)) | |
; keyseq doen't have any meaning in the context of the input method | |
(setq current-input-method-title "英") | |
(rime-quit-no-clear) | |
;; option 1: just return the keyseq string | |
(message "keyseq: %s rime-cmd: %s (rime-english-context-p): %s rime-context: %s" | |
keyseq rime-cmd (rime-english-context-p) rime-context) | |
(setq result (this-command-keys))))) | |
result)) | |
(defun rime-self-insert-command () | |
(interactive "*") | |
(liberime-process-key last-command-event) | |
(when (setq rime-output (liberime-get-commit)) | |
;; previous key is a commit key (space or number) | |
(rime-terminate))) | |
(defun rime-terminate () | |
(setq rime-translating nil) | |
(setq rime-output nil) | |
(when rime-exhibit-timer | |
(cancel-timer rime-exhibit-timer)) | |
(setq rime-context nil) | |
(liberime-clear-composition)) | |
(defun rime-prompt--refresh () | |
;; Show page. | |
(when (and (null unread-command-events) | |
(null unread-post-input-method-events)) | |
;; update context | |
(setq rime-context (liberime-get-context)) | |
(liberime-commit-composition) | |
(message "%s" rime-context) | |
(if (eq (selected-window) (minibuffer-window)) | |
;; minibuffer 中输入中文时,使用当前输入的下一行来显示候选词。 | |
(rime-prompt--minibuffer-message | |
(concat "\n" (rime-prompt--format-prompt))) | |
;; 普通 buffer | |
(let ((message-log-max nil)) | |
(cond | |
;; exwm-xim uses " *temp*" buffer for input, page should be showed in minibuffer. | |
((equal (buffer-name) " *temp*") | |
(message (rime-prompt--format-prompt))) | |
(t | |
(popup-tip (rime-prompt--format-prompt) | |
:point (point) | |
:margin 1))))))) | |
(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))))) | |
(defun rime-prompt--create-candidate-list (candidates index) | |
"这个函数用于创建在 page 中显示的备选词条菜单。" | |
(let ((i 0) | |
result) | |
(dolist (candidate candidates) | |
(when (> i 0) | |
(push " " result)) | |
;; 高亮当前选择的词条 | |
(push (if (= i index) | |
(propertize (format "[%d.%s]" (1+ i) candidate) | |
'face 'rime-prompt) | |
(format "%d.%s" (1+ i) candidate)) | |
result) | |
(setq i (1+ i))) | |
(mapconcat #'identity (reverse result) ""))) | |
(defun rime-prompt--format-prompt () | |
(let* ((composition (alist-get 'composition rime-context)) | |
(menu (alist-get 'menu rime-context)) | |
(page-no (alist-get 'page-no menu)) | |
(index (alist-get 'highlighted-candidate-index menu)) | |
(candidates (alist-get 'candidates menu))) | |
(format "%s\n%s" | |
(alist-get 'preedit composition) | |
(rime-prompt--create-candidate-list candidates index)))) | |
(defun rime-input-forward-point () | |
(interactive) | |
;; send right | |
(liberime-process-key 39)) | |
(defun rime-input-backward-point () | |
(interactive) | |
;; send left | |
(liberime-process-key 37)) | |
(defun rime-input-end-of-line () | |
(interactive) | |
;; send end | |
(liberime-process-key 35)) | |
(defun rime-input-beginning-of-line () | |
(interactive) | |
;; send home | |
(liberime-process-key 36)) | |
(defun rime-input-delete-backward (&optional n) | |
(interactive) | |
;; send backspace | |
(liberime-process-key 8)) | |
(defun rime-input-delete-forward () | |
"在rime-preedit中向前删除1个字符" | |
(interactive) | |
;; send delete | |
(liberime-process-key 127)) | |
(defun rime-prompt--next-page (arg) | |
(interactive) | |
(liberime-process-key 34)) | |
(defun rime-prompt--previous-page (arg) | |
(interactive) | |
(liberime-process-key 33)) | |
(defun rime-prompt--next-word (arg) | |
(interactive) | |
;; send down | |
(liberime-process-key 40)) | |
(defun rime-prompt--previous-word (arg) | |
(interactive) | |
;; send up | |
(liberime-process-key 38)) | |
(add-hook 'emacs-startup-hook 'rime-register-input-method) | |
(rime-register-input-method) | |
(provide 'rime) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment