branch: externals/taxy commit fe190c28cb2a176584e0ddd70bca17a32f52c08c Author: Adam Porter <a...@alphapapa.net> Commit: Adam Porter <a...@alphapapa.net>
Add: (taxy-magit-section-define-column-definer) --- taxy-magit-section.el | 76 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/taxy-magit-section.el b/taxy-magit-section.el index 5a8e48c..85af4d3 100644 --- a/taxy-magit-section.el +++ b/taxy-magit-section.el @@ -168,6 +168,82 @@ Default visibility function for (_ 'show))) (_ nil))) +;;;; Column-based formatting + +;; Column-based, or "table"? + +;; TODO: Probably, this belongs in a separate library, since it's not +;; directly related to using taxy or magit-section. Maybe it could be +;; called something like `flextab' (or, keeping with the theme, +;; `tabley'). + +;;;;; Macros + +(cl-defmacro taxy-magit-section-define-column-definer (prefix &key columns-variable-docstring) + "FIXME: Docstring." + (let* ((definer-name (intern (format "%s-define-column" prefix))) + (definer-docstring (format "Define a column formatting function with NAME. +NAME should be a string. BODY should return a string or nil. In +the BODY, `item' is bound to the item being formatted, and `depth' is +bound to the item's depth in the hierarchy. + +PLIST may be a plist setting the following options: + + `:face' is a face applied to the string. + + `:max-width' defines a customization option for the column's + maximum width with the specified value as its default: an + integer limits the width, while nil does not.")) + (columns-variable-name (intern (format "%s-columns" prefix))) + (columns-variable-docstring (or columns-variable-docstring + (format "Columns defined by `%s'." + definer-name))) + (column-formatters-variable-name (intern (format "%s-column-formatters" prefix))) + (column-formatters-variable-docstring (format "Column formatters defined by `%s'." + definer-name))) + ;; TODO: Add defined columns to customization type for the columns-variable. + `(let ((columns-variable ',columns-variable-name) + (column-formatters-variable ',column-formatters-variable-name)) + (defvar ,columns-variable-name nil + ,columns-variable-docstring) + (defvar ,column-formatters-variable-name nil + ,column-formatters-variable-docstring) + (defmacro ,definer-name (name plist &rest body) + ,definer-docstring + (declare (indent defun)) + (cl-check-type name string) + (pcase-let* ((fn-name (intern (concat ,prefix "-column-format-" (downcase name)))) + ((map (:face face) (:max-width max-width)) plist) + (max-width-variable (intern (concat ,prefix "-" name "-max-width"))) + (max-width-docstring (format "Maximum width of the %s column." name))) + `(progn + ,(when (plist-member plist :max-width) + `(defcustom ,max-width-variable + ,max-width + ,max-width-docstring + :type '(choice (integer :tag "Maximum width") + (const :tag "Unlimited width" nil)))) + (defun ,fn-name (item depth) + (if-let ((string (progn ,@body))) + (progn + ,(when max-width + `(when ,max-width-variable + (setf string (truncate-string-to-width string ,max-width-variable nil nil "…")))) + ,(when face + ;; Faces are not defined until load time, while this checks type at expansion + ;; time, so we can only test that the argument is a symbol, not a face. + (cl-check-type face symbol ":face must be a face symbol") + `(setf string (propertize string 'face ',face))) + string) + "")) + (setf (map-elt ,column-formatters-variable ,name) #',fn-name) + (unless (member ,name (get ',columns-variable 'standard-value)) + (setf (get ',columns-variable 'standard-value) + (append (get ',columns-variable 'standard-value) + (list ,name)))))))))) + +;;;;; Functions + ;; MAYBE: Consider using spaces with `:align-to', rather than formatting strings with indentation, as used by `epkg' ;; (see <https://github.com/emacscollective/epkg/blob/edf8c009066360af61caedf67a2482eaa19481b0/epkg-desc.el#L363>). ;; I'm not sure which would perform better; I guess that with many lines, redisplay might take longer to use the