diff --git a/README.md b/README.md index cea04a0..3f29134 100644 --- a/README.md +++ b/README.md @@ -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-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-defformat + ;; Left side + (" " + ((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) . " ") + " "))) ``` -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 diff --git a/mood-line.el b/mood-line.el index b168b8d..3d943cc 100644 --- a/mood-line.el +++ b/mood-line.el @@ -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,43 +184,49 @@ "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-segment-scroll)) - ((mood-line-segment-vc) " " - (mood-line-segment-major-mode) " " - (mood-line-segment-misc-info) " " - (mood-line-segment-checker) " " - (mood-line-segment-process) " " - " ")) + (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)) + ;; 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) "" - #(":" 0 1 (face mood-line-unimportant)) - (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) " " - " ")) + (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-scroll)) + ;; 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 ;; ---------------------------------- ;; diff --git a/test/mood-line-test.el b/test/mood-line-test.el index 528daf7..f89afb4 100644 --- a/test/mood-line-test.el +++ b/test/mood-line-test.el @@ -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