Add defformat macro

This commit is contained in:
Jessie Hildebrandt 2023-11-20 17:40:27 -05:00
parent 1f0bf3377e
commit 8414402234
3 changed files with 159 additions and 59 deletions

View File

@ -59,21 +59,23 @@ mood-line uses a modular segment format, and it is easy to reconfigure:
;; Custom format:
;; * init.el : ELisp Top 4:32 | ! Issues: 2
(setq mood-line-format
'(;; Left side
(mood-line-defformat
;; Left side
(" "
(mood-line-segment-buffer-status) " "
(mood-line-segment-buffer-name) " : "
((mood-line-segment-buffer-status) . " ")
((mood-line-segment-buffer-name) . " : ")
(mood-line-segment-major-mode))
;; Right side
((mood-line-segment-scroll) " "
(mood-line-segment-cursor-position) " "
(when (mood-line-segment-checker) "|") " "
(mood-line-segment-checker) " "
(((mood-line-segment-scroll) . " ")
((mood-line-segment-cursor-position) . " ")
((when (mood-line-segment-checker) "|") . " ")
((mood-line-segment-checker) . " ")
" ")))
```
More information on the format specification is available in the documentation.
(`M-x describe-variable mood-line-format`)
More information on the format specification is available in the documentation: \
`M-x describe-variable mood-line-format` \
`M-x describe-function mood-line-defformat`
### Glyphs

View File

@ -75,6 +75,45 @@
(declare-function string-blank-p "subr-x")
;; -------------------------------------------------------------------------- ;;
;;
;; Macros
;;
;; -------------------------------------------------------------------------- ;;
(defmacro mood-line--deflazy (name)
"Define dummy function NAME to `require' its module and call actual function.
If NAME is already bound, this does nothing."
(when-let ((not-bound (not (fboundp name)))
(module (intern (car (split-string (symbol-name name) "--")))))
`(defun ,name (&rest args)
"Not yet loaded."
(fmakunbound (quote ,name))
(require (quote ,module))
(apply (function ,name) args))))
(defmacro mood-line-defformat (left &optional right)
"Format LEFT and RIGHT segment lists as a `mood-line-format' sequence.
A segment may be a string, a cons cell of the form (FUNCTION . SEPARATOR),
or any expression that evaluates to a string or nil.
Strings will be collected into the format sequence unaltered.
Cons cells of the form (FUNCTION . SEPARATOR) will expand into the format
sequence as FUNCTION, followed by SEPARATOR.
All other expressions will expand into the format sequence unaltered,
followed by an empty string. This prevents accidental elision of the
following segment should the expression evaluate to nil."
`(quote ,(mapcar
(lambda (segments)
(cl-loop for seg in segments
if (cdr-safe seg) append `(,(car seg) ,(cdr seg))
else if (stringp seg) collect seg
else append `(,seg "")))
`(,left ,right))))
;; -------------------------------------------------------------------------- ;;
;;
;; Constants
@ -145,42 +184,48 @@
"Set of Unicode glyphs for use with mood-line.")
(defconst mood-line-format-default
'((" "
(mood-line-segment-modal) " "
(mood-line-segment-buffer-status) " "
(mood-line-segment-buffer-name) " "
(mood-line-segment-anzu) " "
(mood-line-segment-multiple-cursors) " "
(mood-line-segment-cursor-position) " "
(mood-line-defformat
;; Left
(" "
((mood-line-segment-modal) . " ")
((mood-line-segment-buffer-status) . " ")
((mood-line-segment-buffer-name) . " ")
((mood-line-segment-anzu) . " ")
((mood-line-segment-multiple-cursors) . " ")
((mood-line-segment-cursor-position) . " ")
(mood-line-segment-scroll))
((mood-line-segment-vc) " "
(mood-line-segment-major-mode) " "
(mood-line-segment-misc-info) " "
(mood-line-segment-checker) " "
(mood-line-segment-process) " "
;; Right
(((mood-line-segment-vc) . " ")
((mood-line-segment-major-mode) . " ")
((mood-line-segment-misc-info) . " ")
((mood-line-segment-checker) . " ")
((mood-line-segment-process) . " ")
" "))
"Default format for mood-line.")
(defconst mood-line-format-default-extended
'((" "
(mood-line-segment-modal) " "
(mood-line-segment-buffer-status) " "
(mood-line-segment-buffer-name) " "
(mood-line-segment-anzu) " "
(mood-line-segment-multiple-cursors) " "
(mood-line-segment-cursor-position) ""
(mood-line-defformat
;; Left
(" "
((mood-line-segment-modal) . " ")
((mood-line-segment-buffer-status) . " ")
((mood-line-segment-buffer-name) . " ")
((mood-line-segment-anzu) . " ")
((mood-line-segment-multiple-cursors) . " ")
(mood-line-segment-cursor-position)
#(":" 0 1 (face mood-line-unimportant))
(mood-line-segment-cursor-point) " "
(mood-line-segment-region) " "
((mood-line-segment-cursor-point) . " ")
((mood-line-segment-region) . " ")
(mood-line-segment-scroll))
((mood-line-segment-indentation) " "
(mood-line-segment-eol) " "
(mood-line-segment-encoding) " "
(mood-line-segment-vc) " "
(mood-line-segment-major-mode) " "
(mood-line-segment-misc-info) " "
(mood-line-segment-checker) " "
(mood-line-segment-process) " "
;; Right
(((mood-line-segment-indentation) . " ")
((mood-line-segment-eol) . " ")
((mood-line-segment-encoding) . " ")
((mood-line-segment-vc) . " ")
((mood-line-segment-major-mode) . " ")
((mood-line-segment-misc-info) . " ")
((mood-line-segment-checker) . " ")
((mood-line-segment-process) . " ")
" "))
"Extended default format for mood-line showcasing all included segments.")
@ -265,7 +310,9 @@ When a segment evaluates to nil, the following segment will be skipped and not
processed or displayed. This behavior may be used to, e.g., conditionally
display separating whitespace after a segment.
Examples: `mood-line-format-default' and `mood-line-format-default-extended'"
Examples: `mood-line-format-default' and `mood-line-format-default-extended'
See `mood-line-defformat' for a helpful formatting macro."
:group 'mood-line
:type '(list :tag "Mode line segments"
(repeat :tag "Left side" sexp)
@ -386,17 +433,6 @@ described in the documentation for `mood-line-format', which see."
;;
;; -------------------------------------------------------------------------- ;;
(defmacro mood-line--deflazy (name)
"Define dummy function NAME to `require' its module and call actual function.
If NAME is already bound, this does nothing."
(when-let ((not-bound (not (fboundp name)))
(module (intern (car (split-string (symbol-name name) "--")))))
`(defun ,name (&rest args)
"Not yet loaded."
(fmakunbound (quote ,name))
(require (quote ,module))
(apply (function ,name) args))))
;; ---------------------------------- ;;
;; Modal editing
;; ---------------------------------- ;;

View File

@ -7,6 +7,68 @@
;;; Code:
;; -------------------------------------------------------------------------- ;;
;;
;; Macros
;;
;; -------------------------------------------------------------------------- ;;
;; ---------------------------------- ;;
;; mood-line-defformat
;; ---------------------------------- ;;
(ert-deftest -defformat/right-nil ()
"The format sequence should expand if the right segment list is not provided."
(should (equal (mood-line-defformat
;; Left
("XYZ")
;; Right
)
(list
;; Left
'("XYZ")
;; Right
nil))))
(ert-deftest -defformat/left-right ()
"The expanded sequence should should include left and right segments lists."
(should (equal (mood-line-defformat
;; Left
("ABC")
;; Right
("XYZ"))
(list
;; Left
'("ABC")
;; Right
'("XYZ")))))
(ert-deftest -defformat/cons-cells ()
"Cons cell segments should expand into their `car' and `cdr' values."
(should (equal (mood-line-defformat
;; Left
("ABC" ("ABC" . "XYZ") "XYZ")
;; Right
("..." ((some-fn) . " ") "..."))
(list
;; Left
'("ABC" "ABC" "XYZ" "XYZ")
;; Right
'("..." (some-fn) " " "...")))))
(ert-deftest -defformat/exp-separators ()
"Non-string, non-cons expressions should expand followed by a blank string."
(should (equal (mood-line-defformat
;; Left
("ABC" ("ABC" . "XYZ") some-exp "XYZ" (some-fn))
;; Right
("..." ((some-fn) . " ") (another-fn) "..."))
(list
;; Left
'("ABC" "ABC" "XYZ" some-exp "" "XYZ" (some-fn) "")
;; Right
'("..." (some-fn) " " (another-fn) "" "...")))))
;; -------------------------------------------------------------------------- ;;
;;
;; Helper functions