Commits (6)
* COMMENT -*- mode: org -*-
#+Date: 2019-09-01
Time-stamp: <2019-10-28>
Time-stamp: <2020-02-16>
* zero-el
zero-el provides zero-input-pinyin, an Emacs pinyin input method for Chinese
and zero-input-framework, which is an emacs Chinese input method framework.
* An introduction to zero-el, including how to install and config zero-el.
For normal configurable variables, see
M-x customize-group zero-input
M-x customize-group zero-input-pinyin
* Zero Pinyin Usage
- Enable zero-input minor mode in emacs buffer.
M-x zero-input-mode
You should bind a global hotkey for this function for easier switch, e.g.
(global-set-key (kbd "<f1>") 'zero-input-mode)
- Type pinyin string, for example, "zhongwen", zero will show candidates
matches this pinyin string. You may choose first candidate by space. You may
choose other candidates by digit keys. You may page down, page up by =/-
Some notes on pinyin string,
- There is no ü on US keyboard, type v instead. For example lve 略.
- Use ' to separate pinyin substring like xi'an 西安
* Zero Framework features
- Auto save user phrases. When you type a new phrase, it will be remembered in
user phrase db.
- To delete system phrase or user phrase, type C-<digit> when candidate is
shown in zero-panel.
- Auto convert Chinese period to dot for digits and English letter and digit
mixed words. For example, to type "3.6" when zero-input-mode is on, type
"3." will insert "3。", just continue type 6, it will be converted to
"3.6". Same thing for "H。265" -> "H.265".
* File list
- zero-input.el
......@@ -43,9 +76,6 @@ and zero-input-framework, which is an emacs Chinese input method framework.
serves as an example of how to use zero framework to create new input
* introduce to zero-el
* License
zero-el is under Apache License 2.0
* COMMENT -*- mode: org -*-
#+Date: 2019-10-08
Time-stamp: <2020-02-04>
Time-stamp: <2020-02-16>
#+STARTUP: content
* notes :entry:
** 2019-04-01 zero-el a Chinese IM framework in emacs; FSM :doc:
......@@ -98,6 +98,8 @@ cd ~/lisp/elisp/zero/
- make a git commit in master branch.
- copy zero-input.el over to pkg branch.
cp zero-input.el ../zero-pkg/
make a git commit in pkg branch.
tag it if it is a stable release.
......@@ -111,11 +113,88 @@ cd ~/lisp/elisp/zero/
* later :entry:
* current :entry:
** 2019-11-03 add completion support for zero-input-set-im
complete registered im symbols.
** 2019-10-23 checkdoc and package-lint can't ignore some non-issues.
I can't run them in git pre-commit hook.
* done :entry:
** 2019-11-03 add completion support for zero-input-set-im
complete registered im symbols.
- 2020-02-16
Function: completing-read prompt collection &optional predicate
make sure non-interactively usage still works.
(zero-input-set-im 'pinyin)
- DONE allow input method name to be string.
to add backward compatibility, symbol is also supported.
symbol is auto converted to lower case string.
string is a better fit for names. it can include unicode strings easier.
- problems
- how to call completing-read when call interactively?
search: use completing-read with interactive function
just call the function when &optional arg is nil.
- (zero-input-set-im 'pinyin)
this doesn't allow input pinyin after switching to table and back。
if then else issue.
- DONE when im name is string, alist eq comparison won't work.
change to string-equal based compare.
search: assq-delete-all but with string-equal
emacs - Elisp: How to delete an element from an association list with string key - Stack Overflow
- (zero-input-set-im 'pinyin)
method not registered.
(assq im-name zero-input-ims)
(assoc im-name zero-input-ims)
- try switch to empty and back.
(zero-input-set-im 'pinyin)
(zero-input-set-im 'empty)
it works.
** 2020-02-16 declare zero-input-punctuation-level, zero-input-full-width-p using defcustom
use set-default to set the value.
set-default is the default setter. no problem.
- also for
INVALID zero-input-auto-fix-dot-between-numbers, already via defcustom.
- problems
- search: defvar-local vs defcustom
just add
(make-variable-buffer-local 'zero-input-punctuation-level)
- DONE zero-input-pinyin-fuzzy-flag, why links in doctstring doesn't work?
use raw string there.
80 width can be avoided by using \ escape at line end.
** 2020-02-15 zero-el:中文标点full模式下,无法输入 fan'gai 翻盖
- 建议取消 preedit 状态下'的自动 commit 1st candidate 并插入引号的功能。
** 2020-02-14 zero-el: H。265 > H.265
** 2020-02-02 zero-el: 如果输入“。”之前和之后输入的是数字,则自动将"。"转换为小数点"."。
(setq zero-input-auto-fix-dot-between-numbers t)
enabled by default.
......@@ -54,6 +54,23 @@
(let ((zero-input-initial-fetch-size 12))
(should (= 12 (zero-input-get-initial-fetch-size)))))
(ert-deftest zero-input-add-recent-insert-char ()
(let ((test-ring (make-ring 3)))
(ring-insert test-ring 'a)
(ring-insert test-ring 'b)
(ring-insert test-ring 'c)
(should (eq 'c (ring-ref test-ring 0)))
(should (eq 'b (ring-ref test-ring 1)))
(should (eq 'a (ring-ref test-ring 2))))
(let ((test-ring (make-ring 3)))
(ring-insert test-ring 'a)
(ring-insert test-ring 'b)
(ring-insert test-ring 'c)
(ring-insert test-ring 'd)
(should (eq 'd (ring-ref test-ring 0)))
(should (eq 'c (ring-ref test-ring 1)))
(should (eq 'b (ring-ref test-ring 2)))))
(provide 'zero-input-framework-test)
;;; zero-input-framework-test.el ends here
......@@ -133,7 +133,7 @@ If item is not in lst, return nil."
;; zero-input-el version
(defvar zero-input-version nil "Zero package version.")
(setq zero-input-version "2.2.1")
(setq zero-input-version "2.5.0")
;; FSM state
(defconst zero-input--state-im-off 'IM-OFF)
......@@ -158,23 +158,37 @@ through")
this is used to help with buffer focus in/out events")
(defvar-local zero-input-state zero-input--state-im-off)
(defvar-local zero-input-full-width-p nil
(defcustom zero-input-full-width-p nil
"Set to t to enable full-width mode.
In full-width mode, commit ascii char will insert full-width char if there is a
corresponding full-width char. This full-width char map is
independent from punctuation map. You can change this via
(defvar-local zero-input-punctuation-level zero-input-punctuation-level-basic
"Punctuation level.
:group 'zero-input
:safe t
:type 'boolean)
(make-variable-buffer-local 'zero-input-full-width-p)
(defcustom zero-input-punctuation-level zero-input-punctuation-level-basic
"Default punctuation level.
Should be one of
:group 'zero-input
:safe t
:type `(choice (const :tag "zero-input-punctuation-level-basic"
(const :tag "zero-input-punctuation-level-full"
(const :tag "zero-input-punctuation-level-none"
(make-variable-buffer-local 'zero-input-punctuation-level)
(defvar zero-input-punctuation-levels (list zero-input-punctuation-level-basic
"Punctuation levels to use when `zero-input-cycle-punctuation-level'.")
(defvar-local zero-input-double-quote-flag nil
"Non-nil means next double quote insert close quote.
......@@ -193,8 +207,8 @@ Otherwise, next single quote insert close quote.")
Used to handle Chinese dot in digit input.
e.g. 1。3 could be converted to 1.3.")
(defcustom zero-input-auto-fix-dot-between-numbers t
"Non-nil means zero should change 1。3 to 1.3."
:group 'zero
"Non-nil means zero should change 1。3 to 1.3, H。264 to H.264."
:group 'zero-input
:type 'boolean)
(defvar-local zero-input-preedit-str "")
(defvar-local zero-input-candidates nil)
......@@ -202,7 +216,7 @@ e.g. 1。3 could be converted to 1.3.")
"How many candidates to show on each page.
Change will be effective only in new `zero-input-mode' buffer."
:group 'zero
:group 'zero-input
:type 'integer)
(defvar-local zero-input-current-page 0 "Current page number. count from 0.")
(defvar-local zero-input-initial-fetch-size 21
......@@ -743,11 +757,15 @@ Argument CH the character that was inserted."
(if (and zero-input-mode zero-input-auto-fix-dot-between-numbers)
(let ((ch (or ch (elt (this-command-keys-vector) 0))))
(zero-input-add-recent-insert-char ch)
;; if user typed digit “。” digit, auto convert “。” to “.”
(cl-flet ((my-digit-char-p (ch) (and (>= ch ?0) (<= ch ?9))))
(when (and (my-digit-char-p (ring-ref zero-input-recent-insert-chars 0))
;; if user typed "[0-9A-Z]。[0-9]", auto convert “。” to “.”
(cl-flet ((my-digit-char-p (ch) (and (>= ch ?0) (<= ch ?9)))
(my-capital-letter-p (ch) (and (>= ch ?A) (<= ch ?Z))))
;; ring-ref index 2 is least recent inserted char.
(when (and (let ((ch (ring-ref zero-input-recent-insert-chars 2)))
(or (my-digit-char-p ch)
(my-capital-letter-p ch)))
(equal ? (ring-ref zero-input-recent-insert-chars 1))
(my-digit-char-p (ring-ref zero-input-recent-insert-chars 2)))
(my-digit-char-p (ring-ref zero-input-recent-insert-chars 0)))
(delete-char -2)
(insert "." (car (ring-elements zero-input-recent-insert-chars))))))))
......@@ -779,11 +797,14 @@ virtual functions corresponding variable
registered input method is saved in `zero-input-ims'"
;; add or replace entry in `zero-input-ims'
(unless (symbolp im-name)
(signal 'wrong-type-argument (list 'symbolp im-name)))
(setq zero-input-ims (assq-delete-all im-name zero-input-ims))
(unless (stringp im-name)
(signal 'wrong-type-argument (list 'stringp im-name)))
(setq zero-input-ims (delq (assoc im-name zero-input-ims) zero-input-ims))
(setq zero-input-ims (push (cons im-name im-functions-alist) zero-input-ims)))
;; Built-in empty input method. It only handles Chinese punctuation.
(zero-input-register-im "empty" nil)
;; public API
......@@ -827,21 +848,32 @@ LEVEL the level to set to."
(message "punctuation level set to %s" zero-input-punctuation-level))
(defun zero-input-set-im (im-name)
(defun zero-input-set-im (&optional im-name)
"Select zero input method for current buffer.
if IM-NAME is nil, use default empty input method"
;; TODO provide auto completion for im-name
(interactive "SSet input method to: ")
IM-NAME (a string) should be a registered input method in zero-input."
;; when switch away from an IM, run last IM's :shutdown function.
(if zero-input-im
(let ((shutdown-func (cdr (assq :shutdown (cdr (assq zero-input-im zero-input-ims))))))
(let ((shutdown-func (cdr (assq :shutdown (cdr (assoc zero-input-im zero-input-ims))))))
(if (functionp shutdown-func)
(funcall shutdown-func))))
(if im-name
(let ((im-functions (cdr (assq im-name zero-input-ims))))
(if im-functions
((null im-name)
;; TODO is there an easier way to provide auto complete in mini buffer?
;; I used a recursive call to the same function.
(let ((im-name-str (completing-read "Set input method to: " zero-input-ims)))
(if (s-blank? im-name-str)
(error "Input method name is required")
(zero-input-set-im im-name-str))))
((symbolp im-name)
;; for backward compatibility
(zero-input-set-im (symbol-name im-name)))
(t (let* ((im-slot (assoc im-name zero-input-ims))
(im-functions (cdr im-slot)))
(if im-slot
(zero-input-debug "switching to %s input method" im-name)
;; TODO create a macro to reduce code duplication and human
;; error.
......@@ -877,16 +909,7 @@ if IM-NAME is nil, use default empty input method"
(if (functionp init-func)
(funcall init-func)))
(setq zero-input-im im-name))
(error "Input method %s not registered in zero" im-name)))
(zero-input-debug "using default empty input method")
(setq zero-input-build-candidates-func 'zero-input-build-candidates-default)
(setq zero-input-build-candidates-async-func 'zero-input-build-candidates-async-default)
(setq zero-input-can-start-sequence-func 'zero-input-can-start-sequence-default)
(setq zero-input-handle-preedit-char-func 'zero-input-handle-preedit-char-default)
(setq zero-input-get-preedit-str-for-panel-func 'zero-input-get-preedit-str-for-panel-default)
(setq zero-input-backspace-func 'zero-input-backspace-default)
(setq zero-input-preedit-start-func nil)
(setq zero-input-preedit-end-func nil)))
(error "Input method %S not registered in zero" im-name))))))
(defun zero-input-set-default-im (im-name)
......@@ -37,21 +37,23 @@
;; basic data and emacs facility
;; these two var is only used in docstring to avoid checkdoc line-too-long
;; error.
(defvar zero-input-pinyin-service-interface-xml-file
(defvar zero-input-pinyin-service-interface-xml-url
(defcustom zero-input-pinyin-fuzzy-flag 0
"Non-nil means use this value as GetCandidatesV2 fuzzy_flag param.
see zero-input-pinyin-service dbus interface xml for document.
"Non-nil means enable fuzzy pinyin when calling zero pinyin service.
Fuzzy pinyin means some shengmu and some yunmu could be used
interchangeably, such as zh <-> z, l <-> n.
For supported values, please see zero-input-pinyin-service dbus
interface xml comment for GetCandidatesV2 method fuzzy_flag param.
You can find the xml file locally at
`zero-input-pinyin-service-interface-xml-file' or online at
:type 'integer
:group 'zero-input-pinyin)
or online at
:group 'zero-input-pinyin
:type 'integer)
(defvar zero-input-pinyin-use-async-fetch nil
"Non-nil means use async dbus call to get candidates.")
(setq zero-input-pinyin-use-async-fetch nil)
......@@ -295,7 +297,9 @@ CH the character user typed."
((= ch zero-input-next-page-key)
(t (let ((str (zero-input-convert-punctuation ch)))
(if str
;; ?' is used as pinyin substring separator, never auto commit on ?'
;; insert when pre-editing.
(if (and str (not (eq ch ?')))
(when (zero-input-pinyin-commit-first-candidate-in-full)
(zero-input-set-state zero-input--state-im-waiting-input)
(insert str))
......@@ -352,7 +356,7 @@ DIGIT 0 means delete 10th candidate."
(defun zero-input-pinyin-register-im ()
"Register pinyin input method in zero framework."
(if zero-input-pinyin-use-async-fetch
'((:build-candidates-async . zero-input-pinyin-build-candidates-async))
......@@ -69,7 +69,7 @@
'((:build-candidates . zero-input-table-build-candidates)
(:can-start-sequence . zero-input-table-can-start-sequence)))
This diff is collapsed.