Add: Search buffer refreshing, header line, etc.

This commit is contained in:
Adam Porter 2019-06-10 18:18:36 -05:00
parent a74520c223
commit 0aec8ec603
3 changed files with 109 additions and 33 deletions

View file

@ -69,7 +69,23 @@ Feedback on these APIs is welcome. Eventually, after being tested and polished,
*** org-ql-search *** org-ql-search
The command =org-ql-search= prompts for a query, a list of buffers or files, and how to group and sort results. Without prefix, it searches the current buffer instead of prompting. Then it presents the results in an agenda-like view. Read ~QUERY~ and search with ~org-ql~. Interactively, prompt for these variables:
~BUFFERS-FILES~: ~A~ list of buffers and/or files to search. Interactively, may also be:
+ ~buffer~: search the current buffer
+ ~all~: search all Org buffers
+ ~agenda~: search buffers returned by the function ~org-agenda-files~
+ An expression which evaluates to a list of files/buffers
+ A space-separated list of file or buffer names
~GROUPS~: An ~org-super-agenda~ group set. See variable ~org-super-agenda-groups~.
~NARROW~: When non-nil, don't widen buffers before searching. Interactively, with prefix, leave narrowed.
~SORT~: One or a list of ~org-ql~ sorting functions, like ~date~ or ~priority~.
Press =g= to refresh the results buffer.
[[images/org-ql-search.gif]] [[images/org-ql-search.gif]]

Binary file not shown.

Before

Width:  |  Height:  |  Size: 414 KiB

After

Width:  |  Height:  |  Size: 202 KiB

Before After
Before After

View file

@ -47,6 +47,13 @@
(defvar org-ql-agenda-buffer-name "*Org Agenda NG*" (defvar org-ql-agenda-buffer-name "*Org Agenda NG*"
"Name of default `org-ql-agenda' buffer.") "Name of default `org-ql-agenda' buffer.")
;; For refreshing results buffers.
(defvar org-ql-buffers-files)
(defvar org-ql-query)
(defvar org-ql-sort)
(defvar org-ql-narrow)
(defvar org-ql-super-groups)
;;;; Macros ;;;; Macros
;; FIXME: DRY these two macros. ;; FIXME: DRY these two macros.
@ -124,8 +131,13 @@ is used, rather than binding it locally."
Interactively, prompt for these variables: Interactively, prompt for these variables:
BUFFERS-FILES: A list of buffers and/or files to search. BUFFERS-FILES: A list of buffers and/or files to search.
Interactively, may also be an expression which evaluates to such Interactively, may also be:
a list.
- `buffer': search the current buffer
- `all': search all Org buffers
- `agenda': search buffers returned by the function `org-agenda-files'
- An expression which evaluates to a list of files/buffers
- A space-separated list of file or buffer names
GROUPS: An `org-super-agenda' group set. See variable GROUPS: An `org-super-agenda' group set. See variable
`org-super-agenda-groups'. `org-super-agenda-groups'.
@ -136,17 +148,14 @@ searching. Interactively, with prefix, leave narrowed.
SORT: One or a list of `org-ql' sorting functions, like `date' or SORT: One or a list of `org-ql' sorting functions, like `date' or
`priority'." `priority'."
(declare (indent defun)) (declare (indent defun))
(interactive (progn (interactive (list (pcase-exhaustive (completing-read "Buffers/Files:"
(when (and current-prefix-arg (list 'buffer 'agenda 'all))
(not (derived-mode-p 'org-mode))) ("agenda" (org-agenda-files))
(user-error "Not an Org buffer: %s" (buffer-name))) ("all" (--select (equal (buffer-local-value 'major-mode it) 'org-mode)
(list (--if-let (read-from-minibuffer "Buffers/Files (blank for current buffer): ") (buffer-list)))
;; TODO: Add glob matching? Buffer mode matching? ("buffer" (current-buffer))
(pcase it ((and form (guard (rx bos "("))) (-flatten (eval (read form))))
("" (current-buffer)) (else (s-split (rx (1+ space)) else)))
((rx bos "(") (-flatten (eval (read it))))
(_ (s-split (rx (1+ space)) it)))
(current-buffer))
(read-minibuffer "Query: ") (read-minibuffer "Query: ")
:narrow (eq current-prefix-arg '(4)) :narrow (eq current-prefix-arg '(4))
:groups (pcase (completing-read "Group by: " :groups (pcase (completing-read "Group by: "
@ -163,12 +172,23 @@ SORT: One or a list of `org-ql' sorting functions, like `date' or
"scheduled" "scheduled"
"todo")) "todo"))
("Don't sort" nil) ("Don't sort" nil)
(sort (intern sort)))))) (sort (intern sort)))))
(org-ql-agenda--agenda buffers-files (org-ql-agenda--agenda buffers-files
query query
:narrow narrow :narrow narrow
:sort sort :sort sort
:super-groups groups)) :super-groups groups
:buffer "*Org QL Search*"))
(defun org-ql-search-refresh ()
"Refresh current `org-ql-search' buffer."
(interactive)
(org-ql-agenda--agenda org-ql-buffers-files
org-ql-query
:sort org-ql-sort
:narrow org-ql-narrow
:super-groups org-ql-super-groups
:buffer (current-buffer)))
;;;; Functions ;;;; Functions
@ -191,14 +211,54 @@ SORT: One or a list of `org-ql' sorting functions, like `date' or
(cond ((bound-and-true-p org-super-agenda-mode) (org-super-agenda--group-items it)) (cond ((bound-and-true-p org-super-agenda-mode) (org-super-agenda--group-items it))
(t it)) (t it))
(s-join "\n" it))) (s-join "\n" it)))
(buffer (cl-etypecase buffer
(string (org-ql-agenda--buffer buffer))
(null (org-ql-agenda--buffer buffer))
(buffer buffer)))
(inhibit-read-only t)) (inhibit-read-only t))
(with-current-buffer (org-ql-agenda--buffer buffer) (with-current-buffer buffer
;; Prepare buffer, saving data for refreshing.
(setq-local org-ql-buffers-files buffers-files)
(setq-local org-ql-query query)
(setq-local org-ql-sort sort)
(setq-local org-ql-narrow narrow)
(setq-local org-ql-super-groups super-groups)
;; TODO: Derive a minor mode and set keymap there.
(local-set-key "g" #'org-ql-search-refresh)
(let* ((query-formatted (format "%S" query))
(query-formatted (propertize (org-ql-agenda--font-lock-string 'emacs-lisp-mode query-formatted)
'help-echo query-formatted))
(query-width (length query-formatted))
(available-width (- (window-width)
(length "In: ")
(length "Query: ")
query-width 4))
(buffers-files-formatted (format "%S" buffers-files))
(buffers-files-formatted (propertize (->> buffers-files-formatted
(org-ql-agenda--font-lock-string 'emacs-lisp-mode)
(s-truncate available-width))
'help-echo buffers-files-formatted)))
(setq-local header-line-format (concat (propertize "Query: " 'face 'org-agenda-structure)
query-formatted " "
(propertize "In: " 'face 'org-agenda-structure)
buffers-files-formatted)))
;; Clear buffer, insert entries, etc.
(erase-buffer) (erase-buffer)
(insert entries) (insert entries)
(pop-to-buffer (current-buffer)) (pop-to-buffer (current-buffer))
(org-agenda-finalize) (org-agenda-finalize)
(goto-char (point-min))))) (goto-char (point-min)))))
(defun org-ql-agenda--font-lock-string (mode s)
"Return string S font-locked according to MODE."
;; FIXME: Is this the proper way to do this? It works, but I feel like there must be a built-in way...
(with-temp-buffer
(delay-mode-hooks
(insert s)
(funcall mode)
(font-lock-ensure)
(buffer-string))))
(defun org-ql-agenda--buffer (&optional name) (defun org-ql-agenda--buffer (&optional name)
"Return Agenda NG buffer, creating it if necessary. "Return Agenda NG buffer, creating it if necessary.
If NAME is non-nil, return buffer by that name instead of using If NAME is non-nil, return buffer by that name instead of using