Loading README +9 −1 Original line number Diff line number Diff line * COMMENT -*- mode: org -*- #+Date: 2019-09-01 Time-stamp: <2024-05-23> Time-stamp: <2024-05-26> * zero-el Loading @@ -13,6 +13,14 @@ check [[https://gitlab.emacsos.com/sylecn/zero-el/blob/master/README][README in * introduce to zero-el https://blog.emacsos.com/zero-el.html * optional packages - zero-input-panel-posframe an optional zero-input panel implemented using elisp and posframe package. It requires posframe to work. To check difference between zero-input panels, see README in master branch on "Which panel should I use". * License zero-el is under Apache License 2.0 Loading zero-input.el +229 −34 Original line number Diff line number Diff line Loading @@ -12,9 +12,9 @@ ;; See the License for the specific language governing permissions and ;; limitations under the License. ;; Version: 2.8.0 ;; Version: 2.10.1 ;; URL: https://gitlab.emacsos.com/sylecn/zero-el ;; Package-Requires: ((emacs "24.3") (s "1.2.0")) ;; Package-Requires: ((emacs "24.4") (s "1.2.0")) ;;; Commentary: Loading Loading @@ -80,6 +80,8 @@ EVENT and ERROR are error-handler arguments." (add-hook 'dbus-event-error-functions 'zero-input-panel-error-handler) (defvar zero-input-panel-dbus-service-known-name "com.emacsos.zero.Panel1") (defun zero-input-panel-async-call (method _handler &rest args) "Call METHOD on zero-input-panel service asynchronously. Loading @@ -87,7 +89,7 @@ This is a wrapper around `dbus-call-method-asynchronously'. ARGS optional extra args to pass to the wrapped function." (apply 'dbus-call-method-asynchronously :session "com.emacsos.zero.Panel1" ; well known name zero-input-panel-dbus-service-known-name ; well known name "/com/emacsos/zero/Panel1" ; object path "com.emacsos.zero.Panel1.PanelInterface" ; interface name method nil :timeout 500 args)) Loading @@ -99,14 +101,14 @@ ARGS optional extra args to pass to the wrapped function." (defun zero-input-alist-to-asv (hints) "Convert Lisp alist to dbus a{sv} data structure. HINTS should be an alist of form '((k1 [v1type] v1) (k2 [v2type] v2)). HINTS should be an alist of form \\='((k1 [v1type] v1) (k2 [v2type] v2)). For example, \(zero-input-alist-to-asv '((\"name\" \"foo\") \\='((\"name\" \"foo\") (\"timeout\" :int32 10))) => '(:array \\='(:array (:dict-entry \"name\" (:variant \"foo\")) (:dict-entry \"timeout\" (:variant :int32 10)))" (if (null hints) Loading Loading @@ -251,7 +253,18 @@ 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.8.0") (setq zero-input-version "2.10.1") (defvar zero-input-panel-is-ephemeral nil "Stores whether the panel service is ephemeral or not. When a zero-input panel service can not persist its content on key strokes, it should set this to t, so zero-input-framework will call `zero-input-panel-show-candidates' on every keystroke to refresh the candidates list even when no change is needed. A zero-input panel service should revert this variable to nil on exit.") ;; FSM state (defconst zero-input--state-im-off 'IM-OFF) Loading Loading @@ -370,7 +383,7 @@ Change will be effective only in new `zero-input-mode' buffer." (defvar-local zero-input-initial-fetch-size 21 "How many candidates to fetch for the first call to GetCandidates. It's best set to (1+ (* zero-input-candidates-per-page N)) where It\\='s best set to \\=(1+ (* zero-input-candidates-per-page N)) where N is number of pages you want to fetch in initial fetch.") ;; zero-input-fetch-size is reset to 0 when preedit-str changes. ;; zero-input-fetch-size is set to fetch-size in build-candidates-async Loading Loading @@ -469,7 +482,7 @@ STRING and OBJECTS are passed to `format'" (defun zero-input-candidates-on-page (candidates) "Return candidates on current page for given CANDIDATES list." (cl-flet ((take (n lst) (cl-flet ((my-take (n lst) "take the first n element from lst. if there is not enough elements, return lst as it is." (cl-loop Loading @@ -484,7 +497,7 @@ enough elements, return lst as it is." for n* = n then (1- n*) until (or (zerop n*) (null lst*)) finally (return lst*)))) (take zero-input-candidates-per-page (my-take zero-input-candidates-per-page (drop (* zero-input-candidates-per-page zero-input-current-page) candidates)))) (defun zero-input-show-candidates (&optional candidates) Loading Loading @@ -618,17 +631,23 @@ Return CH's Chinese punctuation if CH is converted. Return nil otherwise." (defun zero-input-page-up () "If not at first page, show candidates on previous page." (interactive) (when (> zero-input-current-page 0) (if (> zero-input-current-page 0) (progn (setq zero-input-current-page (1- zero-input-current-page)) (zero-input-show-candidates))) (zero-input-show-candidates)) (when zero-input-panel-is-ephemeral (zero-input-show-candidates)))) (defun zero-input-just-page-down () "Just page down using existing candidates." (let ((len (length zero-input-candidates))) (when (> len (* zero-input-candidates-per-page (1+ zero-input-current-page))) (if (> len (* zero-input-candidates-per-page (1+ zero-input-current-page))) (progn (setq zero-input-current-page (1+ zero-input-current-page)) (zero-input-debug "showing candidates on page %s\n" zero-input-current-page) (zero-input-show-candidates)))) (zero-input-show-candidates)) (when zero-input-panel-is-ephemeral (zero-input-show-candidates))))) (defun zero-input-page-down () "If there is still candidates to be displayed, show candidates on next page." Loading Loading @@ -811,6 +830,15 @@ N is the argument passed to `self-insert-command'." (zero-input-hide-candidate-list) (zero-input-leave-preedit-state))) (defun zero-input-focus-changed () "A callback function used in `after-focus-change-function'." (when (eq zero-input-state zero-input--state-im-preediting) (let ((state (frame-focus-state))) (cond ((null state) (zero-input-focus-out)) ((eq state t) (zero-input-focus-in)) (t nil))))) (defun zero-input-buffer-list-changed () "A hook function, run when buffer list has changed. This includes user has switched buffer." (if (eq (car (buffer-list)) zero-input-buffer) Loading @@ -820,7 +848,14 @@ N is the argument passed to `self-insert-command'." ;; minor mode ;;============ (defvar zero-input-mode-map (defun zero-input-keyboard-quit () "Handle `keyboard-quit' when `zero-input-mode' is on." (interactive) (when (and (boundp 'zero-input-mode) zero-input-mode) (zero-input-reset)) (keyboard-quit)) (defvar zero-input-mode-map-init (let ((map (make-sparse-keymap))) ;; build zero-input-prefix-map (defvar zero-input-prefix-map (define-prefix-command 'zero-input-prefix-map)) Loading @@ -834,13 +869,19 @@ N is the argument passed to `self-insert-command'." ;; other keybindings (define-key map [remap self-insert-command] 'zero-input-self-insert-command) (define-key map [remap keyboard-quit] 'zero-input-keyboard-quit) map) "Initial keymap for `zero-input-mode'.") (defvar zero-input-mode-map zero-input-mode-map-init "Keymap for `zero-input-mode'.") (defun zero-input-enable-preediting-map () "Enable preediting keymap in `zero-input-mode-map'." (zero-input-debug "zero-input-enable-preediting-map\n") (define-key zero-input-mode-map (kbd "<backspace>") 'zero-input-backspace) (define-key zero-input-mode-map (kbd "DEL") 'zero-input-backspace) (define-key zero-input-mode-map (kbd "RET") 'zero-input-return) (define-key zero-input-mode-map (kbd "<escape>") 'zero-input-reset)) Loading @@ -848,6 +889,7 @@ N is the argument passed to `self-insert-command'." "Disable preediting keymap in `zero-input-mode-map'." (zero-input-debug "zero-input-disable-preediting-map\n") (define-key zero-input-mode-map (kbd "<backspace>") nil) (define-key zero-input-mode-map (kbd "DEL") nil) (define-key zero-input-mode-map (kbd "RET") nil) (define-key zero-input-mode-map (kbd "<escape>") nil)) Loading @@ -860,23 +902,30 @@ Otherwise, show Zero." ;;;###autoload (define-minor-mode zero-input-mode "a Chinese input method framework written as an emacs minor mode. "A Chinese input method framework written as an Emacs minor mode. \\{zero-input-mode-map}" nil (:eval (zero-input-modeline-string)) zero-input-mode-map :init-value nil :lighter (:eval (zero-input-modeline-string)) :keymap zero-input-mode-map ;; body, it's run when mode is activated or deactivated. (if zero-input-mode (progn ;; local variables and variable init (make-local-variable 'zero-input-candidates-per-page) (make-local-variable 'zero-input-full-width-mode) (zero-input-reset) (zero-input-set-im zero-input-im) ;; hooks (if (boundp 'after-focus-change-function) ; emacs 27.1 (add-function :after (local 'after-focus-change-function) #'zero-input-focus-changed) (add-hook 'focus-in-hook 'zero-input-focus-in) (add-hook 'focus-out-hook 'zero-input-focus-out) (add-hook 'focus-out-hook 'zero-input-focus-out)) (setq zero-input-buffer (current-buffer)) (add-hook 'post-self-insert-hook #'zero-input-post-self-insert-command nil t) (add-hook 'buffer-list-update-hook 'zero-input-buffer-list-changed)) (zero-input-reset))) (defun zero-input-post-self-insert-command (&optional ch) "Run after a regular `self-insert-command' is run by zero-input. Loading Loading @@ -914,7 +963,7 @@ After registration, you can use `zero-input-set-default-im' and IM-NAME should be a symbol. IM-FUNCTIONS-ALIST should be a list of form '((:virtual-function-name . implementation-function-name)) \\='((:virtual-function-name . implementation-function-name)) virtual functions corresponding variable =========================================================================== Loading Loading @@ -1614,6 +1663,152 @@ DIGIT 0 means delete 10th candidate." (provide 'zero-input-pinyin) ;; body of zero-input-panel-minibuffer.el ;; utils (defun zero-input-panel-minibuffer--zip-pair1 (lst1 lst2 result) "Zip two lists LST1 and LST2, return a list of pairs from both lists. RESULT is the accumulating list." (if (or (null lst1) (null lst2)) (nreverse result) (zero-input-panel-minibuffer--zip-pair1 (cdr lst1) (cdr lst2) (cons (cons (car lst1) (car lst2)) result)))) (defun zero-input-panel-minibuffer--zip-pair (lst1 lst2) "Zip two lists LST1 and LST2, return a list of pairs from both lists." (zero-input-panel-minibuffer--zip-pair1 lst1 lst2 nil)) ;; main logic (defvar zero-input-panel-minibuffer-last-candidates nil "Store last candidates string shown in minibuffer.") (defvar zero-input-panel-minibuffer-original-resize-mini-windows resize-mini-windows "Store the original value of `resize-mini-windows'. minibuffer panel works best when minibuffer height is 2 or greater. minibuffer height will be auto adjusted when `zero-input-mode' is on and auto restored when `zero-input-mode' is off.") (defun zero-input-panel-minibuffer-make-string (candidates) "Create CANDIDATES string for use with minibuffer panel." (mapconcat 'identity (mapcar (lambda (pair) (concat (int-to-string (% (car pair) 10)) "." (cdr pair))) (zero-input-panel-minibuffer--zip-pair (cl-loop for i from 1 to 10 collect i) candidates)) " ")) (ert-deftest zero-input-panel-minibuffer-make-string () (should (equal (zero-input-panel-minibuffer--zip-pair '(1 2 3 4 5 6) '("a" "b" "c")) '((1 . "a") (2 . "b") (3 . "c")))) (should (equal (zero-input-panel-minibuffer--zip-pair '(1 2 3 4 5 6) '()) nil)) (should (equal (zero-input-panel-minibuffer-make-string '("a" "b" "c")) "1.a 2.b 3.c")) (should (equal (zero-input-panel-minibuffer-make-string '("a" "b" "c" "d")) "1.a 2.b 3.c 4.d"))) (defun zero-input-panel-minibuffer-show-candidates (preedit-str _candidate-count candidates hints) "Show CANDIDATES using minibuffer package. Argument PREEDIT-STR user typed characters. Argument CANDIDATE-COUNT how many candidates to show." (interactive) ;; (zero-input-debug "candidates: %s\n" candidates) (let ((has-next-page (caadr (assoc "has_next_page" hints))) (has-previous-page (caadr (assoc "has_previous_page" hints))) (page-number (caadr (assoc "page_number" hints)))) (let ((candidate-str (zero-input-panel-minibuffer-make-string candidates)) (pagination-str (concat (if has-previous-page "<" " ") " " (int-to-string page-number) " " (if has-next-page ">" " ")))) (let ((str (concat preedit-str "\n" candidate-str " " pagination-str))) (setq zero-input-panel-minibuffer-last-candidates str) (message "%s" str)))) :ignore) (defun zero-input-panel-minibuffer-move (_x _y) "Move panel to (X, Y), based on origin at top left corner." (interactive) ;; move is not needed for minibuffer panel. :ignore) (defun zero-input-panel-minibuffer-show () "Show minibuffer panel." (interactive) (when zero-input-panel-minibuffer-last-candidates (message "%s" zero-input-panel-minibuffer-last-candidates)) :ignore) (defun zero-input-panel-minibuffer-hide () "Hide minibuffer panel." (interactive) (message "%s" "") :ignore) (defun zero-input-panel-minibuffer-quit () "Quit minibuffer panel dbus service." (interactive) (setq resize-mini-windows zero-input-panel-minibuffer-original-resize-mini-windows) (dbus-unregister-service :session zero-input-panel-dbus-service-known-name) (setq zero-input-panel-is-ephemeral nil) :ignore) (defun zero-input-panel-minibuffer-hook () "Hook function to run when activate or deactivate `zero-input-mode'." (if zero-input-mode ;; activate zero-input-mode (progn (let ((w (minibuffer-window))) (when (< (window-size w) 2) (setq zero-input-panel-minibuffer-original-resize-mini-windows resize-mini-windows) (setq resize-mini-windows nil) (window-resize w 1)))) ;; deactivate zero-input-mode (setq resize-mini-windows zero-input-panel-minibuffer-original-resize-mini-windows))) (defun zero-input-panel-minibuffer-init () "Init minibuffer based dbus panel service." (interactive) (let ((service-name zero-input-panel-dbus-service-known-name)) (let ((res (dbus-register-service :session service-name :do-not-queue))) (when (eq res :exists) ;; replace existing panel service with minibuffer based service. (zero-input-panel-quit) ;; async dbus call will return before server handle it. (sleep-for 0.1) (setq res (dbus-register-service :session service-name :do-not-queue))) (if (not (member res '(:primary-owner :already-owner))) (error "Register dbus service failed: %s" res)) (dolist (method (list (cons "ShowCandidates" #'zero-input-panel-minibuffer-show-candidates) (cons "Move" #'zero-input-panel-minibuffer-move) (cons "Show" #'zero-input-panel-minibuffer-show) (cons "Hide" #'zero-input-panel-minibuffer-hide) (cons "Quit" #'zero-input-panel-minibuffer-quit))) (dbus-register-method :session service-name "/com/emacsos/zero/Panel1" "com.emacsos.zero.Panel1.PanelInterface" (car method) (cdr method))) (setq zero-input-panel-is-ephemeral t) (add-hook 'zero-input-mode-hook 'zero-input-panel-minibuffer-hook)))) (provide 'zero-input-panel-minibuffer) (provide 'zero-input) Loading Loading
README +9 −1 Original line number Diff line number Diff line * COMMENT -*- mode: org -*- #+Date: 2019-09-01 Time-stamp: <2024-05-23> Time-stamp: <2024-05-26> * zero-el Loading @@ -13,6 +13,14 @@ check [[https://gitlab.emacsos.com/sylecn/zero-el/blob/master/README][README in * introduce to zero-el https://blog.emacsos.com/zero-el.html * optional packages - zero-input-panel-posframe an optional zero-input panel implemented using elisp and posframe package. It requires posframe to work. To check difference between zero-input panels, see README in master branch on "Which panel should I use". * License zero-el is under Apache License 2.0 Loading
zero-input.el +229 −34 Original line number Diff line number Diff line Loading @@ -12,9 +12,9 @@ ;; See the License for the specific language governing permissions and ;; limitations under the License. ;; Version: 2.8.0 ;; Version: 2.10.1 ;; URL: https://gitlab.emacsos.com/sylecn/zero-el ;; Package-Requires: ((emacs "24.3") (s "1.2.0")) ;; Package-Requires: ((emacs "24.4") (s "1.2.0")) ;;; Commentary: Loading Loading @@ -80,6 +80,8 @@ EVENT and ERROR are error-handler arguments." (add-hook 'dbus-event-error-functions 'zero-input-panel-error-handler) (defvar zero-input-panel-dbus-service-known-name "com.emacsos.zero.Panel1") (defun zero-input-panel-async-call (method _handler &rest args) "Call METHOD on zero-input-panel service asynchronously. Loading @@ -87,7 +89,7 @@ This is a wrapper around `dbus-call-method-asynchronously'. ARGS optional extra args to pass to the wrapped function." (apply 'dbus-call-method-asynchronously :session "com.emacsos.zero.Panel1" ; well known name zero-input-panel-dbus-service-known-name ; well known name "/com/emacsos/zero/Panel1" ; object path "com.emacsos.zero.Panel1.PanelInterface" ; interface name method nil :timeout 500 args)) Loading @@ -99,14 +101,14 @@ ARGS optional extra args to pass to the wrapped function." (defun zero-input-alist-to-asv (hints) "Convert Lisp alist to dbus a{sv} data structure. HINTS should be an alist of form '((k1 [v1type] v1) (k2 [v2type] v2)). HINTS should be an alist of form \\='((k1 [v1type] v1) (k2 [v2type] v2)). For example, \(zero-input-alist-to-asv '((\"name\" \"foo\") \\='((\"name\" \"foo\") (\"timeout\" :int32 10))) => '(:array \\='(:array (:dict-entry \"name\" (:variant \"foo\")) (:dict-entry \"timeout\" (:variant :int32 10)))" (if (null hints) Loading Loading @@ -251,7 +253,18 @@ 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.8.0") (setq zero-input-version "2.10.1") (defvar zero-input-panel-is-ephemeral nil "Stores whether the panel service is ephemeral or not. When a zero-input panel service can not persist its content on key strokes, it should set this to t, so zero-input-framework will call `zero-input-panel-show-candidates' on every keystroke to refresh the candidates list even when no change is needed. A zero-input panel service should revert this variable to nil on exit.") ;; FSM state (defconst zero-input--state-im-off 'IM-OFF) Loading Loading @@ -370,7 +383,7 @@ Change will be effective only in new `zero-input-mode' buffer." (defvar-local zero-input-initial-fetch-size 21 "How many candidates to fetch for the first call to GetCandidates. It's best set to (1+ (* zero-input-candidates-per-page N)) where It\\='s best set to \\=(1+ (* zero-input-candidates-per-page N)) where N is number of pages you want to fetch in initial fetch.") ;; zero-input-fetch-size is reset to 0 when preedit-str changes. ;; zero-input-fetch-size is set to fetch-size in build-candidates-async Loading Loading @@ -469,7 +482,7 @@ STRING and OBJECTS are passed to `format'" (defun zero-input-candidates-on-page (candidates) "Return candidates on current page for given CANDIDATES list." (cl-flet ((take (n lst) (cl-flet ((my-take (n lst) "take the first n element from lst. if there is not enough elements, return lst as it is." (cl-loop Loading @@ -484,7 +497,7 @@ enough elements, return lst as it is." for n* = n then (1- n*) until (or (zerop n*) (null lst*)) finally (return lst*)))) (take zero-input-candidates-per-page (my-take zero-input-candidates-per-page (drop (* zero-input-candidates-per-page zero-input-current-page) candidates)))) (defun zero-input-show-candidates (&optional candidates) Loading Loading @@ -618,17 +631,23 @@ Return CH's Chinese punctuation if CH is converted. Return nil otherwise." (defun zero-input-page-up () "If not at first page, show candidates on previous page." (interactive) (when (> zero-input-current-page 0) (if (> zero-input-current-page 0) (progn (setq zero-input-current-page (1- zero-input-current-page)) (zero-input-show-candidates))) (zero-input-show-candidates)) (when zero-input-panel-is-ephemeral (zero-input-show-candidates)))) (defun zero-input-just-page-down () "Just page down using existing candidates." (let ((len (length zero-input-candidates))) (when (> len (* zero-input-candidates-per-page (1+ zero-input-current-page))) (if (> len (* zero-input-candidates-per-page (1+ zero-input-current-page))) (progn (setq zero-input-current-page (1+ zero-input-current-page)) (zero-input-debug "showing candidates on page %s\n" zero-input-current-page) (zero-input-show-candidates)))) (zero-input-show-candidates)) (when zero-input-panel-is-ephemeral (zero-input-show-candidates))))) (defun zero-input-page-down () "If there is still candidates to be displayed, show candidates on next page." Loading Loading @@ -811,6 +830,15 @@ N is the argument passed to `self-insert-command'." (zero-input-hide-candidate-list) (zero-input-leave-preedit-state))) (defun zero-input-focus-changed () "A callback function used in `after-focus-change-function'." (when (eq zero-input-state zero-input--state-im-preediting) (let ((state (frame-focus-state))) (cond ((null state) (zero-input-focus-out)) ((eq state t) (zero-input-focus-in)) (t nil))))) (defun zero-input-buffer-list-changed () "A hook function, run when buffer list has changed. This includes user has switched buffer." (if (eq (car (buffer-list)) zero-input-buffer) Loading @@ -820,7 +848,14 @@ N is the argument passed to `self-insert-command'." ;; minor mode ;;============ (defvar zero-input-mode-map (defun zero-input-keyboard-quit () "Handle `keyboard-quit' when `zero-input-mode' is on." (interactive) (when (and (boundp 'zero-input-mode) zero-input-mode) (zero-input-reset)) (keyboard-quit)) (defvar zero-input-mode-map-init (let ((map (make-sparse-keymap))) ;; build zero-input-prefix-map (defvar zero-input-prefix-map (define-prefix-command 'zero-input-prefix-map)) Loading @@ -834,13 +869,19 @@ N is the argument passed to `self-insert-command'." ;; other keybindings (define-key map [remap self-insert-command] 'zero-input-self-insert-command) (define-key map [remap keyboard-quit] 'zero-input-keyboard-quit) map) "Initial keymap for `zero-input-mode'.") (defvar zero-input-mode-map zero-input-mode-map-init "Keymap for `zero-input-mode'.") (defun zero-input-enable-preediting-map () "Enable preediting keymap in `zero-input-mode-map'." (zero-input-debug "zero-input-enable-preediting-map\n") (define-key zero-input-mode-map (kbd "<backspace>") 'zero-input-backspace) (define-key zero-input-mode-map (kbd "DEL") 'zero-input-backspace) (define-key zero-input-mode-map (kbd "RET") 'zero-input-return) (define-key zero-input-mode-map (kbd "<escape>") 'zero-input-reset)) Loading @@ -848,6 +889,7 @@ N is the argument passed to `self-insert-command'." "Disable preediting keymap in `zero-input-mode-map'." (zero-input-debug "zero-input-disable-preediting-map\n") (define-key zero-input-mode-map (kbd "<backspace>") nil) (define-key zero-input-mode-map (kbd "DEL") nil) (define-key zero-input-mode-map (kbd "RET") nil) (define-key zero-input-mode-map (kbd "<escape>") nil)) Loading @@ -860,23 +902,30 @@ Otherwise, show Zero." ;;;###autoload (define-minor-mode zero-input-mode "a Chinese input method framework written as an emacs minor mode. "A Chinese input method framework written as an Emacs minor mode. \\{zero-input-mode-map}" nil (:eval (zero-input-modeline-string)) zero-input-mode-map :init-value nil :lighter (:eval (zero-input-modeline-string)) :keymap zero-input-mode-map ;; body, it's run when mode is activated or deactivated. (if zero-input-mode (progn ;; local variables and variable init (make-local-variable 'zero-input-candidates-per-page) (make-local-variable 'zero-input-full-width-mode) (zero-input-reset) (zero-input-set-im zero-input-im) ;; hooks (if (boundp 'after-focus-change-function) ; emacs 27.1 (add-function :after (local 'after-focus-change-function) #'zero-input-focus-changed) (add-hook 'focus-in-hook 'zero-input-focus-in) (add-hook 'focus-out-hook 'zero-input-focus-out) (add-hook 'focus-out-hook 'zero-input-focus-out)) (setq zero-input-buffer (current-buffer)) (add-hook 'post-self-insert-hook #'zero-input-post-self-insert-command nil t) (add-hook 'buffer-list-update-hook 'zero-input-buffer-list-changed)) (zero-input-reset))) (defun zero-input-post-self-insert-command (&optional ch) "Run after a regular `self-insert-command' is run by zero-input. Loading Loading @@ -914,7 +963,7 @@ After registration, you can use `zero-input-set-default-im' and IM-NAME should be a symbol. IM-FUNCTIONS-ALIST should be a list of form '((:virtual-function-name . implementation-function-name)) \\='((:virtual-function-name . implementation-function-name)) virtual functions corresponding variable =========================================================================== Loading Loading @@ -1614,6 +1663,152 @@ DIGIT 0 means delete 10th candidate." (provide 'zero-input-pinyin) ;; body of zero-input-panel-minibuffer.el ;; utils (defun zero-input-panel-minibuffer--zip-pair1 (lst1 lst2 result) "Zip two lists LST1 and LST2, return a list of pairs from both lists. RESULT is the accumulating list." (if (or (null lst1) (null lst2)) (nreverse result) (zero-input-panel-minibuffer--zip-pair1 (cdr lst1) (cdr lst2) (cons (cons (car lst1) (car lst2)) result)))) (defun zero-input-panel-minibuffer--zip-pair (lst1 lst2) "Zip two lists LST1 and LST2, return a list of pairs from both lists." (zero-input-panel-minibuffer--zip-pair1 lst1 lst2 nil)) ;; main logic (defvar zero-input-panel-minibuffer-last-candidates nil "Store last candidates string shown in minibuffer.") (defvar zero-input-panel-minibuffer-original-resize-mini-windows resize-mini-windows "Store the original value of `resize-mini-windows'. minibuffer panel works best when minibuffer height is 2 or greater. minibuffer height will be auto adjusted when `zero-input-mode' is on and auto restored when `zero-input-mode' is off.") (defun zero-input-panel-minibuffer-make-string (candidates) "Create CANDIDATES string for use with minibuffer panel." (mapconcat 'identity (mapcar (lambda (pair) (concat (int-to-string (% (car pair) 10)) "." (cdr pair))) (zero-input-panel-minibuffer--zip-pair (cl-loop for i from 1 to 10 collect i) candidates)) " ")) (ert-deftest zero-input-panel-minibuffer-make-string () (should (equal (zero-input-panel-minibuffer--zip-pair '(1 2 3 4 5 6) '("a" "b" "c")) '((1 . "a") (2 . "b") (3 . "c")))) (should (equal (zero-input-panel-minibuffer--zip-pair '(1 2 3 4 5 6) '()) nil)) (should (equal (zero-input-panel-minibuffer-make-string '("a" "b" "c")) "1.a 2.b 3.c")) (should (equal (zero-input-panel-minibuffer-make-string '("a" "b" "c" "d")) "1.a 2.b 3.c 4.d"))) (defun zero-input-panel-minibuffer-show-candidates (preedit-str _candidate-count candidates hints) "Show CANDIDATES using minibuffer package. Argument PREEDIT-STR user typed characters. Argument CANDIDATE-COUNT how many candidates to show." (interactive) ;; (zero-input-debug "candidates: %s\n" candidates) (let ((has-next-page (caadr (assoc "has_next_page" hints))) (has-previous-page (caadr (assoc "has_previous_page" hints))) (page-number (caadr (assoc "page_number" hints)))) (let ((candidate-str (zero-input-panel-minibuffer-make-string candidates)) (pagination-str (concat (if has-previous-page "<" " ") " " (int-to-string page-number) " " (if has-next-page ">" " ")))) (let ((str (concat preedit-str "\n" candidate-str " " pagination-str))) (setq zero-input-panel-minibuffer-last-candidates str) (message "%s" str)))) :ignore) (defun zero-input-panel-minibuffer-move (_x _y) "Move panel to (X, Y), based on origin at top left corner." (interactive) ;; move is not needed for minibuffer panel. :ignore) (defun zero-input-panel-minibuffer-show () "Show minibuffer panel." (interactive) (when zero-input-panel-minibuffer-last-candidates (message "%s" zero-input-panel-minibuffer-last-candidates)) :ignore) (defun zero-input-panel-minibuffer-hide () "Hide minibuffer panel." (interactive) (message "%s" "") :ignore) (defun zero-input-panel-minibuffer-quit () "Quit minibuffer panel dbus service." (interactive) (setq resize-mini-windows zero-input-panel-minibuffer-original-resize-mini-windows) (dbus-unregister-service :session zero-input-panel-dbus-service-known-name) (setq zero-input-panel-is-ephemeral nil) :ignore) (defun zero-input-panel-minibuffer-hook () "Hook function to run when activate or deactivate `zero-input-mode'." (if zero-input-mode ;; activate zero-input-mode (progn (let ((w (minibuffer-window))) (when (< (window-size w) 2) (setq zero-input-panel-minibuffer-original-resize-mini-windows resize-mini-windows) (setq resize-mini-windows nil) (window-resize w 1)))) ;; deactivate zero-input-mode (setq resize-mini-windows zero-input-panel-minibuffer-original-resize-mini-windows))) (defun zero-input-panel-minibuffer-init () "Init minibuffer based dbus panel service." (interactive) (let ((service-name zero-input-panel-dbus-service-known-name)) (let ((res (dbus-register-service :session service-name :do-not-queue))) (when (eq res :exists) ;; replace existing panel service with minibuffer based service. (zero-input-panel-quit) ;; async dbus call will return before server handle it. (sleep-for 0.1) (setq res (dbus-register-service :session service-name :do-not-queue))) (if (not (member res '(:primary-owner :already-owner))) (error "Register dbus service failed: %s" res)) (dolist (method (list (cons "ShowCandidates" #'zero-input-panel-minibuffer-show-candidates) (cons "Move" #'zero-input-panel-minibuffer-move) (cons "Show" #'zero-input-panel-minibuffer-show) (cons "Hide" #'zero-input-panel-minibuffer-hide) (cons "Quit" #'zero-input-panel-minibuffer-quit))) (dbus-register-method :session service-name "/com/emacsos/zero/Panel1" "com.emacsos.zero.Panel1.PanelInterface" (car method) (cdr method))) (setq zero-input-panel-is-ephemeral t) (add-hook 'zero-input-mode-hook 'zero-input-panel-minibuffer-hook)))) (provide 'zero-input-panel-minibuffer) (provide 'zero-input) Loading