Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
(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
You can’t perform that action at this time.