Last active
September 19, 2022 11:26
-
-
Save rougier/f9f8515c8fe91d41a049d50c448408dd to your computer and use it in GitHub Desktop.
Emacs proof of concept: emulating minibuffer in the mode line
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(defface quick-command-face | |
`((t :foreground ,(face-foreground 'default) | |
:background ,(face-background 'highlight nil t) | |
:height ,(face-attribute 'default :height) | |
:box '(:line-width (1 . 1) | |
:color ,(face-foreground 'default) | |
:style none))) | |
"Face for quick command") | |
(defface quick-command-prompt-face | |
`((t :foreground ,(face-background 'default) | |
:background ,(face-foreground 'default) | |
:weight ,(face-attribute 'bold :weight nil 'default) | |
:box (:line-width (1 . 1) | |
:color ,(face-foreground 'default) | |
:style none))) | |
"Face for prompt") | |
(defface quick-command-region-face | |
`((t :foreground ,(face-foreground 'region nil t) | |
:background ,(face-background 'region nil t))) | |
"Face for active region") | |
(defface quick-command-cursor-face | |
`((t :foreground ,(face-background 'default) | |
;; :background "#d0d0d0" | |
:background ,(face-foreground 'default) | |
:box (:line-width (1 . 1) | |
:color ,(face-foreground 'default) | |
:style none))) | |
"Face for cursor") | |
(defun quick-command--update (current-buffer command-buffer) | |
"Update mode-line with current command" | |
(with-current-buffer command-buffer | |
(let* ((text (concat (buffer-substring (point-min) (point-max)) " ")) | |
(point (point)) | |
(region-beg (if (use-region-p) (- (region-beginning) 1))) | |
(region-end (if (use-region-p) (region-end)))) | |
(add-face-text-property (- point 1) point 'quick-command-cursor-face t text) | |
(when (and region-beg region-end) | |
(add-face-text-property region-beg region-end 'quick-command-region-face t text)) | |
(with-current-buffer current-buffer | |
(setq-local mode-line-format (propertize text 'display '(raise 0.15)))) | |
(force-mode-line-update)))) | |
(defun quick-command (&optional prompt) | |
"Read user-input from the mode-line using the given PROMPT." | |
(interactive) | |
(let* ((saved-mode-line mode-line-format) | |
(cookie (face-remap-add-relative 'mode-line | |
:foreground (face-attribute 'quick-command-face :foreground nil 'default) | |
:background (face-attribute 'quick-command-face :background nil 'default) | |
:height (face-attribute 'quick-command-face :height nil 'default) | |
:weight (face-attribute 'quick-command-face :weight nil 'default) | |
:box (face-attribute 'quick-command-face :box nil 'default))) | |
(command nil) | |
(current-buffer (current-buffer)) | |
(current-window (selected-window)) | |
(prompt (format " %s " (or prompt "Quick command"))) | |
(prompt (concat (propertize prompt 'face 'quick-command-prompt-face) | |
" ")) | |
(command-buffer (get-buffer-create " *quick-command*"))) | |
;; To make sure to remove the relative face | |
(unwind-protect | |
;; Catch enter or exit key press | |
(catch 'break | |
(with-current-buffer command-buffer | |
;; Clear previous buffer content | |
(let ((inhibit-read-only t)) | |
(erase-buffer)) | |
;; Insert prompt | |
(insert (concat (propertize prompt 'intangible t | |
'cursor-intangible t | |
'read-only t | |
'front-sticky nil | |
'rear-nonsticky t))) | |
(quick-command--update current-buffer command-buffer) | |
(cursor-intangible-mode t) | |
;; Main loop where we read key sequences until RET or ESC is pressed | |
(while t | |
(let* ((message "[WIP] Echo area could then be used for displaying completions") | |
(key (key-description (read-key-sequence message)))) | |
;; Command enter | |
(when (string= key "RET") | |
(setq command (buffer-substring (+ (length prompt) 1) (point-max))) | |
(if (> (length command) 0) | |
(throw 'break command) | |
(throw 'break nil))) | |
;; Command abort | |
(when (and (string= key "C-g") | |
(with-current-buffer command-buffer | |
(not (region-active-p)))) | |
(setq command nil) | |
(throw 'break nil)) | |
;; Execute key sequence in command buffer | |
(set-window-buffer current-window command-buffer t) | |
(condition-case error | |
(execute-kbd-macro (kbd key)) | |
((beginning-of-buffer end-of-buffer text-read-only))) | |
;; Make sure to not go into prompt area | |
(goto-char (max (+ (length prompt) 1) (point))) | |
(set-window-buffer current-window current-buffer t) | |
;; Update mode line | |
(quick-command--update current-buffer command-buffer))))) | |
;; Command entered or aborted: restore mode line | |
(with-current-buffer current-buffer | |
(setq-local mode-line-format saved-mode-line) | |
(face-remap-remove-relative cookie) | |
(force-mode-line-update)) | |
(kill-buffer command-buffer) | |
(switch-to-buffer current-buffer)))) | |
;; ---------------------------------------------------------------------------- | |
(set-face-attribute 'mode-line nil | |
:box `(:line-width (1 . 1) | |
:color ,(face-background 'default) | |
:style none)) | |
;; (set-face-attribute 'quick-command-face nil | |
;; :background (face-background 'nano-subtle)) | |
;; (set-face-attribute 'quick-command-region-face nil | |
;; :background "#ffffd0") | |
(let ((command (quick-command))) | |
(when command | |
(command-execute (intern command)))) |
Author
rougier
commented
Sep 18, 2022
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment