From f355e4fc3b9a06e65e1102c073bdf340f06454c2 Mon Sep 17 00:00:00 2001 From: Kyle Meyer Date: Mon, 27 Dec 2021 21:20:32 -0500 Subject: lei q: Allow initial input for query to be customized piem-lei-query reads with initial input of "d:20.days.ago.. ", which I find useful because this time restriction works well with what I'm most often searching for. (This is a value I've long used with Notmuch.) However, what value of "recent" is most reasonable is likely to vary across users, and some users may not want the restriction at all. Add an option so that the initial input can be easily configured. Message-Id: <20211228022037.206597-2-kyle@kyleam.com> --- piem-lei.el | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/piem-lei.el b/piem-lei.el index ffd72f3..4485dcd 100644 --- a/piem-lei.el +++ b/piem-lei.el @@ -37,6 +37,12 @@ "Which lei executable to use." :type 'string) +(defcustom piem-lei-query-initial-input "d:20.days.ago.. " + "Initial input when reading `lei q' queries." + :package-version '(piem . "0.4.0") + :type '(choice (const :tag "None" nil) + (string :tag "Query"))) + ;;;; Helpers @@ -233,7 +239,9 @@ unless DISPLAY is non-nil." QUERY is split according to `split-string-and-unquote'." (interactive (list (split-string-and-unquote - (read-string "Query: " "d:20.days.ago.. " 'piem-lei-query-history)) + (read-string "Query: " + piem-lei-query-initial-input + 'piem-lei-query-history)) (transient-args 'piem-lei-q))) (with-current-buffer (get-buffer-create "*lei-query*") (let ((inhibit-read-only t)) -- cgit v1.2.3 From 8cb30023c1fbf7049cedb8df29dec8c42a332697 Mon Sep 17 00:00:00 2001 From: Kyle Meyer Date: Mon, 27 Dec 2021 21:20:33 -0500 Subject: lei q: Extract buffer names to variables The next commit will split piem-lei-query-thread into two commands, one for general queries with threaded output and one for displaying a single thread for a given message ID. It makes sense to have different buffer names for these commands, and going forward it's likely that there will be more name tweaks (e.g., support for "locked" buffers). Add defvars that can be bound to control the names. Message-Id: <20211228022037.206597-3-kyle@kyleam.com> --- piem-lei.el | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/piem-lei.el b/piem-lei.el index 4485dcd..9779c97 100644 --- a/piem-lei.el +++ b/piem-lei.el @@ -233,6 +233,8 @@ unless DISPLAY is non-nil." (error "Date did not match expected format: %S" date)) 'font-lock-face 'piem-lei-query-date))) +(defvar piem-lei-query--buffer-name "*lei-query*") + ;;;###autoload (defun piem-lei-query (query &optional args) "Call `lei q' with QUERY and ARGS. @@ -243,7 +245,7 @@ QUERY is split according to `split-string-and-unquote'." piem-lei-query-initial-input 'piem-lei-query-history)) (transient-args 'piem-lei-q))) - (with-current-buffer (get-buffer-create "*lei-query*") + (with-current-buffer (get-buffer-create piem-lei-query--buffer-name) (let ((inhibit-read-only t)) (erase-buffer) (piem-lei-insert-output @@ -669,6 +671,8 @@ Return a list with a `piem-lei-msg' object for each root." (forward-line)) (nreverse items)))) +(defvar piem-lei-query-threads--buffer-name "*lei-thread*") + (defun piem-lei-query-thread (mid &optional args) "Show thread containing message MID. ARGS is passed to the underlying `lei q' call." @@ -681,7 +685,8 @@ ARGS is passed to the underlying `lei q' call." (append args (list "--threads") query))) (msgs (piem-lei-query--thread records)) depths pt-final subject-prev) - (with-current-buffer (get-buffer-create "*lei-thread*") + (with-current-buffer + (get-buffer-create piem-lei-query-threads--buffer-name) (let ((inhibit-read-only t)) (erase-buffer) (while msgs -- cgit v1.2.3 From 601d79ec752dba957f4a27b993280bc1499676ef Mon Sep 17 00:00:00 2001 From: Kyle Meyer Date: Mon, 27 Dec 2021 21:20:34 -0500 Subject: lei q: Support displaying multiple threads piem-lei-query shows unthreaded results. From there, piem-lei-query-thread can be used to show the entire thread for that result. This design is driven largely by my use of Notmuch, where I call notmuch-search and then follow up with a custom variant of notmuch-tree to show _one_ thread. However, users familiar with notmuch-tree probably expect to be able to display _multiple_ threads. piem-lei-query--thread already returns a list of threads, so it really just needs to be exposed at the command level. Update piem-lei-query-thread to make it handle a general query, renaming it slightly to make it clearer that the command now supports displaying multiple threads. Then, add a wrapper piem-lei-mid-thread that handles the old "single thread for a given MID" behavior. Rather than adding another suffix command to the lei-q transient (piem-lei-query-threads in addition to the existing piem-lei-query), I considered adding --threads to the transient and then having piem-lei-query check whether it's in the arguments. Conceptually, I dislike that because it conflates threaded _display_ with lei's --threads behavior that's instead about whether to include other messages from a match's thread in the results. Also, it'd mean some downstream handling of piem-lei-buffer-args (e.g., by piem-lei-query-show) would be complicated by the need to filter out --threads. Note that piem-lei-query-thread no longer sets piem-lei-buffer-mid because the buffer is no longer tied to a single message ID, which is okay because, unlike in show buffers, the value isn't actually used. Suggested-by: Xinglu Chen Link: https://inbox.kyleam.com/piem/871r96am1q.fsf@yoctocell.xyz Message-Id: <20211228022037.206597-4-kyle@kyleam.com> --- piem-lei.el | 41 +++++++++++++++++++++++++++-------------- 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/piem-lei.el b/piem-lei.el index 9779c97..63a2548 100644 --- a/piem-lei.el +++ b/piem-lei.el @@ -176,7 +176,7 @@ unless DISPLAY is non-nil." (defvar piem-lei-show-mode-map (let ((map (make-sparse-keymap))) (define-key map "s" #'piem-lei-q) - (define-key map "t" #'piem-lei-query-thread) + (define-key map "t" #'piem-lei-mid-thread) map) "Keymap for `piem-lei-show-mode'.") @@ -376,7 +376,7 @@ line's message, scroll its text downward, passing ARG to (define-key map "n" #'piem-lei-query-next-line) (define-key map "p" #'piem-lei-query-previous-line) (define-key map "s" #'piem-lei-q) - (define-key map "t" #'piem-lei-query-thread) + (define-key map "t" #'piem-lei-mid-thread) map) "Keymap for `piem-lei-query-mode'.") @@ -521,7 +521,8 @@ external (via `lei add-external')." (piem-lei-q:--limit) (piem-lei-q:--offset)] ["Actions" - ("s" "Search with lei" piem-lei-query)]) + ("s" "Search" piem-lei-query) + ("t" "Search, threaded output" piem-lei-query-threads)]) ;;;;; Threading @@ -671,17 +672,20 @@ Return a list with a `piem-lei-msg' object for each root." (forward-line)) (nreverse items)))) -(defvar piem-lei-query-threads--buffer-name "*lei-thread*") +(defvar piem-lei-query-threads--buffer-name piem-lei-query--buffer-name) -(defun piem-lei-query-thread (mid &optional args) - "Show thread containing message MID. -ARGS is passed to the underlying `lei q' call." +(defun piem-lei-query-threads (query &optional args pt-mid) + "Show threads containing matches for QUERY. +ARGS is passed to the underlying `lei q' call. If PT-MID is +non-nil and matches the message ID of a result, move point to +that line." (interactive - (if-let ((mid (piem-lei-get-mid))) - (list mid piem-lei-buffer-args) - (list (read-string "Message ID: " nil nil (piem-mid)) nil))) - (let* ((query (list (concat "mid:" mid))) - (records (piem-lei-query--slurp + (list (split-string-and-unquote + (read-string "Query: " + piem-lei-query-initial-input + 'piem-lei-query-history)) + (transient-args 'piem-lei-q))) + (let* ((records (piem-lei-query--slurp (append args (list "--threads") query))) (msgs (piem-lei-query--thread records)) depths pt-final subject-prev) @@ -729,17 +733,26 @@ ARGS is passed to the underlying `lei q' call." 'font-lock-face 'piem-lei-query-thread-ghost)) (setq subject-prev nil)) - (when (equal mid-msg mid) + (when (equal mid-msg pt-mid) (setq pt-final (line-beginning-position))) (insert ?\n))) (insert "End of lei-q results")) (goto-char (or pt-final (point-min))) (piem-lei-query-mode) (setq piem-lei-buffer-args args) - (setq piem-lei-buffer-mid mid) (setq piem-lei-buffer-query query) (pop-to-buffer-same-window (current-buffer))))) +(defun piem-lei-mid-thread (mid &optional args) + "Show thread containing message MID. +ARGS is passed to the underlying `lei q' call." + (interactive + (if-let ((mid (piem-lei-get-mid))) + (list mid piem-lei-buffer-args) + (list (read-string "Message ID: " nil nil (piem-mid)) nil))) + (let ((piem-lei-query-threads--buffer-name "*lei-thread*")) + (piem-lei-query-threads (list (concat "mid:" mid)) args mid))) + ;;;; piem integration -- cgit v1.2.3 From f56c5e01de855187c7a348ba186b045a6970575c Mon Sep 17 00:00:00 2001 From: Kyle Meyer Date: Mon, 27 Dec 2021 21:20:35 -0500 Subject: lei q: Add date placeholder for ghost messages piem-lei-query-threads inserts only the message ID of a ghost message, using spaces for where the date and time is for other messages. The space placeholders will become visually confusing when the next commit starts inserting an empty line between threads. Switch to using a dummy date-time instead. Message-Id: <20211228022037.206597-5-kyle@kyleam.com> --- piem-lei.el | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/piem-lei.el b/piem-lei.el index 63a2548..dedaee2 100644 --- a/piem-lei.el +++ b/piem-lei.el @@ -727,11 +727,12 @@ that line." (line-end-position) (list 'piem-lei-query-result data)) (setq subject-prev subject)) - (insert (make-string 17 ?\s) ; Date alignment. - (piem-lei-query--format-thread-marker depth) - (propertize (concat " <" mid-msg ">") - 'font-lock-face - 'piem-lei-query-thread-ghost)) + (insert (propertize + (concat "0000-00-00 00:00 " + (piem-lei-query--format-thread-marker depth) + " <" mid-msg ">") + 'font-lock-face + 'piem-lei-query-thread-ghost)) (setq subject-prev nil)) (when (equal mid-msg pt-mid) (setq pt-final (line-beginning-position))) -- cgit v1.2.3 From 69201fc7787ea92dbd8afc8b4ce9a40c71981a30 Mon Sep 17 00:00:00 2001 From: Kyle Meyer Date: Mon, 27 Dec 2021 21:20:36 -0500 Subject: lei q: Separate different threads with newline For piem-lei-query-threads output with multiple threads, add a newline to make it easier to spot the start of a new thread. In terms of presentation, I think it would also be nice to distinguish search hits in the thread from other messages. For local messages, pct could be used to do this, but punt on that for now. Message-Id: <20211228022037.206597-6-kyle@kyleam.com> --- piem-lei.el | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/piem-lei.el b/piem-lei.el index dedaee2..1c232da 100644 --- a/piem-lei.el +++ b/piem-lei.el @@ -699,6 +699,11 @@ that line." (children (piem-lei-msg-children msg)) (depth (1+ (or (cdr (assoc (piem-lei-msg-parent msg) depths)) -1)))) + (when (and (equal depth 0) + (not (bobp))) + ;; Add newline between threads to make different threads + ;; easier to distinguish. + (insert ?\n)) (when children (setq msgs (append children msgs))) (push (cons msg depth) depths) -- cgit v1.2.3 From 8689741dd5a2dbb22ff848fa7993727d834064f0 Mon Sep 17 00:00:00 2001 From: Kyle Meyer Date: Mon, 27 Dec 2021 21:20:37 -0500 Subject: lei q: Sort threads by date of initial message Now that there is a command for displaying multiple threads, the order that piem-lei-query--thread returns the threads matters. When lei-q is given the --threads option, the threads don't have a clear order, and --sort and --reverse don't have an effect, so piem-lei-query--thread must handle the sorting. Teach piem-lei-query--thread to sort by the date of the root message in a thread, adding a option to control the direction of this sorting. It may be better to sort by the latest date within the entire thread. However, doing so is more expensive, so don't bother until a clear case is made that it's the better option. Message-Id: <20211228022037.206597-7-kyle@kyleam.com> --- piem-lei.el | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/piem-lei.el b/piem-lei.el index 1c232da..6c53733 100644 --- a/piem-lei.el +++ b/piem-lei.el @@ -43,6 +43,13 @@ :type '(choice (const :tag "None" nil) (string :tag "Query"))) +(defcustom piem-lei-query-oldest-thread-first nil + "Whether to display older threads before newer ones. +The date and time of the initial message is taken as the age of +the thread." + :package-version '(piem . "0.4.0") + :type 'boolean) + ;;;; Helpers @@ -567,6 +574,20 @@ external (via `lei add-external')." (setq msg2 (piem-lei-msg-parent msg2))) nil))) +(defun piem-lei--msg-time-with-fallback (msg) + (or (piem-lei-msg-time msg) + ;; The initial message is a ghost. Use the time from the first + ;; child encountered, without making any effort to ensure that + ;; it's the sibling with the earliest time. + (catch 'stop + (let ((children (piem-lei-msg-children msg))) + (while children + (let ((child (pop children))) + (if-let ((time (piem-lei-msg-time child))) + (throw 'stop time) + (setq children + (append children (piem-lei-msg-children child)))))))))) + (defun piem-lei-query--thread (records) "Thread messages in RECORDS. @@ -612,7 +633,13 @@ Return a list with a `piem-lei-msg' object for each root." (unless (piem-lei-msg-parent v) (push v roots))) thread) - (nreverse roots)))) + (let* ((fn (if piem-lei-query-oldest-thread-first #'not #'identity)) + (sort-fn + (lambda (a b) + (funcall fn + (time-less-p (piem-lei--msg-time-with-fallback b) + (piem-lei--msg-time-with-fallback a)))))) + (sort roots sort-fn))))) (defvar piem-lei-query--subject-split-re (rx string-start -- cgit v1.2.3