[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[elpa] master 3db82e5 29/36: Implement named columns
From: |
Oleh Krehel |
Subject: |
[elpa] master 3db82e5 29/36: Implement named columns |
Date: |
Sat, 22 Jul 2017 11:22:25 -0400 (EDT) |
branch: master
commit 3db82e5f4340e40143c594192177088fe1a61443
Author: David AMAR <address@hidden>
Commit: Oleh Krehel <address@hidden>
Implement named columns
This is a first rough implementation to gather early reviews
diff with code snippet from #147:
- Dash dependencies removed
- slight refactoring
Add basic column integration test.
Fixes #220
---
hydra-test.el | 72 +++++++++++++++++++++++++++++++++++++
hydra.el | 114 +++++++++++++++++++++++++++++++++++++++++++++++++++++++---
2 files changed, 182 insertions(+), 4 deletions(-)
diff --git a/hydra-test.el b/hydra-test.el
index 9a3e3d7..5181cfb 100644
--- a/hydra-test.el
+++ b/hydra-test.el
@@ -1409,6 +1409,78 @@ t: info-to"
314 315 (face hydra-face-blue)
322 323 (face hydra-face-blue)))))
+;; checked:
+;; basic rendering
+;; column compatibility with ruby style and no colum specified
+;; column declared several time
+;; nil column
+(ert-deftest hydra-column-1 ()
+ (should (equal (eval
+ (cadr
+ (nth 2
+ (nth 3
+ (macroexpand
+ '(defhydra hydra-rectangle (:body-pre
(rectangle-mark-mode 1)
+ :color pink
+ :post
(deactivate-mark))
+ "
+ ^_k_^ ()()
+_h_ _l_ (O)(o)
+ ^_j_^ ( O )
+^^^^ (’’)(’’)
+^^^^
+"
+ ("h" backward-char nil)
+ ("l" forward-char nil)
+ ("k" previous-line nil)
+ ("j" next-line nil)
+ ("Of" 5x5 "outside of table 1")
+ ("e" exchange-point-and-mark "exchange"
:column "firstcol")
+ ("n" copy-rectangle-as-kill "new-copy")
+ ("d" delete-rectangle "delete")
+ ("r" (if (region-active-p)
+ (deactivate-mark)
+ (rectangle-mark-mode 1)) "reset"
:column "secondcol")
+ ("y" yank-rectangle "yank")
+ ("u" undo "undo")
+ ("s" string-rectangle "string")
+ ("p" kill-rectangle "paste")
+ ("o" nil "ok" :column "firstcol")
+ ("Os" 5x5-bol "outside of table 2" :column
nil)
+ ("Ot" 5x5-eol "outside of table 3")))))))
+
+#(" k ()()
+h l (O)(o)
+ j ( O )
+ (’’)(’’)
+
+
+firstcol | secondcol
+----------- | ------------
+e: exchange | r: reset
+n: new-copy | y: yank
+d: delete | u: undo
+o: ok | s: string
+ | p: paste
+[Of]: outside of table 1, [Os]: outside of table 2, [Ot]: outside of table 3."
+2 3 (face hydra-face-pink)
+17 18 (face hydra-face-pink)
+21 22 (face hydra-face-pink)
+38 39 (face hydra-face-pink)
+142 143 (face hydra-face-pink)
+156 157 (face hydra-face-pink)
+170 171 (face hydra-face-pink)
+184 185 (face hydra-face-pink)
+198 199 (face hydra-face-pink)
+212 213 (face hydra-face-pink)
+226 227 (face hydra-face-blue)
+240 241 (face hydra-face-pink)
+268 269 (face hydra-face-pink)
+283 285 (face hydra-face-pink)
+309 311 (face hydra-face-pink)
+335 337 (face hydra-face-pink)))))
+
+
(provide 'hydra-test)
;;; hydra-test.el ends here
diff --git a/hydra.el b/hydra.el
index eaedd6c..992f9db 100644
--- a/hydra.el
+++ b/hydra.el
@@ -408,6 +408,14 @@ one of the properties on the list."
Return DEFAULT if PROP is not in H."
(hydra-plist-get-default (cl-cdddr h) prop default))
+(defun hydra--head-set-property (h prop value)
+ "set a property PROP to the value VALUE in the hydra head H"
+ (cons (car h) (plist-put (cdr h) prop value)))
+
+(defun hydra--head-has-property (h prop)
+ "return non nil if heads H has the property PROP"
+ (plist-member (cdr h) prop))
+
(defun hydra--body-foreign-keys (body)
"Return what BODY does with a non-head binding."
(or
@@ -469,17 +477,19 @@ Return DEFAULT if PROP is not in H."
(defun hydra-key-doc-function-default (key key-width doc doc-width)
"Doc"
- (format (format "%%%ds: %%%ds" key-width (- -1 doc-width))
- key doc))
+ (cond
+ ((equal key " ") (format (format "%%-%ds" (+ 3 key-width doc-width)) doc))
+ (t (format (format "%%%ds: %%%ds" key-width (- -1 doc-width)) key doc))))
(defun hydra--to-string (x)
(if (stringp x)
x
(eval x)))
-(defun hydra--hint (body heads)
+(defun hydra--hint-heads-wocol (body heads)
"Generate a hint for the echo area.
-BODY, and HEADS are parameters to `defhydra'."
+BODY, and HEADS are parameters to `defhydra'.
+Works for heads without a property :column."
(let (alist)
(dolist (h heads)
(let ((val (assoc (cadr h) alist))
@@ -535,6 +545,17 @@ BODY, and HEADS are parameters to `defhydra'."
(eval res)
res))))
+(defun hydra--hint (body heads)
+ "Generate a hint for the echo area.
+BODY, and HEADS are parameters to `defhydra'."
+ (let* ((sorted-heads (hydra--sort-heads (hydra--normalize-heads heads)))
+ (heads-w-col (cl-remove-if-not (lambda (heads) (hydra--head-property
(nth 0 heads) :column)) sorted-heads))
+ (heads-wo-col (cl-remove-if (lambda (heads) (hydra--head-property
(nth 0 heads) :column)) sorted-heads)))
+ (concat (when heads-w-col
+ (concat "\n" (hydra--hint-from-matrix body
(hydra--generate-matrix heads-w-col))))
+ (when heads-wo-col
+ (hydra--hint-heads-wocol body (car heads-wo-col))))))
+
(defvar hydra-fontify-head-function nil
"Possible replacement for `hydra-fontify-head-default'.")
@@ -952,6 +973,91 @@ NAMES should be defined by `defhydradio' or similar."
(dolist (n names)
(set n (aref (get n 'range) 0))))
+;; Following functions deal with automatic docstring table generation from
:column head property
+(defun hydra--normalize-heads (heads)
+ "Ensure each head from HEADS have a property :column.
+Set it to the same value as preceding head or nil if no previous value
+was defined."
+ (let ((current-col nil))
+ (mapcar (lambda (head)
+ (if (hydra--head-has-property head :column)
+ (setq current-col (hydra--head-property head :column)))
+ (hydra--head-set-property head :column current-col))
+ heads)))
+
+(defun hydra--sort-heads (normalized-heads)
+ "Return a list of heads with non-nil doc sorted by ascending column property
+each head of NORMALIZED-HEADS must have a column property"
+ (let* ((heads-wo-nil-doc (cl-remove-if-not (lambda (head) (nth 2 head))
normalized-heads))
+ (heads-sorted (cl-sort heads-wo-nil-doc (lambda (it other)
+ (string<
(hydra--head-property it :column)
+
(hydra--head-property other :column))))))
+ ;; this operation partition the sorted head list into lists of heads with
same column property
+ (cl-loop for head in heads-sorted
+ for column-name = (hydra--head-property head :column)
+ with prev-column-name = (hydra--head-property (nth 0 heads-sorted)
:column)
+ unless (equal prev-column-name column-name) collect heads-one-column
into heads-all-columns
+ and do (setq heads-one-column nil)
+ collect head into heads-one-column
+ do (setq prev-column-name column-name)
+ finally return (append heads-all-columns (list heads-one-column)))))
+
+(defun hydra--pad-heads (heads-groups padding-head)
+ "Return a list of heads copied from HEADS-GROUPS where each heads group have
the same length.
+This is achieved by adding PADDING-HEAD were needed."
+ (cl-loop for heads-group in heads-groups
+ for this-head-group-length = (length heads-group)
+ with head-group-max-length = (apply #'max (mapcar (lambda (heads) (length
heads)) heads-groups))
+ if (<= this-head-group-length head-group-max-length)
+ collect (append heads-group (make-list (- head-group-max-length
this-head-group-length) padding-head))
+ into balanced-heads-groups
+ else collect heads-group into balanced-heads-groups
+ finally return balanced-heads-groups))
+
+(defun hydra--generate-matrix (heads-groups)
+ "Return a copy of HEADS-GROUPS with following differences:
+2 virtual heads acting as table header were added to each heads-group
+each head is decorated with 2 new properties max-doc-len and max-key-len
representing the maximum dimension of their owning group
+every heads-group have equal length by adding padding heads where applicable."
+ (when heads-groups
+ (cl-loop for heads-group in (hydra--pad-heads heads-groups '(" " nil " "
:exit t))
+ for column-name = (hydra--head-property (nth 0 heads-group) :column)
+ for max-key-len = (apply #'max (mapcar (lambda (x) (length (car x)))
heads-group))
+ for max-doc-len = (apply #'max
+ (length column-name)
+ (mapcar (lambda (x) (length (hydra--to-string
(nth 2 x)))) heads-group))
+ for header-virtual-head = `(" " nil ,column-name :column ,column-name
:exit t)
+ for separator-virtual-head = `(" " nil ,(make-string (+ 2 max-doc-len
max-key-len) ?-) :column ,column-name :exit t)
+ for decorated-heads = (copy-tree (apply 'list header-virtual-head
separator-virtual-head heads-group))
+ collect (mapcar (lambda (it)
+ (hydra--head-set-property it :max-key-len max-key-len)
+ (hydra--head-set-property it :max-doc-len
max-doc-len))
+ decorated-heads)
+ into decorated-heads-matrix
+ finally return decorated-heads-matrix)))
+
+(defun hydra--hint-from-matrix (body heads-matrix)
+ "Generate a formated table-style docstring according to HEADS-MATRIX and
BODY data and structure
+HEADS-MATRIX is expected to be a list of heads with following features:
+Each heads must have the same length
+Each head must have a property max-key-len and max-doc-len."
+ (when heads-matrix
+ (cl-loop with first-heads-col = (nth 0 heads-matrix)
+ with last-row-index = (- (length first-heads-col) 1)
+ for row-index from 0 to last-row-index
+ for heads-in-row = (mapcar (lambda (heads) (nth row-index heads))
heads-matrix)
+ concat (concat
+ (mapconcat (lambda (head)
+ (funcall hydra-key-doc-function
+ (hydra-fontify-head head body) ;; key
+ (hydra--head-property head :max-key-len)
+ (nth 2 head) ;; doc
+ (hydra--head-property head :max-doc-len)))
+ heads-in-row "| ") "\n")
+ into matrix-image
+ finally return matrix-image)))
+;; previous functions dealt with automatic docstring table generation from
:column head property
+
(defun hydra-idle-message (secs hint name)
"In SECS seconds display HINT."
(cancel-timer hydra-message-timer)
- [elpa] master 272dc4a 05/36: hydra.el (hydra-docstring-keys-translate-alist): Update, (continued)
- [elpa] master 272dc4a 05/36: hydra.el (hydra-docstring-keys-translate-alist): Update, Oleh Krehel, 2017/07/22
- [elpa] master 5be27f4 02/36: Add a shortcut to supply a head's eval-able docstring, Oleh Krehel, 2017/07/22
- [elpa] master 585db09 17/36: hydra-examples.el: Add example of setting verbosity, Oleh Krehel, 2017/07/22
- [elpa] master 6d5bdf7 11/36: Introduce (:hint none), Oleh Krehel, 2017/07/22
- [elpa] master 76d51ec 23/36: Fix compile warnings, Oleh Krehel, 2017/07/22
- [elpa] master dd5f703 27/36: hydra.el: Use error-message-string, not message, Oleh Krehel, 2017/07/22
- [elpa] master d2aaf86 20/36: Fix e.g. _f_(foo) in format string, Oleh Krehel, 2017/07/22
- [elpa] master a72d68a 28/36: hydra.el (hydra-fontify-head-default): Fix head keys as "%f", Oleh Krehel, 2017/07/22
- [elpa] master 3527b32 24/36: Fix byte compiler warnings, Oleh Krehel, 2017/07/22
- [elpa] master 943636f 35/36: hydra.el: Bump version, Oleh Krehel, 2017/07/22
- [elpa] master 3db82e5 29/36: Implement named columns,
Oleh Krehel <=
- [elpa] master 95008ea 30/36: hydra.el: Add automatic lookup for remaped cmd, Oleh Krehel, 2017/07/22
- [elpa] master 32b8352 36/36: Merge commit '943636fe4a35298d9d234222bc4520dec9ef2305' from hydra, Oleh Krehel, 2017/07/22
- [elpa] master 91f8e7c 22/36: hydra.el: Bump version, Oleh Krehel, 2017/07/22
- [elpa] master a85a617 03/36: Allow to use e.g. "↑" in place of "<up>" in the docstring, Oleh Krehel, 2017/07/22
- [elpa] master 9c2589f 19/36: Update the package description, Oleh Krehel, 2017/07/22
- [elpa] master 63de503 21/36: hydra.el (hydra-key-regex): Add "$", Oleh Krehel, 2017/07/22
- [elpa] master 81d88e4 15/36: hydra.el (hydra-show-hint): Extract from defhydra, Oleh Krehel, 2017/07/22
- [elpa] master 1d378c6 16/36: Allow to set hydra verbosity, Oleh Krehel, 2017/07/22
- [elpa] master 2ebf862 25/36: Move Amaranth warning message to a defvar, Oleh Krehel, 2017/07/22
- [elpa] master a07b92a 26/36: lv.el (lv-window): Rename to " *LV*", Oleh Krehel, 2017/07/22