Skip to content

Instantly share code, notes, and snippets.

@rougier
Last active September 19, 2022 11:26
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rougier/f9f8515c8fe91d41a049d50c448408dd to your computer and use it in GitHub Desktop.
Save rougier/f9f8515c8fe91d41a049d50c448408dd to your computer and use it in GitHub Desktop.
Emacs proof of concept: emulating minibuffer in the mode line
(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))))
@rougier
Copy link
Author

rougier commented Sep 18, 2022

Screenshot 2022-09-18 at 11 31 22

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment