From 49b973ab11218e2e2f7afb3379208a7317e20779 Mon Sep 17 00:00:00 2001 From: Adam Porter Date: Fri, 18 Mar 2022 10:03:36 -0500 Subject: [PATCH 01/40] Meta: 0.6.1-pre --- README.org | 4 ++ helm-org-ql.el | 2 +- org-ql.el | 2 +- org-ql.info | 163 ++++++++++++++++++++++++++----------------------- 4 files changed, 93 insertions(+), 78 deletions(-) diff --git a/README.org b/README.org index 1a20b83..2cfde77 100644 --- a/README.org +++ b/README.org @@ -522,6 +522,10 @@ Simple links may also be written manually in either sexp or non-sexp form, like: /Note:/ Breaking changes may be made before version 1.0, but in the event of major changes, attempts at backward compatibility will be made with obsolescence declarations, translation of arguments, etc. Users who need stability guarantees before 1.0 may choose to use tagged stable releases. +** 0.6.1-pre + +Nothing new yet. + ** 0.6 *Added* diff --git a/helm-org-ql.el b/helm-org-ql.el index feac1c3..0ab72ec 100644 --- a/helm-org-ql.el +++ b/helm-org-ql.el @@ -2,7 +2,7 @@ ;; Author: Adam Porter ;; URL: https://github.com/alphapapa/org-ql -;; Version: 0.6 +;; Version: 0.6.1-pre ;; Package-Requires: ((emacs "26.1") (dash "2.18.1") (s "1.12.0") (helm-org "1.0") (org-ql "0.6-pre")) ;;; Commentary: diff --git a/org-ql.el b/org-ql.el index 91d2710..f1ede36 100644 --- a/org-ql.el +++ b/org-ql.el @@ -2,7 +2,7 @@ ;; Author: Adam Porter ;; Url: https://github.com/alphapapa/org-ql -;; Version: 0.6 +;; Version: 0.6.1-pre ;; Package-Requires: ((emacs "26.1") (dash "2.18.1") (f "0.17.2") (map "2.1") (org "9.0") (org-super-agenda "1.2") (ov "1.0.6") (peg "1.0") (s "1.12.0") (transient "0.1") (ts "0.2-pre")) ;; Keywords: hypermedia, outlines, Org, agenda diff --git a/org-ql.info b/org-ql.info index 09826ca..84e125f 100644 --- a/org-ql.info +++ b/org-ql.info @@ -69,6 +69,7 @@ Functions / Macros Changelog +* 0.6.1-pre: 061-pre. * 0.6: 06. * 0.5.2: 052. * 0.5.1: 051. @@ -957,6 +958,7 @@ releases. * Menu: +* 0.6.1-pre: 061-pre. * 0.6: 06. * 0.5.2: 052. * 0.5.1: 051. @@ -981,9 +983,17 @@ releases. * 0.1: 01.  -File: README.info, Node: 06, Next: 052, Up: Changelog +File: README.info, Node: 061-pre, Next: 06, Up: Changelog -5.1 0.6 +5.1 0.6.1-pre +============= + +Nothing new yet. + + +File: README.info, Node: 06, Next: 052, Prev: 061-pre, Up: Changelog + +5.2 0.6 ======= *Added* @@ -1050,7 +1060,7 @@ File: README.info, Node: 06, Next: 052, Up: Changelog  File: README.info, Node: 052, Next: 051, Prev: 06, Up: Changelog -5.2 0.5.2 +5.3 0.5.2 ========= *Fixed* @@ -1061,7 +1071,7 @@ File: README.info, Node: 052, Next: 051, Prev: 06, Up: Changelog  File: README.info, Node: 051, Next: 05, Prev: 052, Up: Changelog -5.3 0.5.1 +5.4 0.5.1 ========= *Fixed* @@ -1074,7 +1084,7 @@ File: README.info, Node: 051, Next: 05, Prev: 052, Up: Changelog  File: README.info, Node: 05, Next: 049, Prev: 051, Up: Changelog -5.4 0.5 +5.5 0.5 ======= *Added* @@ -1115,7 +1125,7 @@ File: README.info, Node: 05, Next: 049, Prev: 051, Up: Changelog  File: README.info, Node: 049, Next: 048, Prev: 05, Up: Changelog -5.5 0.4.9 +5.6 0.4.9 ========= *Fixed* @@ -1126,7 +1136,7 @@ File: README.info, Node: 049, Next: 048, Prev: 05, Up: Changelog  File: README.info, Node: 048, Next: 047, Prev: 049, Up: Changelog -5.6 0.4.8 +5.7 0.4.8 ========= *Fixed* @@ -1138,7 +1148,7 @@ File: README.info, Node: 048, Next: 047, Prev: 049, Up: Changelog  File: README.info, Node: 047, Next: 046, Prev: 048, Up: Changelog -5.7 0.4.7 +5.8 0.4.7 ========= *Fixed* @@ -1151,7 +1161,7 @@ File: README.info, Node: 047, Next: 046, Prev: 048, Up: Changelog  File: README.info, Node: 046, Next: 045, Prev: 047, Up: Changelog -5.8 0.4.6 +5.9 0.4.6 ========= *Fixed* @@ -1164,8 +1174,8 @@ File: README.info, Node: 046, Next: 045, Prev: 047, Up: Changelog  File: README.info, Node: 045, Next: 044, Prev: 046, Up: Changelog -5.9 0.4.5 -========= +5.10 0.4.5 +========== *Fixed* • Non-case-folding predicates like ‘(todo)’ unnecessarily disabled @@ -1176,7 +1186,7 @@ File: README.info, Node: 045, Next: 044, Prev: 046, Up: Changelog  File: README.info, Node: 044, Next: 043, Prev: 045, Up: Changelog -5.10 0.4.4 +5.11 0.4.4 ========== *Fixed* @@ -1188,7 +1198,7 @@ File: README.info, Node: 044, Next: 043, Prev: 045, Up: Changelog  File: README.info, Node: 043, Next: 042, Prev: 044, Up: Changelog -5.11 0.4.3 +5.12 0.4.3 ========== *Fixed* @@ -1198,7 +1208,7 @@ File: README.info, Node: 043, Next: 042, Prev: 044, Up: Changelog  File: README.info, Node: 042, Next: 041, Prev: 043, Up: Changelog -5.12 0.4.2 +5.13 0.4.2 ========== *Fixed* @@ -1207,7 +1217,7 @@ File: README.info, Node: 042, Next: 041, Prev: 043, Up: Changelog  File: README.info, Node: 041, Next: 04, Prev: 042, Up: Changelog -5.13 0.4.1 +5.14 0.4.1 ========== *Fixed* @@ -1217,7 +1227,7 @@ File: README.info, Node: 041, Next: 04, Prev: 042, Up: Changelog  File: README.info, Node: 04, Next: 032, Prev: 041, Up: Changelog -5.14 0.4 +5.15 0.4 ======== _Note:_ The next release, 0.5, may include changes which will require @@ -1298,7 +1308,7 @@ as they will be pushed to the master branch when ready.  File: README.info, Node: 032, Next: 031, Prev: 04, Up: Changelog -5.15 0.3.2 +5.16 0.3.2 ========== *Fixed* @@ -1311,7 +1321,7 @@ File: README.info, Node: 032, Next: 031, Prev: 04, Up: Changelog  File: README.info, Node: 031, Next: 03, Prev: 032, Up: Changelog -5.16 0.3.1 +5.17 0.3.1 ========== *Fixed* @@ -1321,7 +1331,7 @@ File: README.info, Node: 031, Next: 03, Prev: 032, Up: Changelog  File: README.info, Node: 03, Next: 023, Prev: 031, Up: Changelog -5.17 0.3 +5.18 0.3 ======== *Added* @@ -1386,7 +1396,7 @@ File: README.info, Node: 03, Next: 023, Prev: 031, Up: Changelog  File: README.info, Node: 023, Next: 022, Prev: 03, Up: Changelog -5.18 0.2.3 +5.19 0.2.3 ========== *Fixed* @@ -1396,7 +1406,7 @@ File: README.info, Node: 023, Next: 022, Prev: 03, Up: Changelog  File: README.info, Node: 022, Next: 021, Prev: 023, Up: Changelog -5.19 0.2.2 +5.20 0.2.2 ========== *Fixed* @@ -1407,7 +1417,7 @@ File: README.info, Node: 022, Next: 021, Prev: 023, Up: Changelog  File: README.info, Node: 021, Next: 02, Prev: 022, Up: Changelog -5.20 0.2.1 +5.21 0.2.1 ========== *Fixed* @@ -1417,7 +1427,7 @@ File: README.info, Node: 021, Next: 02, Prev: 022, Up: Changelog  File: README.info, Node: 02, Next: 01, Prev: 021, Up: Changelog -5.21 0.2 +5.22 0.2 ======== *Added* @@ -1500,7 +1510,7 @@ File: README.info, Node: 02, Next: 01, Prev: 021, Up: Changelog  File: README.info, Node: 01, Prev: 02, Up: Changelog -5.22 0.1 +5.23 0.1 ======== First tagged release. @@ -1558,58 +1568,59 @@ GPLv3  Tag Table: Node: Top225 -Node: Contents1689 -Node: Screenshots1812 -Node: Installation1930 -Node: Quelpa2426 -Node: Helm support2954 -Node: Usage3345 -Node: Commands3743 -Node: org-ql-search4216 -Node: helm-org-ql5866 -Node: org-ql-view6226 -Node: org-ql-view-sidebar6726 -Node: org-ql-view-recent-items7082 -Node: org-ql-sparse-tree7566 -Node: Queries8366 -Node: Non-sexp query syntax9477 -Node: General predicates11201 -Node: Ancestor/descendant predicates17422 -Node: Date/time predicates18550 -Node: Functions / Macros21638 -Node: Agenda-like views21936 -Node: Listing / acting-on results23341 -Node: Custom predicates28977 -Node: Dynamic block32636 -Node: Links35348 -Node: Tips36035 -Node: Changelog36353 -Node: 0637071 -Node: 05240070 -Node: 05140370 -Node: 0540787 -Node: 04942261 -Node: 04842535 -Node: 04742882 -Node: 04643277 -Node: 04543677 -Node: 04444036 -Node: 04344395 -Node: 04244592 -Node: 04144753 -Node: 0444994 -Node: 03248927 -Node: 03149306 -Node: 0349503 -Node: 02352478 -Node: 02252706 -Node: 02152974 -Node: 0253173 -Node: 0157208 -Node: Notes57309 -Node: Comparison with Org Agenda searches57471 -Node: org-sidebar58343 -Node: License58622 +Node: Contents1712 +Node: Screenshots1835 +Node: Installation1953 +Node: Quelpa2449 +Node: Helm support2977 +Node: Usage3368 +Node: Commands3766 +Node: org-ql-search4239 +Node: helm-org-ql5889 +Node: org-ql-view6249 +Node: org-ql-view-sidebar6749 +Node: org-ql-view-recent-items7105 +Node: org-ql-sparse-tree7589 +Node: Queries8389 +Node: Non-sexp query syntax9500 +Node: General predicates11224 +Node: Ancestor/descendant predicates17445 +Node: Date/time predicates18573 +Node: Functions / Macros21661 +Node: Agenda-like views21959 +Node: Listing / acting-on results23364 +Node: Custom predicates29000 +Node: Dynamic block32659 +Node: Links35371 +Node: Tips36058 +Node: Changelog36376 +Node: 061-pre37117 +Node: 0637228 +Node: 05240243 +Node: 05140543 +Node: 0540960 +Node: 04942434 +Node: 04842708 +Node: 04743055 +Node: 04643450 +Node: 04543850 +Node: 04444211 +Node: 04344570 +Node: 04244767 +Node: 04144928 +Node: 0445169 +Node: 03249102 +Node: 03149481 +Node: 0349678 +Node: 02352653 +Node: 02252881 +Node: 02153149 +Node: 0253348 +Node: 0157383 +Node: Notes57484 +Node: Comparison with Org Agenda searches57646 +Node: org-sidebar58518 +Node: License58797  End Tag Table From 1e7700e64e129101482cda35af36b10aae4311e1 Mon Sep 17 00:00:00 2001 From: Adam Porter Date: Fri, 18 Mar 2022 10:28:17 -0500 Subject: [PATCH 02/40] Fix: Links to headings with stats cookies in dynamic blocks Fixes #248. Closes #249. Thanks to Maikol Solis (@maikol-solis) for reporting, and to him and Ihor Radchenko (@yantar92) for helping plan the fix. --- README.org | 3 ++- org-ql-search.el | 15 +++++++++--- org-ql.info | 59 ++++++++++++++++++++++++++---------------------- 3 files changed, 46 insertions(+), 31 deletions(-) diff --git a/README.org b/README.org index 2cfde77..3161a4d 100644 --- a/README.org +++ b/README.org @@ -524,7 +524,8 @@ Simple links may also be written manually in either sexp or non-sexp form, like: ** 0.6.1-pre -Nothing new yet. +*Fixed* ++ In dynamic blocks, links to headings with statistics cookies were broken. (Fixes [[https://github.com/alphapapa/org-ql/issues/248][#248]]. Thanks to [[https://github.com/maikol-solis][Maikol Solis]] and [[https://github.com/yantar92][Ihor Radchenko]].) ** 0.6 diff --git a/org-ql-search.el b/org-ql-search.el index 4fb5489..aa75656 100644 --- a/org-ql-search.el +++ b/org-ql-search.el @@ -38,6 +38,15 @@ (require 'org-ql) (require 'org-ql-view) +;;;; Compatibility + +(defalias 'org-ql-search--link-heading-search-string + (cond ((fboundp 'org-link--normalize-string) #'org-link--normalize-string) + ((fboundp 'org-link-heading-search-string) #'org-link-heading-search-string) + ((fboundp 'org-make-org-heading-search-string) #'org-make-org-heading-search-string) + (t (warn "org-ql: Unable to define alias `org-ql-search--link-heading-search-string'. This may affect links in dynamic blocks. Please report this as a bug.") + #'identity))) + ;;;; Variables (defvar org-ql-block-header nil @@ -285,9 +294,9 @@ For example, an org-ql dynamic block header could look like: (list (cons 'todo (lambda (element) (org-element-property :todo-keyword element))) (cons 'heading (lambda (element) - (org-make-link-string (org-element-property :raw-value element) - (org-link-display-format - (org-element-property :raw-value element))))) + (let ((normalized-heading + (org-ql-search--link-heading-search-string (org-element-property :raw-value element)))) + (org-make-link-string normalized-heading (org-link-display-format normalized-heading))))) (cons 'priority (lambda (element) (--when-let (org-element-property :priority element) (char-to-string it)))) diff --git a/org-ql.info b/org-ql.info index 84e125f..8349fd0 100644 --- a/org-ql.info +++ b/org-ql.info @@ -988,7 +988,12 @@ File: README.info, Node: 061-pre, Next: 06, Up: Changelog 5.1 0.6.1-pre ============= -Nothing new yet. +*Fixed* + • In dynamic blocks, links to headings with statistics cookies were + broken. (Fixes #248 + (https://github.com/alphapapa/org-ql/issues/248). Thanks to Maikol + Solis (https://github.com/maikol-solis) and Ihor Radchenko + (https://github.com/yantar92).)  File: README.info, Node: 06, Next: 052, Prev: 061-pre, Up: Changelog @@ -1595,32 +1600,32 @@ Node: Links35371 Node: Tips36058 Node: Changelog36376 Node: 061-pre37117 -Node: 0637228 -Node: 05240243 -Node: 05140543 -Node: 0540960 -Node: 04942434 -Node: 04842708 -Node: 04743055 -Node: 04643450 -Node: 04543850 -Node: 04444211 -Node: 04344570 -Node: 04244767 -Node: 04144928 -Node: 0445169 -Node: 03249102 -Node: 03149481 -Node: 0349678 -Node: 02352653 -Node: 02252881 -Node: 02153149 -Node: 0253348 -Node: 0157383 -Node: Notes57484 -Node: Comparison with Org Agenda searches57646 -Node: org-sidebar58518 -Node: License58797 +Node: 0637492 +Node: 05240507 +Node: 05140807 +Node: 0541224 +Node: 04942698 +Node: 04842972 +Node: 04743319 +Node: 04643714 +Node: 04544114 +Node: 04444475 +Node: 04344834 +Node: 04245031 +Node: 04145192 +Node: 0445433 +Node: 03249366 +Node: 03149745 +Node: 0349942 +Node: 02352917 +Node: 02253145 +Node: 02153413 +Node: 0253612 +Node: 0157647 +Node: Notes57748 +Node: Comparison with Org Agenda searches57910 +Node: org-sidebar58782 +Node: License59061  End Tag Table From 4f11878b7d5f7c51f7d5a1a5805d5d0383f66966 Mon Sep 17 00:00:00 2001 From: Adam Porter Date: Fri, 18 Mar 2022 09:58:22 -0500 Subject: [PATCH 03/40] Update: Transient macro names Closes #269. See https://github.com/magit/transient/issues/192. Thanks to Jonas Bernoulli (@tarsius). --- README.org | 3 +++ org-ql-view.el | 12 +++++------ org-ql.info | 57 +++++++++++++++++++++++++++----------------------- 3 files changed, 40 insertions(+), 32 deletions(-) diff --git a/README.org b/README.org index 3161a4d..89ea764 100644 --- a/README.org +++ b/README.org @@ -527,6 +527,9 @@ Simple links may also be written manually in either sexp or non-sexp form, like: *Fixed* + In dynamic blocks, links to headings with statistics cookies were broken. (Fixes [[https://github.com/alphapapa/org-ql/issues/248][#248]]. Thanks to [[https://github.com/maikol-solis][Maikol Solis]] and [[https://github.com/yantar92][Ihor Radchenko]].) +*Updated* ++ Compatibility with new macro names in Transient. ([[https://github.com/alphapapa/org-ql/pull/269][#269]]. Thanks to [[https://github.com/tarsius][Jonas Bernoulli]].) + ** 0.6 *Added* diff --git a/org-ql-view.el b/org-ql-view.el index e571dbf..74f74d9 100644 --- a/org-ql-view.el +++ b/org-ql-view.el @@ -718,7 +718,7 @@ When opened, the link searches the buffer it's opened from." (unless (or value transient--prefix) (message "Unset %s" variable)))) -(define-transient-command org-ql-view-dispatch () +(transient-define-prefix org-ql-view-dispatch () "Show Org QL View dispatcher." [["Edit" ("t" org-ql-view--transient-title) @@ -753,7 +753,7 @@ When opened, the link searches the buffer it's opened from." org-ql-view--format-query (org-ql-view--font-lock-string 'emacs-lisp-mode))))) -(define-infix-command org-ql-view--transient-title () +(transient-define-infix org-ql-view--transient-title () ;; TODO: Add an asterisk or something when the view has been modified but not saved. :description (lambda () (org-ql-view--format-transient-key-value "Title" org-ql-view-title)) :class 'org-ql-view--variable @@ -766,7 +766,7 @@ When opened, the link searches the buffer it's opened from." (format "%s" org-ql-view-title)) history))) -(define-infix-command org-ql-view--transient-query () +(transient-define-infix org-ql-view--transient-query () :description (lambda () (org-ql-view--format-transient-lisp-argument "Query" org-ql-view-query)) :class 'org-ql-view--variable :argument "" @@ -784,7 +784,7 @@ When opened, the link searches the buffer it's opened from." ;; Parse non-sexp query into sexp query. (org-ql--query-string-to-sexp query))))) -(define-infix-command org-ql-view--transient-in () +(transient-define-infix org-ql-view--transient-in () :description (lambda () (org-ql-view--format-transient-lisp-argument "In buffers/files" org-ql-view-buffers-files)) :class 'org-ql-view--variable :argument "" @@ -795,7 +795,7 @@ When opened, the link searches the buffer it's opened from." ;; argument, but it gives the same result. (org-ql-view--complete-buffers-files))) -(define-infix-command org-ql-view--transient-super-groups () +(transient-define-infix org-ql-view--transient-super-groups () :description (lambda () (org-ql-view--format-transient-lisp-argument "Group by" org-ql-view-super-groups)) :class 'org-ql-view--variable @@ -806,7 +806,7 @@ When opened, the link searches the buffer it's opened from." ;; FIXME: Figure out how to integrate initial-input and history. (org-ql-view--complete-super-groups))) -(define-infix-command org-ql-view--transient-sort () +(transient-define-infix org-ql-view--transient-sort () :description (lambda () (org-ql-view--format-transient-lisp-argument "Sort by" (or org-ql-view-sort 'buffer-order))) diff --git a/org-ql.info b/org-ql.info index 8349fd0..7e1a4cf 100644 --- a/org-ql.info +++ b/org-ql.info @@ -995,6 +995,11 @@ File: README.info, Node: 061-pre, Next: 06, Up: Changelog Solis (https://github.com/maikol-solis) and Ihor Radchenko (https://github.com/yantar92).) + *Updated* + • Compatibility with new macro names in Transient. (#269 + (https://github.com/alphapapa/org-ql/pull/269). Thanks to Jonas + Bernoulli (https://github.com/tarsius).) +  File: README.info, Node: 06, Next: 052, Prev: 061-pre, Up: Changelog @@ -1600,32 +1605,32 @@ Node: Links35371 Node: Tips36058 Node: Changelog36376 Node: 061-pre37117 -Node: 0637492 -Node: 05240507 -Node: 05140807 -Node: 0541224 -Node: 04942698 -Node: 04842972 -Node: 04743319 -Node: 04643714 -Node: 04544114 -Node: 04444475 -Node: 04344834 -Node: 04245031 -Node: 04145192 -Node: 0445433 -Node: 03249366 -Node: 03149745 -Node: 0349942 -Node: 02352917 -Node: 02253145 -Node: 02153413 -Node: 0253612 -Node: 0157647 -Node: Notes57748 -Node: Comparison with Org Agenda searches57910 -Node: org-sidebar58782 -Node: License59061 +Node: 0637685 +Node: 05240700 +Node: 05141000 +Node: 0541417 +Node: 04942891 +Node: 04843165 +Node: 04743512 +Node: 04643907 +Node: 04544307 +Node: 04444668 +Node: 04345027 +Node: 04245224 +Node: 04145385 +Node: 0445626 +Node: 03249559 +Node: 03149938 +Node: 0350135 +Node: 02353110 +Node: 02253338 +Node: 02153606 +Node: 0253805 +Node: 0157840 +Node: Notes57941 +Node: Comparison with Org Agenda searches58103 +Node: org-sidebar58975 +Node: License59254  End Tag Table From f666fe150f1bf3c3ce43d0e15f90b20d43c2e772 Mon Sep 17 00:00:00 2001 From: Adam Porter Date: Fri, 18 Mar 2022 10:08:00 -0500 Subject: [PATCH 04/40] Release: 0.6.1 --- README.org | 2 +- helm-org-ql.el | 2 +- org-ql.el | 2 +- org-ql.info | 118 ++++++++++++++++++++++++------------------------- 4 files changed, 62 insertions(+), 62 deletions(-) diff --git a/README.org b/README.org index 89ea764..c1a383c 100644 --- a/README.org +++ b/README.org @@ -522,7 +522,7 @@ Simple links may also be written manually in either sexp or non-sexp form, like: /Note:/ Breaking changes may be made before version 1.0, but in the event of major changes, attempts at backward compatibility will be made with obsolescence declarations, translation of arguments, etc. Users who need stability guarantees before 1.0 may choose to use tagged stable releases. -** 0.6.1-pre +** 0.6.1 *Fixed* + In dynamic blocks, links to headings with statistics cookies were broken. (Fixes [[https://github.com/alphapapa/org-ql/issues/248][#248]]. Thanks to [[https://github.com/maikol-solis][Maikol Solis]] and [[https://github.com/yantar92][Ihor Radchenko]].) diff --git a/helm-org-ql.el b/helm-org-ql.el index 0ab72ec..38b9722 100644 --- a/helm-org-ql.el +++ b/helm-org-ql.el @@ -2,7 +2,7 @@ ;; Author: Adam Porter ;; URL: https://github.com/alphapapa/org-ql -;; Version: 0.6.1-pre +;; Version: 0.6.1 ;; Package-Requires: ((emacs "26.1") (dash "2.18.1") (s "1.12.0") (helm-org "1.0") (org-ql "0.6-pre")) ;;; Commentary: diff --git a/org-ql.el b/org-ql.el index f1ede36..6cb3ead 100644 --- a/org-ql.el +++ b/org-ql.el @@ -2,7 +2,7 @@ ;; Author: Adam Porter ;; Url: https://github.com/alphapapa/org-ql -;; Version: 0.6.1-pre +;; Version: 0.6.1 ;; Package-Requires: ((emacs "26.1") (dash "2.18.1") (f "0.17.2") (map "2.1") (org "9.0") (org-super-agenda "1.2") (ov "1.0.6") (peg "1.0") (s "1.12.0") (transient "0.1") (ts "0.2-pre")) ;; Keywords: hypermedia, outlines, Org, agenda diff --git a/org-ql.info b/org-ql.info index 7e1a4cf..b970f67 100644 --- a/org-ql.info +++ b/org-ql.info @@ -69,7 +69,7 @@ Functions / Macros Changelog -* 0.6.1-pre: 061-pre. +* 0.6.1: 061. * 0.6: 06. * 0.5.2: 052. * 0.5.1: 051. @@ -958,7 +958,7 @@ releases. * Menu: -* 0.6.1-pre: 061-pre. +* 0.6.1: 061. * 0.6: 06. * 0.5.2: 052. * 0.5.1: 051. @@ -983,10 +983,10 @@ releases. * 0.1: 01.  -File: README.info, Node: 061-pre, Next: 06, Up: Changelog +File: README.info, Node: 061, Next: 06, Up: Changelog -5.1 0.6.1-pre -============= +5.1 0.6.1 +========= *Fixed* • In dynamic blocks, links to headings with statistics cookies were @@ -1001,7 +1001,7 @@ File: README.info, Node: 061-pre, Next: 06, Up: Changelog Bernoulli (https://github.com/tarsius).)  -File: README.info, Node: 06, Next: 052, Prev: 061-pre, Up: Changelog +File: README.info, Node: 06, Next: 052, Prev: 061, Up: Changelog 5.2 0.6 ======= @@ -1578,59 +1578,59 @@ GPLv3  Tag Table: Node: Top225 -Node: Contents1712 -Node: Screenshots1835 -Node: Installation1953 -Node: Quelpa2449 -Node: Helm support2977 -Node: Usage3368 -Node: Commands3766 -Node: org-ql-search4239 -Node: helm-org-ql5889 -Node: org-ql-view6249 -Node: org-ql-view-sidebar6749 -Node: org-ql-view-recent-items7105 -Node: org-ql-sparse-tree7589 -Node: Queries8389 -Node: Non-sexp query syntax9500 -Node: General predicates11224 -Node: Ancestor/descendant predicates17445 -Node: Date/time predicates18573 -Node: Functions / Macros21661 -Node: Agenda-like views21959 -Node: Listing / acting-on results23364 -Node: Custom predicates29000 -Node: Dynamic block32659 -Node: Links35371 -Node: Tips36058 -Node: Changelog36376 -Node: 061-pre37117 -Node: 0637685 -Node: 05240700 -Node: 05141000 -Node: 0541417 -Node: 04942891 -Node: 04843165 -Node: 04743512 -Node: 04643907 -Node: 04544307 -Node: 04444668 -Node: 04345027 -Node: 04245224 -Node: 04145385 -Node: 0445626 -Node: 03249559 -Node: 03149938 -Node: 0350135 -Node: 02353110 -Node: 02253338 -Node: 02153606 -Node: 0253805 -Node: 0157840 -Node: Notes57941 -Node: Comparison with Org Agenda searches58103 -Node: org-sidebar58975 -Node: License59254 +Node: Contents1704 +Node: Screenshots1827 +Node: Installation1945 +Node: Quelpa2441 +Node: Helm support2969 +Node: Usage3360 +Node: Commands3758 +Node: org-ql-search4231 +Node: helm-org-ql5881 +Node: org-ql-view6241 +Node: org-ql-view-sidebar6741 +Node: org-ql-view-recent-items7097 +Node: org-ql-sparse-tree7581 +Node: Queries8381 +Node: Non-sexp query syntax9492 +Node: General predicates11216 +Node: Ancestor/descendant predicates17437 +Node: Date/time predicates18565 +Node: Functions / Macros21653 +Node: Agenda-like views21951 +Node: Listing / acting-on results23356 +Node: Custom predicates28992 +Node: Dynamic block32651 +Node: Links35363 +Node: Tips36050 +Node: Changelog36368 +Node: 06137101 +Node: 0637657 +Node: 05240668 +Node: 05140968 +Node: 0541385 +Node: 04942859 +Node: 04843133 +Node: 04743480 +Node: 04643875 +Node: 04544275 +Node: 04444636 +Node: 04344995 +Node: 04245192 +Node: 04145353 +Node: 0445594 +Node: 03249527 +Node: 03149906 +Node: 0350103 +Node: 02353078 +Node: 02253306 +Node: 02153574 +Node: 0253773 +Node: 0157808 +Node: Notes57909 +Node: Comparison with Org Agenda searches58071 +Node: org-sidebar58943 +Node: License59222  End Tag Table From 46f523d94a376b168176c75bbd0e3e0d00e61170 Mon Sep 17 00:00:00 2001 From: Adam Porter Date: Fri, 18 Mar 2022 10:34:54 -0500 Subject: [PATCH 05/40] Meta: 0.7-pre --- README.org | 4 ++ org-ql.el | 2 +- org-ql.info | 167 ++++++++++++++++++++++++++++------------------------ 3 files changed, 94 insertions(+), 79 deletions(-) diff --git a/README.org b/README.org index c1a383c..c6e4daa 100644 --- a/README.org +++ b/README.org @@ -522,6 +522,10 @@ Simple links may also be written manually in either sexp or non-sexp form, like: /Note:/ Breaking changes may be made before version 1.0, but in the event of major changes, attempts at backward compatibility will be made with obsolescence declarations, translation of arguments, etc. Users who need stability guarantees before 1.0 may choose to use tagged stable releases. +** 0.7-pre + +Nothing new yet. + ** 0.6.1 *Fixed* diff --git a/org-ql.el b/org-ql.el index 6cb3ead..ffed560 100644 --- a/org-ql.el +++ b/org-ql.el @@ -2,7 +2,7 @@ ;; Author: Adam Porter ;; Url: https://github.com/alphapapa/org-ql -;; Version: 0.6.1 +;; Version: 0.7-pre ;; Package-Requires: ((emacs "26.1") (dash "2.18.1") (f "0.17.2") (map "2.1") (org "9.0") (org-super-agenda "1.2") (ov "1.0.6") (peg "1.0") (s "1.12.0") (transient "0.1") (ts "0.2-pre")) ;; Keywords: hypermedia, outlines, Org, agenda diff --git a/org-ql.info b/org-ql.info index b970f67..84895de 100644 --- a/org-ql.info +++ b/org-ql.info @@ -69,6 +69,7 @@ Functions / Macros Changelog +* 0.7-pre: 07-pre. * 0.6.1: 061. * 0.6: 06. * 0.5.2: 052. @@ -958,6 +959,7 @@ releases. * Menu: +* 0.7-pre: 07-pre. * 0.6.1: 061. * 0.6: 06. * 0.5.2: 052. @@ -983,9 +985,17 @@ releases. * 0.1: 01.  -File: README.info, Node: 061, Next: 06, Up: Changelog +File: README.info, Node: 07-pre, Next: 061, Up: Changelog -5.1 0.6.1 +5.1 0.7-pre +=========== + +Nothing new yet. + + +File: README.info, Node: 061, Next: 06, Prev: 07-pre, Up: Changelog + +5.2 0.6.1 ========= *Fixed* @@ -1003,7 +1013,7 @@ File: README.info, Node: 061, Next: 06, Up: Changelog  File: README.info, Node: 06, Next: 052, Prev: 061, Up: Changelog -5.2 0.6 +5.3 0.6 ======= *Added* @@ -1070,7 +1080,7 @@ File: README.info, Node: 06, Next: 052, Prev: 061, Up: Changelog  File: README.info, Node: 052, Next: 051, Prev: 06, Up: Changelog -5.3 0.5.2 +5.4 0.5.2 ========= *Fixed* @@ -1081,7 +1091,7 @@ File: README.info, Node: 052, Next: 051, Prev: 06, Up: Changelog  File: README.info, Node: 051, Next: 05, Prev: 052, Up: Changelog -5.4 0.5.1 +5.5 0.5.1 ========= *Fixed* @@ -1094,7 +1104,7 @@ File: README.info, Node: 051, Next: 05, Prev: 052, Up: Changelog  File: README.info, Node: 05, Next: 049, Prev: 051, Up: Changelog -5.5 0.5 +5.6 0.5 ======= *Added* @@ -1135,7 +1145,7 @@ File: README.info, Node: 05, Next: 049, Prev: 051, Up: Changelog  File: README.info, Node: 049, Next: 048, Prev: 05, Up: Changelog -5.6 0.4.9 +5.7 0.4.9 ========= *Fixed* @@ -1146,7 +1156,7 @@ File: README.info, Node: 049, Next: 048, Prev: 05, Up: Changelog  File: README.info, Node: 048, Next: 047, Prev: 049, Up: Changelog -5.7 0.4.8 +5.8 0.4.8 ========= *Fixed* @@ -1158,7 +1168,7 @@ File: README.info, Node: 048, Next: 047, Prev: 049, Up: Changelog  File: README.info, Node: 047, Next: 046, Prev: 048, Up: Changelog -5.8 0.4.7 +5.9 0.4.7 ========= *Fixed* @@ -1171,8 +1181,8 @@ File: README.info, Node: 047, Next: 046, Prev: 048, Up: Changelog  File: README.info, Node: 046, Next: 045, Prev: 047, Up: Changelog -5.9 0.4.6 -========= +5.10 0.4.6 +========== *Fixed* • Compatibility with newer versions of the peg library, which removed @@ -1184,7 +1194,7 @@ File: README.info, Node: 046, Next: 045, Prev: 047, Up: Changelog  File: README.info, Node: 045, Next: 044, Prev: 046, Up: Changelog -5.10 0.4.5 +5.11 0.4.5 ========== *Fixed* @@ -1196,7 +1206,7 @@ File: README.info, Node: 045, Next: 044, Prev: 046, Up: Changelog  File: README.info, Node: 044, Next: 043, Prev: 045, Up: Changelog -5.11 0.4.4 +5.12 0.4.4 ========== *Fixed* @@ -1208,7 +1218,7 @@ File: README.info, Node: 044, Next: 043, Prev: 045, Up: Changelog  File: README.info, Node: 043, Next: 042, Prev: 044, Up: Changelog -5.12 0.4.3 +5.13 0.4.3 ========== *Fixed* @@ -1218,7 +1228,7 @@ File: README.info, Node: 043, Next: 042, Prev: 044, Up: Changelog  File: README.info, Node: 042, Next: 041, Prev: 043, Up: Changelog -5.13 0.4.2 +5.14 0.4.2 ========== *Fixed* @@ -1227,7 +1237,7 @@ File: README.info, Node: 042, Next: 041, Prev: 043, Up: Changelog  File: README.info, Node: 041, Next: 04, Prev: 042, Up: Changelog -5.14 0.4.1 +5.15 0.4.1 ========== *Fixed* @@ -1237,7 +1247,7 @@ File: README.info, Node: 041, Next: 04, Prev: 042, Up: Changelog  File: README.info, Node: 04, Next: 032, Prev: 041, Up: Changelog -5.15 0.4 +5.16 0.4 ======== _Note:_ The next release, 0.5, may include changes which will require @@ -1318,7 +1328,7 @@ as they will be pushed to the master branch when ready.  File: README.info, Node: 032, Next: 031, Prev: 04, Up: Changelog -5.16 0.3.2 +5.17 0.3.2 ========== *Fixed* @@ -1331,7 +1341,7 @@ File: README.info, Node: 032, Next: 031, Prev: 04, Up: Changelog  File: README.info, Node: 031, Next: 03, Prev: 032, Up: Changelog -5.17 0.3.1 +5.18 0.3.1 ========== *Fixed* @@ -1341,7 +1351,7 @@ File: README.info, Node: 031, Next: 03, Prev: 032, Up: Changelog  File: README.info, Node: 03, Next: 023, Prev: 031, Up: Changelog -5.18 0.3 +5.19 0.3 ======== *Added* @@ -1406,7 +1416,7 @@ File: README.info, Node: 03, Next: 023, Prev: 031, Up: Changelog  File: README.info, Node: 023, Next: 022, Prev: 03, Up: Changelog -5.19 0.2.3 +5.20 0.2.3 ========== *Fixed* @@ -1416,7 +1426,7 @@ File: README.info, Node: 023, Next: 022, Prev: 03, Up: Changelog  File: README.info, Node: 022, Next: 021, Prev: 023, Up: Changelog -5.20 0.2.2 +5.21 0.2.2 ========== *Fixed* @@ -1427,7 +1437,7 @@ File: README.info, Node: 022, Next: 021, Prev: 023, Up: Changelog  File: README.info, Node: 021, Next: 02, Prev: 022, Up: Changelog -5.21 0.2.1 +5.22 0.2.1 ========== *Fixed* @@ -1437,7 +1447,7 @@ File: README.info, Node: 021, Next: 02, Prev: 022, Up: Changelog  File: README.info, Node: 02, Next: 01, Prev: 021, Up: Changelog -5.22 0.2 +5.23 0.2 ======== *Added* @@ -1520,7 +1530,7 @@ File: README.info, Node: 02, Next: 01, Prev: 021, Up: Changelog  File: README.info, Node: 01, Prev: 02, Up: Changelog -5.23 0.1 +5.24 0.1 ======== First tagged release. @@ -1578,59 +1588,60 @@ GPLv3  Tag Table: Node: Top225 -Node: Contents1704 -Node: Screenshots1827 -Node: Installation1945 -Node: Quelpa2441 -Node: Helm support2969 -Node: Usage3360 -Node: Commands3758 -Node: org-ql-search4231 -Node: helm-org-ql5881 -Node: org-ql-view6241 -Node: org-ql-view-sidebar6741 -Node: org-ql-view-recent-items7097 -Node: org-ql-sparse-tree7581 -Node: Queries8381 -Node: Non-sexp query syntax9492 -Node: General predicates11216 -Node: Ancestor/descendant predicates17437 -Node: Date/time predicates18565 -Node: Functions / Macros21653 -Node: Agenda-like views21951 -Node: Listing / acting-on results23356 -Node: Custom predicates28992 -Node: Dynamic block32651 -Node: Links35363 -Node: Tips36050 -Node: Changelog36368 -Node: 06137101 -Node: 0637657 -Node: 05240668 -Node: 05140968 -Node: 0541385 -Node: 04942859 -Node: 04843133 -Node: 04743480 -Node: 04643875 -Node: 04544275 -Node: 04444636 -Node: 04344995 -Node: 04245192 -Node: 04145353 -Node: 0445594 -Node: 03249527 -Node: 03149906 -Node: 0350103 -Node: 02353078 -Node: 02253306 -Node: 02153574 -Node: 0253773 -Node: 0157808 -Node: Notes57909 -Node: Comparison with Org Agenda searches58071 -Node: org-sidebar58943 -Node: License59222 +Node: Contents1724 +Node: Screenshots1847 +Node: Installation1965 +Node: Quelpa2461 +Node: Helm support2989 +Node: Usage3380 +Node: Commands3778 +Node: org-ql-search4251 +Node: helm-org-ql5901 +Node: org-ql-view6261 +Node: org-ql-view-sidebar6761 +Node: org-ql-view-recent-items7117 +Node: org-ql-sparse-tree7601 +Node: Queries8401 +Node: Non-sexp query syntax9512 +Node: General predicates11236 +Node: Ancestor/descendant predicates17457 +Node: Date/time predicates18585 +Node: Functions / Macros21673 +Node: Agenda-like views21971 +Node: Listing / acting-on results23376 +Node: Custom predicates29012 +Node: Dynamic block32671 +Node: Links35383 +Node: Tips36070 +Node: Changelog36388 +Node: 07-pre37141 +Node: 06137248 +Node: 0637819 +Node: 05240830 +Node: 05141130 +Node: 0541547 +Node: 04943021 +Node: 04843295 +Node: 04743642 +Node: 04644037 +Node: 04544439 +Node: 04444800 +Node: 04345159 +Node: 04245356 +Node: 04145517 +Node: 0445758 +Node: 03249691 +Node: 03150070 +Node: 0350267 +Node: 02353242 +Node: 02253470 +Node: 02153738 +Node: 0253937 +Node: 0157972 +Node: Notes58073 +Node: Comparison with Org Agenda searches58235 +Node: org-sidebar59107 +Node: License59386  End Tag Table From 115500c8a0a3190ec6a69d1120fe81944d819125 Mon Sep 17 00:00:00 2001 From: Adam Porter Date: Fri, 27 May 2022 02:01:00 -0500 Subject: [PATCH 06/40] Fix: (link) Save excursion around regexp search Fixes #279. Thanks to Marc Fargas (@telenieko) for reporting. Released as 0.6.2. --- README.org | 5 + org-ql.el | 5 +- org-ql.info | 514 +++++++++++++++++++++++++++------------------------- 3 files changed, 272 insertions(+), 252 deletions(-) diff --git a/README.org b/README.org index c1a383c..a31d224 100644 --- a/README.org +++ b/README.org @@ -522,6 +522,11 @@ Simple links may also be written manually in either sexp or non-sexp form, like: /Note:/ Breaking changes may be made before version 1.0, but in the event of major changes, attempts at backward compatibility will be made with obsolescence declarations, translation of arguments, etc. Users who need stability guarantees before 1.0 may choose to use tagged stable releases. +** 0.6.2 + +*Fixed* ++ ~link~ predicate when used in an ~or~'ed query. ([[https://github.com/alphapapa/org-ql/issues/279][#279]]. Thanks to [[https://github.com/telenieko][Marc Fargas]] for reporting.) + ** 0.6.1 *Fixed* diff --git a/org-ql.el b/org-ql.el index 6cb3ead..18ccf74 100644 --- a/org-ql.el +++ b/org-ql.el @@ -2,7 +2,7 @@ ;; Author: Adam Porter ;; Url: https://github.com/alphapapa/org-ql -;; Version: 0.6.1 +;; Version: 0.6.2 ;; Package-Requires: ((emacs "26.1") (dash "2.18.1") (f "0.17.2") (map "2.1") (org "9.0") (org-super-agenda "1.2") (ov "1.0.6") (peg "1.0") (s "1.12.0") (transient "0.1") (ts "0.2-pre")) ;; Keywords: hypermedia, outlines, Org, agenda @@ -1461,7 +1461,8 @@ any link is found." (setf description (regexp-quote description))) (when target (setf target (regexp-quote target)))) - (when (re-search-forward org-ql-link-regexp (org-entry-end-position) t) + (when (save-excursion + (re-search-forward org-ql-link-regexp (org-entry-end-position) t)) (pcase description-or-target ('nil (and (or (null target) (string-match-p target (match-string 1))) diff --git a/org-ql.info b/org-ql.info index b970f67..6f2db2b 100644 --- a/org-ql.info +++ b/org-ql.info @@ -11,6 +11,14 @@ File: README.info, Node: Top, Next: Contents, Up: (dir) org-ql ****** +This package provides a query language for Org files. It offers two +syntax styles: Lisp-like sexps and search engine-like keywords. + + It includes three libraries: The ‘org-ql’ library is flexible and may +be used as a backend for other tools. The libraries ‘org-ql-search’ and +‘helm-org-ql’ (a separate package) provide interactive search commands +and saved views. + * Menu: * Contents:: @@ -23,15 +31,11 @@ org-ql — The Detailed Node Listing — - - Installation * Quelpa:: * Helm support:: - - Usage * Commands:: @@ -50,8 +54,6 @@ Commands * org-ql-view-recent-items:: * org-ql-sparse-tree:: - - Queries * Non-sexp query syntax:: @@ -59,8 +61,6 @@ Queries * Ancestor/descendant predicates:: * Date/time predicates:: - - Functions / Macros * Agenda-like views:: @@ -69,6 +69,7 @@ Functions / Macros Changelog +* 0.6.2: 062. * 0.6.1: 061. * 0.6: 06. * 0.5.2: 052. @@ -93,20 +94,11 @@ Changelog * 0.2: 02. * 0.1: 01. - - Notes * Comparison with Org Agenda searches:: * org-sidebar:: -This package provides a query language for Org files. It offers two -syntax styles: Lisp-like sexps and search engine-like keywords. - - It includes three libraries: The org-ql library is flexible and may -be used as a backend for other tools. The libraries org-ql-search and -helm-org-ql (a separate package) provide interactive search commands and -saved views.  File: README.info, Node: Contents, Next: Screenshots, Prev: Top, Up: Top @@ -127,13 +119,13 @@ File: README.info, Node: Installation, Next: Usage, Prev: Screenshots, Up: T 3 Installation ************** -The package org-ql may be installed directly from MELPA +The package ‘org-ql’ may be installed directly from MELPA (https://melpa.org/#/org-ql) or with other tools like Quelpa (https://framagit.org/steckerhalter/quelpa). After installation, you can use the commands without additional configuration. To use the functions and macros in your own Elisp code, -use libraries org-ql and org-ql-view. +use libraries ‘org-ql’ and ‘org-ql-view’. * Menu: @@ -164,8 +156,8 @@ File: README.info, Node: Helm support, Prev: Quelpa, Up: Installation 3.2 Helm support ================ -The command helm-org-ql is available in the package helm-org-ql. It may -be installed from MELPA, or with Quelpa, like so: +The command ‘helm-org-ql’ is available in the package ‘helm-org-ql’. It +may be installed from MELPA, or with Quelpa, like so: (use-package helm-org-ql :quelpa (helm-org-ql :fetcher github :repo "alphapapa/org-ql" @@ -248,10 +240,10 @@ Interactively, with prefix, leave narrowed. ‘priority’. *Bindings:* Keys bound in results buffer. - • r: Refresh results. With prefix, prompt to adjust search + • ‘r’: Refresh results. With prefix, prompt to adjust search parameters. - • v: Show transient view dispatcher (like Magit’s popups). - • C-x C-s: Save query to variable ‘org-ql-views’ (accessible with + • ‘v’: Show ‘transient’ view dispatcher (like Magit’s popups). + • ‘C-x C-s’: Save query to variable ‘org-ql-views’ (accessible with command ‘org-ql-view’). *Note:* The view buffer is currently put in ‘org-agenda-mode’, which @@ -268,12 +260,12 @@ File: README.info, Node: helm-org-ql, Next: org-ql-view, Prev: org-ql-search, ----------------- _Note: This command uses . It is available separately in the package -helm-org-ql._ +‘helm-org-ql’._ This command displays matches with Helm. - • Press C-x C-s in the Helm session to save the results to an - org-ql-search buffer. + • Press ‘C-x C-s’ in the Helm session to save the results to an + ‘org-ql-search’ buffer.  File: README.info, Node: org-ql-view, Next: org-ql-view-sidebar, Prev: helm-org-ql, Up: Commands @@ -284,10 +276,10 @@ File: README.info, Node: org-ql-view, Next: org-ql-view-sidebar, Prev: helm-o Choose and display a view stored in ‘org-ql-views’. *Bindings:* Keys bound in view buffer. - • g, r: Refresh results. With prefix, prompt to adjust search + • ‘g’, ‘r’: Refresh results. With prefix, prompt to adjust search parameters. - • v: Show transient view dispatcher (like Magit’s popups). - • C-x C-s: Save query to variable ‘org-ql-views’ (accessible with + • ‘v’: Show ‘transient’ view dispatcher (like Magit’s popups). + • ‘C-x C-s’: Save query to variable ‘org-ql-views’ (accessible with command ‘org-ql-view’).  @@ -296,9 +288,9 @@ File: README.info, Node: org-ql-view-sidebar, Next: org-ql-view-recent-items, 4.1.4 org-ql-view-sidebar ------------------------- -Show a sidebar window listing views stored in org-ql-views for easy -access. In the sidebar, press RET or mouse-1 to show the view at point, -and press c to customize the view at point. +Show a sidebar window listing views stored in ‘org-ql-views’ for easy +access. In the sidebar, press ‘RET’ or ‘mouse-1’ to show the view at +point, and press ‘c’ to customize the view at point.  File: README.info, Node: org-ql-view-recent-items, Next: org-ql-sparse-tree, Prev: org-ql-view-sidebar, Up: Commands @@ -308,8 +300,8 @@ File: README.info, Node: org-ql-view-recent-items, Next: org-ql-sparse-tree, Show items in ‘FILES’ from last ‘DAYS’ days with timestamps of ‘TYPE’. ‘TYPE’ may be ‘ts’, ‘ts-active’, ‘ts-inactive’, ‘clocked’, ‘closed’, -‘deadline’, ‘planning’, or ‘scheduled’. FILES defaults to those -returned by the function org-agenda-files. +‘deadline’, ‘planning’, or ‘scheduled’. ‘FILES’ defaults to those +returned by the function ‘org-agenda-files’.  File: README.info, Node: org-ql-sparse-tree, Prev: org-ql-view-recent-items, Up: Commands @@ -336,7 +328,7 @@ File: README.info, Node: Queries, Next: Functions / Macros, Prev: Commands, =========== • • • • - An org-ql query is a Lisp expression which may contain arbitrary + An ‘org-ql’ query is a Lisp expression which may contain arbitrary expressions, as well as calling certain built-in predicates. It is byte-compiled into a predicate function which is tested with point on each heading in an Org buffer; when it returns non-nil, the heading @@ -365,11 +357,11 @@ File: README.info, Node: Non-sexp query syntax, Next: General predicates, Up: 4.2.1 Non-sexp query syntax --------------------------- -The command org-ql-search also accepts, and the command helm-org-ql only -accepts, an alternative, non-sexp query syntax. The syntax is simple, -and a few examples of queries in both syntaxes should suffice. By -default, when multiple predicates are used, they are combined with -boolean and. +The command ‘org-ql-search’ also accepts, and the command ‘helm-org-ql’ +only accepts, an alternative, non-sexp query syntax. The syntax is +simple, and a few examples of queries in both syntaxes should suffice. +By default, when multiple predicates are used, they are combined with +boolean ‘and’. Sexp syntax Non-sexp syntax ------------------------------------------------------------------------------------------------------- @@ -385,9 +377,9 @@ Sexp syntax Non-sexp syntax ‘(and (tags "space") (not (regexp "moon")))’ ‘tags:space !moon’ ‘(priority >= B)’ ‘priority:A,B’ - Note that the effort, level, and priority predicates do not support -comparators in the non-sexp syntax, so multiple arguments should be -passed instead, as seen in the last example. + Note that the ‘effort’, ‘level’, and ‘priority’ predicates do not +support comparators in the non-sexp syntax, so multiple arguments should +be passed instead, as seen in the last example.  File: README.info, Node: General predicates, Next: Ancestor/descendant predicates, Prev: Non-sexp query syntax, Up: Queries @@ -409,14 +401,14 @@ Arguments are listed next to predicate names, where applicable. Matches if effort is between DURATIONs, inclusive. ‘(effort COMPARATOR DURATION)’: Matches if effort compares to ‘DURATION’ with ‘COMPARATOR’. ‘COMPARATOR’ may be ‘<’, ‘<=’, ‘>’, or ‘>=’. - ‘DURATION’ should be an Org effort string, like 5 or 0:05. + ‘DURATION’ should be an Org effort string, like ‘5’ or ‘0:05’. ‘habit’ Return non-nil if entry is a habit. ‘heading (&rest strings)’ Return non-nil if current entry’s heading matches all ‘STRINGS’. Matching is done case-insensitively. - • Aliases: h. -‘‘heading-regexp (&rest regexps)’’ + • Aliases: ‘h’. +‘heading-regexp (&rest regexps)’ Return non-nil if current entry’s heading matches all ‘REGEXPS’ (regexp strings). Matching is done case-insensitively. • Aliases: ‘h*’. @@ -436,8 +428,8 @@ Arguments are listed next to predicate names, where applicable. ‘outline-path (&rest strings)’ Return non-nil if current node’s outline path matches all of ‘STRINGS’. Each string may appear as a substring in any part of - the node’s outline path. For example, the path Food/Fruits/Grapes - would match ‘(olp "Fruit" "Grape")’. + the node’s outline path. For example, the path + ‘Food/Fruits/Grapes’ would match ‘(olp "Fruit" "Grape")’. • Aliases: ‘olp’. ‘outline-path-segment (&rest strings)’ Return non-nil if current node’s outline path matches ‘STRINGS’. @@ -469,8 +461,8 @@ Arguments are listed next to predicate names, where applicable. Return non-nil if current entry matches all of ‘REGEXPS’ (regexp strings). Matches against entire entry, from beginning of its heading to the next heading. - • Aliases: r. -‘‘src (&key lang regexps)’’ + • Aliases: ‘r’. +‘src (&key lang regexps)’ Return non-nil if current entry contains an Org Babel source block. If ‘LANG’ is non-nil, match blocks of that language. If ‘REGEXPS’ is non-nil, require that block’s contents match all regexps. @@ -552,10 +544,10 @@ to look forward, or negative to look backward), a string parseable by Return non-nil if current entry has a timestamp in given period. Without arguments, return non-nil if entry has a timestamp. - ‘ts-active, ts-a’ - Like ts, but only matches active timestamps. - ‘ts-inactive, ts-i’ - Like ts, but only matches inactive timestamps. + ‘ts-active’, ‘ts-a’ + Like ‘ts’, but only matches active timestamps. + ‘ts-inactive’, ‘ts-i’ + Like ‘ts’, but only matches inactive timestamps. The following predicates, in addition to the keyword arguments, can also take a single argument, a number, which looks backward or forward a @@ -583,8 +575,8 @@ searching for items planned in the next few days: • *Forward-looking* ‘deadline’ Return non-nil if current entry has deadline in given period. - If argument is auto, return non-nil if entry has deadline - within org-deadline-warning-days. Without arguments, return + If argument is ‘auto’, return non-nil if entry has deadline + within ‘org-deadline-warning-days’. Without arguments, return non-nil if entry has any deadline. ‘planning’ Return non-nil if current entry has planning timestamp (i.e. @@ -622,7 +614,7 @@ File: README.info, Node: Agenda-like views, Next: Listing / acting-on results, For use as a custom agenda block type in ‘org-agenda-custom-commands’. For example, you could define a custom series command like this, which would list all priority A - items tagged Emacs with to-do keyword SOMEDAY, followed by the + items tagged ‘Emacs’ with to-do keyword ‘SOMEDAY’, followed by the standard agenda view, in a single buffer: (setq org-agenda-custom-commands @@ -642,8 +634,8 @@ File: README.info, Node: Agenda-like views, Next: Listing / acting-on results, However, the ‘org-ql-block’ version runs in about 1/5th the time. - The variable org-ql-block-header may be bound to a string to use as - the block header, otherwise the header is formed automatically. + The variable ‘org-ql-block-header’ may be bound to a string to use + as the block header, otherwise the header is formed automatically.  File: README.info, Node: Listing / acting-on results, Next: Custom predicates, Prev: Agenda-like views, Up: Functions / Macros @@ -791,7 +783,7 @@ File: README.info, Node: Custom predicates, Prev: Listing / acting-on results, • See: Custom predicate tutorial (examples/defpred.org) - 1. Macro: org-ql-defpred + 1. Macro: ‘org-ql-defpred’ _Arguments:_ ‘(name args docstring &key body preambles normalizers)’ @@ -880,9 +872,9 @@ supported: • Each column may also be specified as a list with the second element being a header string. For example, to abbreviate the priority column: ‘(priority "P")’. - • For certain columns, like property, arguments may be passed by - specifying the column type itself as a list. For example, to - display a column showing the values of a ‘property’ named + • For certain columns, like ‘property’, arguments may be passed + by specifying the column type itself as a list. For example, + to display a column showing the values of a ‘property’ named ‘milestone’, with the header being abbreviated to ‘M’: ‘((property "milestone") "M")’. • ‘:sort’ One or a list of Org QL sorting methods (see @@ -897,7 +889,7 @@ in the following example). For example, this dynamic block shows the first seven headings that are to-do items with priority A or B, sorted by deadline then priority, -with certain columns (including the value of the agenda-group property +with certain columns (including the value of the ‘agenda-group’ property with a custom header) and timestamp format: #+BEGIN: org-ql :query "todo: priority:A,B" :columns (todo (priority "P") ((property "agenda-group") "Group") deadline heading) :sort (deadline priority) :take 7 :ts-format "%Y-%m-%d %H:%M" @@ -940,7 +932,7 @@ File: README.info, Node: Tips, Prev: Links, Up: Usage ======== • Org QL View buffers can be bookmarked with Emacs bookmark commands, - e.g. C-x r m. This also integrates with org-sidebar + e.g. ‘C-x r m’. This also integrates with org-sidebar (https://github.com/alphapapa/org-sidebar) and Burly (https://github.com/alphapapa/burly.el). @@ -958,6 +950,7 @@ releases. * Menu: +* 0.6.2: 062. * 0.6.1: 061. * 0.6: 06. * 0.5.2: 052. @@ -983,9 +976,20 @@ releases. * 0.1: 01.  -File: README.info, Node: 061, Next: 06, Up: Changelog +File: README.info, Node: 062, Next: 061, Up: Changelog -5.1 0.6.1 +5.1 0.6.2 +========= + +*Fixed* + • ‘link’ predicate when used in an ‘or’’ed query. (#279 + (https://github.com/alphapapa/org-ql/issues/279). Thanks to Marc + Fargas (https://github.com/telenieko) for reporting.) + + +File: README.info, Node: 061, Next: 06, Prev: 062, Up: Changelog + +5.2 0.6.1 ========= *Fixed* @@ -1003,11 +1007,11 @@ File: README.info, Node: 061, Next: 06, Up: Changelog  File: README.info, Node: 06, Next: 052, Prev: 061, Up: Changelog -5.2 0.6 +5.3 0.6 ======= *Added* - • Macro org-ql-defpred, used to define search predicates. (See + • Macro ‘org-ql-defpred’, used to define search predicates. (See tutorial (examples/defpred.org).) • Predicate ‘effort’. • Predicate ‘heading-regexp’, which matches regular expressions @@ -1028,21 +1032,21 @@ File: README.info, Node: 06, Next: 052, Prev: 061, Up: Changelog For example, ‘:sort '(todo priority date)’ now does what ‘:sort '(date priority todo)’ did in earlier versions. (This change is made to enable the new ‘reverse’ sorting method.) Users who have - customized org-ql-views will need to update the stored views’ + customized ‘org-ql-views’ will need to update the stored views’ sorting methods to preserve the desired sort order. - • Helm support (including the command helm-org-ql) has been moved to - a separate package, helm-org-ql. + • Helm support (including the command ‘helm-org-ql’) has been moved + to a separate package, ‘helm-org-ql’. • Predicate ‘heading’ now matches plain strings instead of regular expressions. - • Update dash dependency, and remove dependency on obsolete - dash-functional. (Fixes #179 + • Update ‘dash’ dependency, and remove dependency on obsolete + ‘dash-functional’. (Fixes #179 (https://github.com/alphapapa/org-ql/issues/179), #209 (https://github.com/alphapapa/org-ql/issues/209). Thanks to Mark Hudnall (https://github.com/landakram), Akira Komamura (https://github.com/akirak), Nathanael kinfe (https://github.com/natask), Pablo Stafforini (https://github.com/benthamite), Jason May - (https://github.com/jmay), and Basil L. Contovounesios + (https://github.com/jmay), and Basil L. Contovounesios (https://github.com/basil-conto).) *Removed* @@ -1057,7 +1061,7 @@ File: README.info, Node: 06, Next: 052, Prev: 061, Up: Changelog *Internal* • Predicates are now defined more cleanly with a macro - (org-ql-defpred) that consolidates functionality related to each + (‘org-ql-defpred’) that consolidates functionality related to each predicate. This will also allow users to more easily define custom predicates. • Version 1.0 of library ‘peg’ is now required. @@ -1070,7 +1074,7 @@ File: README.info, Node: 06, Next: 052, Prev: 061, Up: Changelog  File: README.info, Node: 052, Next: 051, Prev: 06, Up: Changelog -5.3 0.5.2 +5.4 0.5.2 ========= *Fixed* @@ -1081,12 +1085,12 @@ File: README.info, Node: 052, Next: 051, Prev: 06, Up: Changelog  File: README.info, Node: 051, Next: 05, Prev: 052, Up: Changelog -5.4 0.5.1 +5.5 0.5.1 ========= *Fixed* • Custom sorting functions could corrupt the cache, causing items to - disappear after refreshing an org-ql-search buffer. (#186 + disappear after refreshing an ‘org-ql-search’ buffer. (#186 (https://github.com/alphapapa/org-ql/issues/186), #187 (https://github.com/alphapapa/org-ql/issues/187). Thanks to Nathanael kinfe (https://github.com/natask).) @@ -1094,19 +1098,19 @@ File: README.info, Node: 051, Next: 05, Prev: 052, Up: Changelog  File: README.info, Node: 05, Next: 049, Prev: 051, Up: Changelog -5.5 0.5 +5.6 0.5 ======= *Added* - • View dispatcher using transient.el (like Magit), bound to v in + • View dispatcher using ‘transient.el’ (like Magit), bound to ‘v’ in search/view buffers. - • Predicate link, which matches descriptions and targets in Org + • Predicate ‘link’, which matches descriptions and targets in Org links. • Predicate ‘tags-regexp’ (alias: ‘tags*’), which matches regexps against entry tags (e.g, helpful when a tag might end in "s"). • Emacs bookmark support: Org QL View buffers can be bookmarked with, - e.g. C-x r m and shown with, e.g. C-x r b. (This also enables - view restoration with Burly + e.g. ‘C-x r m’ and shown with, e.g. ‘C-x r b’. (This also + enables view restoration with Burly (https://github.com/alphapapa/burly.el).) • Dynamic block support. • Org link support (storing and opening links to Org QL View @@ -1114,7 +1118,7 @@ File: README.info, Node: 05, Next: 049, Prev: 051, Up: Changelog • Mascot. *Changed* - • Binding to refresh search/view buffers changed to r. + • Binding to refresh search/view buffers changed to ‘r’. *Internal* • When formatting entries for Org QL View buffers, use internal @@ -1123,9 +1127,9 @@ File: README.info, Node: 05, Next: 049, Prev: 051, Up: Changelog Org versions before 9.3. *Deprecated* - • Macro org-ql is marked obsolete. It will be removed in v0.7. - Functions org-ql-select and org-ql-query should be used instead. - (The macro serves only to confuse with regard to quoting + • Macro ‘org-ql’ is marked obsolete. It will be removed in v0.7. + Functions ‘org-ql-select’ and ‘org-ql-query’ should be used + instead. (The macro serves only to confuse with regard to quoting arguments.) *Acknowledgments* @@ -1135,18 +1139,18 @@ File: README.info, Node: 05, Next: 049, Prev: 051, Up: Changelog  File: README.info, Node: 049, Next: 048, Prev: 05, Up: Changelog -5.6 0.4.9 +5.7 0.4.9 ========= *Fixed* - • Agenda restriction in org-ql-block. (Fixes #84 + • Agenda restriction in ‘org-ql-block’. (Fixes #84 (https://github.com/alphapapa/org-ql/issues/84). Thanks to Ihor Radchenko (https://github.com/yantar92).)  File: README.info, Node: 048, Next: 047, Prev: 049, Up: Changelog -5.7 0.4.8 +5.8 0.4.8 ========= *Fixed* @@ -1158,12 +1162,12 @@ File: README.info, Node: 048, Next: 047, Prev: 049, Up: Changelog  File: README.info, Node: 047, Next: 046, Prev: 048, Up: Changelog -5.8 0.4.7 +5.9 0.4.7 ========= *Fixed* - • Give a useful error if org-ql-search-directories-files is called - without a directories argument and org-directory doesn’t exist. + • Give a useful error if ‘org-ql-search-directories-files’ is called + without a directories argument and ‘org-directory’ doesn’t exist. (Fixes #139 (https://github.com/alphapapa/org-ql/issues/139). Thanks to Matt Huszagh (https://github.com/matthuszagh) for reporting.) @@ -1171,12 +1175,12 @@ File: README.info, Node: 047, Next: 046, Prev: 048, Up: Changelog  File: README.info, Node: 046, Next: 045, Prev: 047, Up: Changelog -5.9 0.4.6 -========= +5.10 0.4.6 +========== *Fixed* - • Compatibility with newer versions of the peg library, which removed - a macro used by this package. (Fixes #75 + • Compatibility with newer versions of the ‘peg’ library, which + removed a macro used by this package. (Fixes #75 (https://github.com/alphapapa/org-ql/issues/75). Thanks to Karl Voit (https://github.com/novoid) and @karlicoss (https://github.com/karlicoss) for reporting.) @@ -1184,7 +1188,7 @@ File: README.info, Node: 046, Next: 045, Prev: 047, Up: Changelog  File: README.info, Node: 045, Next: 044, Prev: 046, Up: Changelog -5.10 0.4.5 +5.11 0.4.5 ========== *Fixed* @@ -1196,7 +1200,7 @@ File: README.info, Node: 045, Next: 044, Prev: 046, Up: Changelog  File: README.info, Node: 044, Next: 043, Prev: 045, Up: Changelog -5.11 0.4.4 +5.12 0.4.4 ========== *Fixed* @@ -1208,17 +1212,17 @@ File: README.info, Node: 044, Next: 043, Prev: 045, Up: Changelog  File: README.info, Node: 043, Next: 042, Prev: 044, Up: Changelog -5.12 0.4.3 +5.13 0.4.3 ========== *Fixed* - • When org-ql-view-refresh is called, ensure the buffer is an Org QL - View buffer. + • When ‘org-ql-view-refresh’ is called, ensure the buffer is an Org + QL View buffer.  File: README.info, Node: 042, Next: 041, Prev: 043, Up: Changelog -5.13 0.4.2 +5.14 0.4.2 ========== *Fixed* @@ -1227,24 +1231,24 @@ File: README.info, Node: 042, Next: 041, Prev: 043, Up: Changelog  File: README.info, Node: 041, Next: 04, Prev: 042, Up: Changelog -5.14 0.4.1 +5.15 0.4.1 ========== *Fixed* - • level predicate used with arguments in plain queries. (Thanks to + • ‘level’ predicate used with arguments in plain queries. (Thanks to Akira Komamura (https://github.com/akirak) for reporting.)  File: README.info, Node: 04, Next: 032, Prev: 041, Up: Changelog -5.15 0.4 +5.16 0.4 ======== _Note:_ The next release, 0.5, may include changes which will require minor updates to written queries (e.g. a few predicates may be renamed). Users who wish to avoid those changes happening unexpectedly -in their configs should avoid upgrading org-ql beyond 0.4 automatically, -as they will be pushed to the master branch when ready. +in their configs should avoid upgrading ‘org-ql’ beyond 0.4 +automatically, as they will be pushed to the ‘master’ branch when ready. *Added* • *Commands* @@ -1258,13 +1262,13 @@ as they will be pushed to the master branch when ready. • *Queries* • Negation of terms in plain queries using ‘!’. For example, ‘tags:space !moon’ to exclude entries which contain ‘moon’. - • Predicates outline-path (alias olp) and outline-path-segment - (alias olps). + • Predicates ‘outline-path’ (alias ‘olp’) and + ‘outline-path-segment’ (alias ‘olps’). • Predicate ‘src’, which matches Org Babel source blocks. - • Predicates parent and ancestors. (Thanks to Josh Moller-Mara - (https://github.com/mm--).) - • Alias h for heading predicate. - • Alias r for regexp predicate. (Thanks to Feng Shu + • Predicates ‘parent’ and ‘ancestors’. (Thanks to Josh + Moller-Mara (https://github.com/mm--).) + • Alias ‘h’ for ‘heading’ predicate. + • Alias ‘r’ for ‘regexp’ predicate. (Thanks to Feng Shu (https://github.com/tumashu).) • Info manual. • Function ‘helm-org-ql-source’, which returns a Helm source that @@ -1272,40 +1276,40 @@ as they will be pushed to the master branch when ready. for custom Helm commands that search certain files. • Display a message when views are refreshed. (Thanks to xeijin (https://github.com/xeijin).) - • Respect Org Agenda restriction in org-ql-block. (Thanks to Ihor + • Respect Org Agenda restriction in ‘org-ql-block’. (Thanks to Ihor Radchenko (https://github.com/yantar92) for reporting.) - • Option org-ql-view-sidebar-sort-views. - • Mouseover help-echo text for org-ql-views default view names. - • "Dangling tasks" default view in org-ql-views. (Users who have - modified org-ql-views from the default will not see the new view + • Option ‘org-ql-view-sidebar-sort-views’. + • Mouseover ‘help-echo’ text for ‘org-ql-views’ default view names. + • "Dangling tasks" default view in ‘org-ql-views’. (Users who have + modified ‘org-ql-views’ from the default will not see the new view unless they copy it into their config.) *Changed* - • Some default org-ql-view views (users who have modified - org-ql-views from the default will not see the new views unless + • Some default ‘org-ql-view’ views (users who have modified + ‘org-ql-views’ from the default will not see the new views unless they copy them into their config): • Rename some views. - • "Stuck projects" view (now uses descendants instead of - children, which is more useful. + • "Stuck projects" view (now uses ‘descendants’ instead of + ‘children’, which is more useful. *Fixed* - • Inherit file tags when org-tag-inheritance is enabled. (Fixes #55 - (https://github.com/alphapapa/org-ql/issues/55). Thanks to Mikhail - Skorzhinskiy (https://github.com/mskorzhinskiy).) - • Call helm-make-source directly instead of using - helm-build-sync-source macro. (Fixes #60 + • Inherit file tags when ‘org-tag-inheritance’ is enabled. (Fixes + #55 (https://github.com/alphapapa/org-ql/issues/55). Thanks to + Mikhail Skorzhinskiy (https://github.com/mskorzhinskiy).) + • Call ‘helm-make-source’ directly instead of using + ‘helm-build-sync-source’ macro. (Fixes #60 (https://github.com/alphapapa/org-ql/issues/60). Thanks to Matt Huszagh (https://github.com/matthuszagh) for reporting.) • Search/view buffers now always end with a newline, which prevents - side-scrolling of the window when calling end-of-buffer. - • Face for done to-do keywords in org-ql-view buffers. (Thanks to + side-scrolling of the window when calling ‘end-of-buffer’. + • Face for done to-do keywords in ‘org-ql-view’ buffers. (Thanks to Yiming Chen (https://github.com/dsdshcym).) • Make view buffers read-only. (Fixes #72 (https://github.com/alphapapa/org-ql/issues/72). Thanks to xeijin (https://github.com/xeijin).) • Sorting with single sorter specified as an atom. (Thanks to Jeff Filipovits (https://github.com/legalnonsense).) - • Autoload for org-ql-block agenda block. (Fixes #53 + • Autoload for ‘org-ql-block’ agenda block. (Fixes #53 (https://github.com/alphapapa/org-ql/issues/53). Thanks to reports from Gus Cantieni (https://github.com/gcantieni), Karl Voit (https://github.com/novoid), rieje (https://github.com/rieje), and @@ -1318,20 +1322,20 @@ as they will be pushed to the master branch when ready.  File: README.info, Node: 032, Next: 031, Prev: 04, Up: Changelog -5.16 0.3.2 +5.17 0.3.2 ========== *Fixed* - • In org-ql-search, accept symbol as ‘:super-groups’ argument. - • In the This week and Next week default org-ql-views views, set - timestamps for beginning-of-week to 00:00:00 and end-of-week to + • In ‘org-ql-search’, accept symbol as ‘:super-groups’ argument. + • In the ‘This week’ and ‘Next week’ default ‘org-ql-views’ views, + set timestamps for beginning-of-week to 00:00:00 and end-of-week to 23:59:59. • Plain quoted-phrases in non-sexp queries.  File: README.info, Node: 031, Next: 03, Prev: 032, Up: Changelog -5.17 0.3.1 +5.18 0.3.1 ========== *Fixed* @@ -1341,103 +1345,106 @@ File: README.info, Node: 031, Next: 03, Prev: 032, Up: Changelog  File: README.info, Node: 03, Next: 023, Prev: 031, Up: Changelog -5.18 0.3 +5.19 0.3 ======== *Added* - • Alternative, non-sexp query syntax for commands org-ql-search and - helm-org-ql. See . - • Command helm-org-ql. - • Command org-ql-sparse-tree, like org-sparse-tree for org-ql + • Alternative, non-sexp query syntax for commands ‘org-ql-search’ and + ‘helm-org-ql’. See . + • Command ‘helm-org-ql’. + • Command ‘org-ql-sparse-tree’, like ‘org-sparse-tree’ for ‘org-ql’ queries. (Thanks to Akira Komamura (https://github.com/akirak).) - • Command org-ql-view-sidebar. + • Command ‘org-ql-view-sidebar’. • Per-buffer, per-heading tag caching, which increases the speed of tags-related queries by 6-7x. • More tags-related predicates and aliases: - • For inherited tags: tags-inherited, inherited-tags, tags-i, - itags. - • For heading-local tags: tags-local, local-tags, tags-l, ltags. - • tags-all, tags&: Matches all given tags using boolean AND - (rather than boolean OR, which the tags predicate uses). - • Variable org-ql-block-header, which overrides the default header in - org-ql-block agenda blocks. - • Predicate (path). - • Option org-ql-views may now be customized in a guided, structured - way with the customization UI (e.g. - M-x customize-option RET org-ql-views RET, or press c in the - org-ql-view-sidebar buffer). - • Enable more Org Agenda commands in org-ql-view buffers (e.g. + • For inherited tags: ‘tags-inherited’, ‘inherited-tags’, + ‘tags-i’, ‘itags’. + • For heading-local tags: ‘tags-local’, ‘local-tags’, ‘tags-l’, + ‘ltags’. + • ‘tags-all’, ‘tags&’: Matches all given tags using boolean + ‘AND’ (rather than boolean ‘OR’, which the ‘tags’ predicate + uses). + • Variable ‘org-ql-block-header’, which overrides the default header + in ‘org-ql-block’ agenda blocks. + • Predicate ‘(path)’. + • Option ‘org-ql-views’ may now be customized in a guided, structured + way with the customization UI (e.g. ‘M-x customize-option RET + org-ql-views RET’, or press ‘c’ in the ‘org-ql-view-sidebar’ + buffer). + • Enable more Org Agenda commands in ‘org-ql-view’ buffers (e.g. setting deadlines and scheduling). (Fixes #35 (https://github.com/alphapapa/org-ql/issues/35). Thanks to Milan Zamazal (https://github.com/mz-pdm) and Mikhail Skorzhinskii (https://github.com/mskorzhinskiy).) - • Function org-ql-select’s buffers-files argument can be a function - which returns a list of buffers and/or files. + • Function ‘org-ql-select’’s ‘buffers-files’ argument can be a + function which returns a list of buffers and/or files. *Changed* - • Predicate heading now accepts multiple regexps, which are matched - with boolean AND. - • Predicate regexp now matches its regexp arguments with boolean AND. - • Package org-super-agenda is now a dependency. This removes the + • Predicate ‘heading’ now accepts multiple regexps, which are matched + with boolean ‘AND’. + • Predicate ‘regexp’ now matches its regexp arguments with boolean + ‘AND’. + • Package ‘org-super-agenda’ is now a dependency. This removes the need for awkward code to handle the case where it’s not installed, and makes grouping features always available. Of course, the - global minor mode org-super-agenda-mode is not activated by org-ql, - so no behavior is changed in Org Agenda or org-ql; it only means - that commands like org-ql-search will always provide grouping when - called with the appropriate arguments. + global minor mode ‘org-super-agenda-mode’ is not activated by + ‘org-ql’, so no behavior is changed in Org Agenda or ‘org-ql’; it + only means that commands like ‘org-ql-search’ will always provide + grouping when called with the appropriate arguments. *Removed* - • Macro org-ql-agenda. Instead, use function org-ql-search. See - also command org-ql-view, etc. + • Macro ‘org-ql-agenda’. Instead, use function ‘org-ql-search’. See + also command ‘org-ql-view’, etc. *Fixed* - • Predicate heading now matches only against heading text, i.e. not - including tags at the end of the line, to-do keyword, etc. - • Predicate todo now matches case-sensitively, avoiding - non-todo-keyword matches (e.g. a heading which begins Waiting on - will no longer match for a todo keyword WAITING). - • Interactive completion in org-ql-search. + • Predicate ‘heading’ now matches only against heading text, i.e. + not including tags at the end of the line, to-do keyword, etc. + • Predicate ‘todo’ now matches case-sensitively, avoiding + non-todo-keyword matches (e.g. a heading which begins ‘Waiting on’ + will no longer match for a todo keyword ‘WAITING’). + • Interactive completion in ‘org-ql-search’. *Internal* - • Refactored code from file org-ql-agenda.el into files - org-ql-search.el and org-ql-view.el. Function and variable names - have been changed accordingly. + • Refactored code from file ‘org-ql-agenda.el’ into files + ‘org-ql-search.el’ and ‘org-ql-view.el’. Function and variable + names have been changed accordingly.  File: README.info, Node: 023, Next: 022, Prev: 03, Up: Changelog -5.19 0.2.3 +5.20 0.2.3 ========== *Fixed* • Priority queries could fail to match headings whose to-do keywords - had non-alphabetic characters, like TO-READ. + had non-alphabetic characters, like ‘TO-READ’.  File: README.info, Node: 022, Next: 021, Prev: 023, Up: Changelog -5.20 0.2.2 +5.21 0.2.2 ========== *Fixed* - • (deadline auto) selector matched entries whose deadlines had a + • ‘(deadline auto)’ selector matched entries whose deadlines had a warning period that had not yet been entered - (org-deadline-warning-days too soon). + (‘org-deadline-warning-days’ too soon).  File: README.info, Node: 021, Next: 02, Prev: 022, Up: Changelog -5.21 0.2.1 +5.22 0.2.1 ========== *Fixed* - • (descendants) selector matched against parent heading instead of + • ‘(descendants)’ selector matched against parent heading instead of only descendants.  File: README.info, Node: 02, Next: 01, Prev: 021, Up: Changelog -5.22 0.2 +5.23 0.2 ======== *Added* @@ -1458,7 +1465,7 @@ File: README.info, Node: 02, Next: 01, Prev: 021, Up: Changelog • Selectors ‘ts-a’ and ‘ts-i’, aliases for ‘ts-active’ and ‘ts-inactive’. • Selector ‘ts’ now accepts a ‘:type’ argument. - • Face org-ql-agenda-due-date. + • Face ‘org-ql-agenda-due-date’. • Selectors ‘(children)’ and ‘(descendants)’. • Function ‘org-ql-search’ and macro ‘org-ql-agenda’ accept a ‘:title’ argument, which is displayed in the header. @@ -1467,11 +1474,11 @@ File: README.info, Node: 02, Next: 01, Prev: 021, Up: Changelog • Customization group ‘org-ql’. • Command ‘org-ql-view’, which displays views saved to variable ‘org-ql-views’, which can be saved from ‘org-ql-search’ buffers - with command ‘org-ql-search-save’, which is bound to C-x C-s in + with command ‘org-ql-search-save’, which is bound to ‘C-x C-s’ in view buffers. • Variable ‘org-ql-view-map’, active in view buffers displayed by ‘org-ql-search’, ‘org-ql-agenda’, and ‘org-ql-view’. - • random sort method. + • ‘random’ sort method. • Save position when refreshing search buffers. *Changed* @@ -1490,14 +1497,14 @@ File: README.info, Node: 02, Next: 01, Prev: 021, Up: Changelog *Fixed* • Handle date ranges in date-based selectors. (Thanks to Cody - Goodman (https://github.com/codygman), Samuel W. Flint + Goodman (https://github.com/codygman), Samuel W. Flint (https://github.com/swflint), and Vikas Rawal (https://github.com/vikasrawal).) - • Don’t overwrite bindings in org-agenda-mode-map. + • Don’t overwrite bindings in ‘org-agenda-mode-map’. • Don’t search buffers without headings, and show a message if the user attempts it. • Don’t search hidden/special buffers. - • Properly accept arbitrary sort functions in org-ql-select, etc. + • Properly accept arbitrary sort functions in ‘org-ql-select’, etc. (Fixes #37 (https://github.com/alphapapa/org-ql/issues/37). Thanks to Milan Zamazal (https://github.com/mz-pdm).) • Planning-line-related predicates searched too far into entries. @@ -1511,7 +1518,7 @@ File: README.info, Node: 02, Next: 01, Prev: 021, Up: Changelog (https://github.com/dakra).) *Internal* - • Optimizations for some query selectors, e.g. regexp and todo. + • Optimizations for some query selectors, e.g. ‘regexp’ and ‘todo’. These can provide a significant improvement for some queries. See benchmarks in notes.org (notes.org). • Library ts (https://github.com/alphapapa/ts.el) is now used for @@ -1520,7 +1527,7 @@ File: README.info, Node: 02, Next: 01, Prev: 021, Up: Changelog  File: README.info, Node: 01, Prev: 02, Up: Changelog -5.23 0.1 +5.24 0.1 ======== First tagged release. @@ -1551,10 +1558,10 @@ in the advanced searching tutorial it would require using ‘org-search-view’ with a query with specific regular expression syntax, like this: -+lisp +{^\*+\s-+TO-READ\s-} + +lisp +{^\*+\s-+TO-READ\s-} - But with org-ql-search, you would write a query like -lisp todo:TO-READ, or in Lisp syntax, ‘(and "lisp" (todo "TO-READ"))’. + But with ‘org-ql-search’, you would write a query like ‘lisp +todo:TO-READ’, or in Lisp syntax, ‘(and "lisp" (todo "TO-READ"))’.  File: README.info, Node: org-sidebar, Prev: Comparison with Org Agenda searches, Up: Notes @@ -1578,59 +1585,66 @@ GPLv3  Tag Table: Node: Top225 -Node: Contents1704 -Node: Screenshots1827 -Node: Installation1945 -Node: Quelpa2441 -Node: Helm support2969 -Node: Usage3360 -Node: Commands3758 -Node: org-ql-search4231 -Node: helm-org-ql5881 -Node: org-ql-view6241 -Node: org-ql-view-sidebar6741 -Node: org-ql-view-recent-items7097 -Node: org-ql-sparse-tree7581 -Node: Queries8381 -Node: Non-sexp query syntax9492 -Node: General predicates11216 -Node: Ancestor/descendant predicates17437 -Node: Date/time predicates18565 -Node: Functions / Macros21653 -Node: Agenda-like views21951 -Node: Listing / acting-on results23356 -Node: Custom predicates28992 -Node: Dynamic block32651 -Node: Links35363 -Node: Tips36050 -Node: Changelog36368 -Node: 06137101 -Node: 0637657 -Node: 05240668 -Node: 05140968 -Node: 0541385 -Node: 04942859 -Node: 04843133 -Node: 04743480 -Node: 04643875 -Node: 04544275 -Node: 04444636 -Node: 04344995 -Node: 04245192 -Node: 04145353 -Node: 0445594 -Node: 03249527 -Node: 03149906 -Node: 0350103 -Node: 02353078 -Node: 02253306 -Node: 02153574 -Node: 0253773 -Node: 0157808 -Node: Notes57909 -Node: Comparison with Org Agenda searches58071 -Node: org-sidebar58943 -Node: License59222 +Node: Contents1728 +Node: Screenshots1851 +Node: Installation1969 +Node: Quelpa2483 +Node: Helm support3011 +Node: Usage3414 +Node: Commands3812 +Node: org-ql-search4285 +Node: helm-org-ql5959 +Node: org-ql-view6337 +Node: org-ql-view-sidebar6867 +Node: org-ql-view-recent-items7247 +Node: org-ql-sparse-tree7743 +Node: Queries8543 +Node: Non-sexp query syntax9660 +Node: General predicates11419 +Node: Ancestor/descendant predicates17658 +Node: Date/time predicates18786 +Node: Functions / Macros21910 +Node: Agenda-like views22208 +Ref: Function ‘org-ql-block’22370 +Node: Listing / acting-on results23631 +Ref: Caching23839 +Ref: Function ‘org-ql-select’24752 +Ref: Function ‘org-ql-query’27178 +Ref: Macro ‘org-ql’ (deprecated)28952 +Node: Custom predicates29267 +Ref: Macro ‘org-ql-defpred’29491 +Node: Dynamic block32932 +Node: Links35656 +Node: Tips36343 +Node: Changelog36667 +Node: 06237415 +Node: 06137708 +Node: 0638276 +Node: 05241330 +Node: 05141630 +Node: 0542053 +Node: 04943582 +Node: 04843862 +Node: 04744209 +Node: 04644616 +Node: 04545024 +Node: 04445385 +Node: 04345744 +Node: 04245947 +Node: 04146108 +Node: 0446355 +Node: 03250456 +Node: 03150859 +Node: 0351056 +Node: 02354356 +Node: 02254590 +Node: 02154870 +Node: 0255075 +Node: 0159153 +Node: Notes59254 +Node: Comparison with Org Agenda searches59416 +Node: org-sidebar60305 +Node: License60584  End Tag Table From d0acc8cbba63ef50f1c0ecffdf1770792b13592d Mon Sep 17 00:00:00 2001 From: Adam Porter Date: Mon, 30 May 2022 09:23:46 -0500 Subject: [PATCH 07/40] Add: (org-ql-find) --- README.org | 20 +- images/org-ql-find.png | Bin 0 -> 94724 bytes org-ql-find.el | 178 +++++++++++++ org-ql.info | 563 ++++++++++++++++++++++------------------- 4 files changed, 496 insertions(+), 265 deletions(-) create mode 100644 images/org-ql-find.png create mode 100644 org-ql-find.el diff --git a/README.org b/README.org index 61fe3bd..507c328 100644 --- a/README.org +++ b/README.org @@ -94,6 +94,9 @@ Lisp code examples are in [[examples.org]]. :TOC: ignore-children :END: ++ *Jumping to an entry:* + - [[#org-ql-find][org-ql-find]] (command) + - [[#helm-org-ql][helm-org-ql]] (command) + *Showing an agenda-like view:* - [[#org-ql-search][org-ql-search]] (command) - [[#org-ql-view][org-ql-view]] (command) @@ -101,8 +104,18 @@ Lisp code examples are in [[examples.org]]. - [[#org-ql-view-recent-items][org-ql-view-recent-items]] (command) + *Showing a tree in a buffer:* - [[#org-ql-sparse-tree][org-ql-sparse-tree]] (command) -+ *Showing results with Helm*: - - [[#helm-org-ql][helm-org-ql]] (command) + +*** org-ql-find + +/Note: These commands use [[#non-sexp-query-syntax][non-sexp queries]]./ + +These commands jump to a heading selected using Emacs's built-in completion facilities with an Org QL query: + +- ~org-ql-find~ searches all entry content. +- ~org-ql-find-path~ searches only headings (using the ~outline-path:~ predicate). +- ~org-ql-find-heading~ searches only headings (using the ~heading:~ predicate). + +[[images/org-ql-find.png]] *** org-ql-search @@ -524,7 +537,8 @@ Simple links may also be written manually in either sexp or non-sexp form, like: ** 0.7-pre -Nothing new yet. +*Added* ++ Commands ~org-ql-find~, ~org-ql-find-heading~, and ~org-ql-find-path~, which jump to entries selected using Emacs's built-in completion facilities and Org QL queries (like ~helm-org-ql~, but doesn't require Helm.). ** 0.6.2 diff --git a/images/org-ql-find.png b/images/org-ql-find.png new file mode 100644 index 0000000000000000000000000000000000000000..34359d09a48f1267c2cb29ee5d4e2b729f240427 GIT binary patch literal 94724 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 Wr5k-;Dj$LNeR{h3xvX + +;; This program is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see . + +;;; Commentary: + +;; This library provides a way to quickly find and go to Org entries +;; selected with Emacs's built-in completions API (so it works with +;; packages that extend it, like Vertico, Marginalia, etc). It works +;; like `helm-org-ql' but does not require Helm. + +;;; Code: + +(require 'cl-lib) + +(require 'org) +(require 'org-ql) + +;;;; Customization + +(defgroup org-ql-find nil + "Options for `org-ql-find'." + :group 'org-ql) + +(defcustom org-ql-find-reverse-paths t + "Whether to reverse Org outline paths in `org-ql-find' results." + :type 'boolean) + +(defcustom org-ql-find-goto-hook '(org-show-entry) + "Functions called when selecting an entry." + :type 'hook) + +(defface org-ql-find-snippet '((t (:inherit font-lock-comment-face))) + "Snippets.") + +;;;; Functions + +;;;###autoload +(cl-defun org-ql-find (buffers-files &key query-prefix + (prompt "Find entry: ")) + "Go to an Org entry in BUFFERS-FILES selected by searching entries with `org-ql'. +Interactively, with universal prefix, select multiple buffers to +search with completion. + +If QUERY-PREFIX, prepend it to the query (e.g. use \"heading:\" +to only search headings, easily creating a custom command that +saves the user from having to type it)." + (interactive + (list (if current-prefix-arg + (mapcar #'get-buffer + (completing-read-multiple + "Buffers: " + (mapcar #'buffer-name + (cl-remove-if-not (lambda (buffer) + (eq 'org-mode (buffer-local-value 'major-mode buffer))) + (buffer-list))) nil t)) + (current-buffer)))) + ;; Emacs's completion API is not always easy to understand, + ;; especially when using "programmed completion." This code was + ;; made possible by the example Clemens Radermacher shared at + ;; . + (let ((table (make-hash-table :test #'equal)) + (window-width (window-width))) + (cl-labels ((action + () (font-lock-ensure (point-at-bol) (point-at-eol)) + (let* ((path (thread-first (org-get-outline-path t t) + (org-format-outline-path window-width nil "") + (org-split-string ""))) + (path (if org-ql-find-reverse-paths + (string-join (nreverse path) "\\") + (string-join path "/")))) + (puthash path (point-marker) table) + path)) + (affix (completions) + (cl-loop for completion in completions + for marker = (gethash completion table) + for todo-state = (if-let (it (org-entry-get marker "TODO")) + (concat (propertize it + 'face (org-get-todo-face it)) + " ") + "") + for snippet = (if-let (it (snippet marker)) + (propertize (concat " " it) + 'face 'org-ql-find-snippet) + "") + collect (list completion todo-state snippet))) + (annotate (candidate) + (or (snippet (gethash candidate table)) "")) + (snippet (marker) + (org-with-point-at marker + (org-end-of-meta-data t) + (unless (org-at-heading-p) + (let ((end (min (+ (point) 51) + (org-entry-end-position)))) + (truncate-string-to-width + (replace-regexp-in-string "\n" " " (buffer-substring (point) end) + t t) + 50 nil nil t))))) + (group (candidate transform) + (pcase transform + (`nil (buffer-name (marker-buffer (gethash candidate table)))) + (_ candidate))) + (try (string _table _pred point &optional _metadata) + (cons string point)) + (all (string table pred _point) + (all-completions string table pred)) + (collection (str _pred flag) + (pcase flag + ('metadata (list 'metadata + (cons 'group-function #'group) + (cons 'affixation-function #'affix) + (cons 'annotation-function #'annotate))) + (`t (unless (string-empty-p str) + (org-ql-select buffers-files (org-ql--query-string-to-sexp (concat query-prefix str)) + :action #'action)))))) + (let* ((completion-styles '(org-ql-find)) + (completion-styles-alist (list (list 'org-ql-find #'try #'all "Org QL Find"))) + (selected (completing-read prompt #'collection nil)) + (marker (gethash selected table))) + (with-current-buffer (marker-buffer marker) + (goto-char marker) + (display-buffer (current-buffer)) + (run-hook-with-args 'org-ql-find-goto-hook)))))) + +;;;###autoload +(defun org-ql-find-heading (buffers-files) + "Go to an Org entry in BUFFERS-FILES selected by searching with `org-ql'. +Only headings are searched (using the \"heading:\" predicate). +Interactively, with universal prefix, select multiple buffers to +search with completion." + (interactive + (list (if current-prefix-arg + (mapcar #'get-buffer + (completing-read-multiple + "Buffers: " + (mapcar #'buffer-name + (cl-remove-if-not (lambda (buffer) + (eq 'org-mode (buffer-local-value 'major-mode buffer))) + (buffer-list))) nil t)) + (current-buffer)))) + (org-ql-find buffers-files :prompt "Find heading: " :query-prefix "heading:")) + +;;;###autoload +(defun org-ql-find-path (buffers-files) + "Go to an Org entry in BUFFERS-FILES selected by searching with `org-ql'. +Only outline paths are searched (using the \"outline-path:\" +predicate). Interactively, with universal prefix, select +multiple buffers to search with completion." + (interactive + (list (if current-prefix-arg + (mapcar #'get-buffer + (completing-read-multiple + "Buffers: " + (mapcar #'buffer-name + (cl-remove-if-not (lambda (buffer) + (eq 'org-mode (buffer-local-value 'major-mode buffer))) + (buffer-list))) nil t)) + (current-buffer)))) + (org-ql-find buffers-files :prompt "Find outline path: " :query-prefix "outline-path:")) + +(provide 'org-ql-find) + +;;; org-ql-find.el ends here diff --git a/org-ql.info b/org-ql.info index 84895de..0576cf5 100644 --- a/org-ql.info +++ b/org-ql.info @@ -11,6 +11,14 @@ File: README.info, Node: Top, Next: Contents, Up: (dir) org-ql ****** +This package provides a query language for Org files. It offers two +syntax styles: Lisp-like sexps and search engine-like keywords. + + It includes three libraries: The ‘org-ql’ library is flexible and may +be used as a backend for other tools. The libraries ‘org-ql-search’ and +‘helm-org-ql’ (a separate package) provide interactive search commands +and saved views. + * Menu: * Contents:: @@ -23,15 +31,11 @@ org-ql — The Detailed Node Listing — - - Installation * Quelpa:: * Helm support:: - - Usage * Commands:: @@ -43,6 +47,7 @@ Usage Commands +* org-ql-find:: * org-ql-search:: * helm-org-ql:: * org-ql-view:: @@ -50,8 +55,6 @@ Commands * org-ql-view-recent-items:: * org-ql-sparse-tree:: - - Queries * Non-sexp query syntax:: @@ -59,8 +62,6 @@ Queries * Ancestor/descendant predicates:: * Date/time predicates:: - - Functions / Macros * Agenda-like views:: @@ -70,6 +71,7 @@ Functions / Macros Changelog * 0.7-pre: 07-pre. +* 0.6.2: 062. * 0.6.1: 061. * 0.6: 06. * 0.5.2: 052. @@ -94,20 +96,11 @@ Changelog * 0.2: 02. * 0.1: 01. - - Notes * Comparison with Org Agenda searches:: * org-sidebar:: -This package provides a query language for Org files. It offers two -syntax styles: Lisp-like sexps and search engine-like keywords. - - It includes three libraries: The org-ql library is flexible and may -be used as a backend for other tools. The libraries org-ql-search and -helm-org-ql (a separate package) provide interactive search commands and -saved views.  File: README.info, Node: Contents, Next: Screenshots, Prev: Top, Up: Top @@ -128,13 +121,13 @@ File: README.info, Node: Installation, Next: Usage, Prev: Screenshots, Up: T 3 Installation ************** -The package org-ql may be installed directly from MELPA +The package ‘org-ql’ may be installed directly from MELPA (https://melpa.org/#/org-ql) or with other tools like Quelpa (https://framagit.org/steckerhalter/quelpa). After installation, you can use the commands without additional configuration. To use the functions and macros in your own Elisp code, -use libraries org-ql and org-ql-view. +use libraries ‘org-ql’ and ‘org-ql-view’. * Menu: @@ -165,8 +158,8 @@ File: README.info, Node: Helm support, Prev: Quelpa, Up: Installation 3.2 Helm support ================ -The command helm-org-ql is available in the package helm-org-ql. It may -be installed from MELPA, or with Quelpa, like so: +The command ‘helm-org-ql’ is available in the package ‘helm-org-ql’. It +may be installed from MELPA, or with Quelpa, like so: (use-package helm-org-ql :quelpa (helm-org-ql :fetcher github :repo "alphapapa/org-ql" @@ -200,6 +193,9 @@ File: README.info, Node: Commands, Next: Queries, Up: Usage 4.1 Commands ============ + • *Jumping to an entry:* + • (command) + • (command) • *Showing an agenda-like view:* • (command) • (command) @@ -207,11 +203,10 @@ File: README.info, Node: Commands, Next: Queries, Up: Usage • (command) • *Showing a tree in a buffer:* • (command) - • *Showing results with Helm*: - • (command) * Menu: +* org-ql-find:: * org-ql-search:: * helm-org-ql:: * org-ql-view:: @@ -220,9 +215,26 @@ File: README.info, Node: Commands, Next: Queries, Up: Usage * org-ql-sparse-tree::  -File: README.info, Node: org-ql-search, Next: helm-org-ql, Up: Commands +File: README.info, Node: org-ql-find, Next: org-ql-search, Up: Commands -4.1.1 org-ql-search +4.1.1 org-ql-find +----------------- + +_Note: These commands use ._ + + These commands jump to a heading selected using Emacs’s built-in +completion facilities with an Org QL query: + + • ‘org-ql-find’ searches all entry content. + • ‘org-ql-find-path’ searches only headings (using the + ‘outline-path:’ predicate). + • ‘org-ql-find-heading’ searches only headings (using the ‘heading:’ + predicate). + + +File: README.info, Node: org-ql-search, Next: helm-org-ql, Prev: org-ql-find, Up: Commands + +4.1.2 org-ql-search ------------------- _Note: This command supports both sexp queries and ._ @@ -249,10 +261,10 @@ Interactively, with prefix, leave narrowed. ‘priority’. *Bindings:* Keys bound in results buffer. - • r: Refresh results. With prefix, prompt to adjust search + • ‘r’: Refresh results. With prefix, prompt to adjust search parameters. - • v: Show transient view dispatcher (like Magit’s popups). - • C-x C-s: Save query to variable ‘org-ql-views’ (accessible with + • ‘v’: Show ‘transient’ view dispatcher (like Magit’s popups). + • ‘C-x C-s’: Save query to variable ‘org-ql-views’ (accessible with command ‘org-ql-view’). *Note:* The view buffer is currently put in ‘org-agenda-mode’, which @@ -265,57 +277,57 @@ text properties are placed on each item, imitating an Agenda buffer.)  File: README.info, Node: helm-org-ql, Next: org-ql-view, Prev: org-ql-search, Up: Commands -4.1.2 helm-org-ql +4.1.3 helm-org-ql ----------------- _Note: This command uses . It is available separately in the package -helm-org-ql._ +‘helm-org-ql’._ This command displays matches with Helm. - • Press C-x C-s in the Helm session to save the results to an - org-ql-search buffer. + • Press ‘C-x C-s’ in the Helm session to save the results to an + ‘org-ql-search’ buffer.  File: README.info, Node: org-ql-view, Next: org-ql-view-sidebar, Prev: helm-org-ql, Up: Commands -4.1.3 org-ql-view +4.1.4 org-ql-view ----------------- Choose and display a view stored in ‘org-ql-views’. *Bindings:* Keys bound in view buffer. - • g, r: Refresh results. With prefix, prompt to adjust search + • ‘g’, ‘r’: Refresh results. With prefix, prompt to adjust search parameters. - • v: Show transient view dispatcher (like Magit’s popups). - • C-x C-s: Save query to variable ‘org-ql-views’ (accessible with + • ‘v’: Show ‘transient’ view dispatcher (like Magit’s popups). + • ‘C-x C-s’: Save query to variable ‘org-ql-views’ (accessible with command ‘org-ql-view’).  File: README.info, Node: org-ql-view-sidebar, Next: org-ql-view-recent-items, Prev: org-ql-view, Up: Commands -4.1.4 org-ql-view-sidebar +4.1.5 org-ql-view-sidebar ------------------------- -Show a sidebar window listing views stored in org-ql-views for easy -access. In the sidebar, press RET or mouse-1 to show the view at point, -and press c to customize the view at point. +Show a sidebar window listing views stored in ‘org-ql-views’ for easy +access. In the sidebar, press ‘RET’ or ‘mouse-1’ to show the view at +point, and press ‘c’ to customize the view at point.  File: README.info, Node: org-ql-view-recent-items, Next: org-ql-sparse-tree, Prev: org-ql-view-sidebar, Up: Commands -4.1.5 org-ql-view-recent-items +4.1.6 org-ql-view-recent-items ------------------------------ Show items in ‘FILES’ from last ‘DAYS’ days with timestamps of ‘TYPE’. ‘TYPE’ may be ‘ts’, ‘ts-active’, ‘ts-inactive’, ‘clocked’, ‘closed’, -‘deadline’, ‘planning’, or ‘scheduled’. FILES defaults to those -returned by the function org-agenda-files. +‘deadline’, ‘planning’, or ‘scheduled’. ‘FILES’ defaults to those +returned by the function ‘org-agenda-files’.  File: README.info, Node: org-ql-sparse-tree, Prev: org-ql-view-recent-items, Up: Commands -4.1.6 org-ql-sparse-tree +4.1.7 org-ql-sparse-tree ------------------------ Arguments: ‘(query &key keep-previous (buffer (current-buffer)))’ @@ -337,7 +349,7 @@ File: README.info, Node: Queries, Next: Functions / Macros, Prev: Commands, =========== • • • • - An org-ql query is a Lisp expression which may contain arbitrary + An ‘org-ql’ query is a Lisp expression which may contain arbitrary expressions, as well as calling certain built-in predicates. It is byte-compiled into a predicate function which is tested with point on each heading in an Org buffer; when it returns non-nil, the heading @@ -366,11 +378,11 @@ File: README.info, Node: Non-sexp query syntax, Next: General predicates, Up: 4.2.1 Non-sexp query syntax --------------------------- -The command org-ql-search also accepts, and the command helm-org-ql only -accepts, an alternative, non-sexp query syntax. The syntax is simple, -and a few examples of queries in both syntaxes should suffice. By -default, when multiple predicates are used, they are combined with -boolean and. +The command ‘org-ql-search’ also accepts, and the command ‘helm-org-ql’ +only accepts, an alternative, non-sexp query syntax. The syntax is +simple, and a few examples of queries in both syntaxes should suffice. +By default, when multiple predicates are used, they are combined with +boolean ‘and’. Sexp syntax Non-sexp syntax ------------------------------------------------------------------------------------------------------- @@ -386,9 +398,9 @@ Sexp syntax Non-sexp syntax ‘(and (tags "space") (not (regexp "moon")))’ ‘tags:space !moon’ ‘(priority >= B)’ ‘priority:A,B’ - Note that the effort, level, and priority predicates do not support -comparators in the non-sexp syntax, so multiple arguments should be -passed instead, as seen in the last example. + Note that the ‘effort’, ‘level’, and ‘priority’ predicates do not +support comparators in the non-sexp syntax, so multiple arguments should +be passed instead, as seen in the last example.  File: README.info, Node: General predicates, Next: Ancestor/descendant predicates, Prev: Non-sexp query syntax, Up: Queries @@ -410,14 +422,14 @@ Arguments are listed next to predicate names, where applicable. Matches if effort is between DURATIONs, inclusive. ‘(effort COMPARATOR DURATION)’: Matches if effort compares to ‘DURATION’ with ‘COMPARATOR’. ‘COMPARATOR’ may be ‘<’, ‘<=’, ‘>’, or ‘>=’. - ‘DURATION’ should be an Org effort string, like 5 or 0:05. + ‘DURATION’ should be an Org effort string, like ‘5’ or ‘0:05’. ‘habit’ Return non-nil if entry is a habit. ‘heading (&rest strings)’ Return non-nil if current entry’s heading matches all ‘STRINGS’. Matching is done case-insensitively. - • Aliases: h. -‘‘heading-regexp (&rest regexps)’’ + • Aliases: ‘h’. +‘heading-regexp (&rest regexps)’ Return non-nil if current entry’s heading matches all ‘REGEXPS’ (regexp strings). Matching is done case-insensitively. • Aliases: ‘h*’. @@ -437,8 +449,8 @@ Arguments are listed next to predicate names, where applicable. ‘outline-path (&rest strings)’ Return non-nil if current node’s outline path matches all of ‘STRINGS’. Each string may appear as a substring in any part of - the node’s outline path. For example, the path Food/Fruits/Grapes - would match ‘(olp "Fruit" "Grape")’. + the node’s outline path. For example, the path + ‘Food/Fruits/Grapes’ would match ‘(olp "Fruit" "Grape")’. • Aliases: ‘olp’. ‘outline-path-segment (&rest strings)’ Return non-nil if current node’s outline path matches ‘STRINGS’. @@ -470,8 +482,8 @@ Arguments are listed next to predicate names, where applicable. Return non-nil if current entry matches all of ‘REGEXPS’ (regexp strings). Matches against entire entry, from beginning of its heading to the next heading. - • Aliases: r. -‘‘src (&key lang regexps)’’ + • Aliases: ‘r’. +‘src (&key lang regexps)’ Return non-nil if current entry contains an Org Babel source block. If ‘LANG’ is non-nil, match blocks of that language. If ‘REGEXPS’ is non-nil, require that block’s contents match all regexps. @@ -553,10 +565,10 @@ to look forward, or negative to look backward), a string parseable by Return non-nil if current entry has a timestamp in given period. Without arguments, return non-nil if entry has a timestamp. - ‘ts-active, ts-a’ - Like ts, but only matches active timestamps. - ‘ts-inactive, ts-i’ - Like ts, but only matches inactive timestamps. + ‘ts-active’, ‘ts-a’ + Like ‘ts’, but only matches active timestamps. + ‘ts-inactive’, ‘ts-i’ + Like ‘ts’, but only matches inactive timestamps. The following predicates, in addition to the keyword arguments, can also take a single argument, a number, which looks backward or forward a @@ -584,8 +596,8 @@ searching for items planned in the next few days: • *Forward-looking* ‘deadline’ Return non-nil if current entry has deadline in given period. - If argument is auto, return non-nil if entry has deadline - within org-deadline-warning-days. Without arguments, return + If argument is ‘auto’, return non-nil if entry has deadline + within ‘org-deadline-warning-days’. Without arguments, return non-nil if entry has any deadline. ‘planning’ Return non-nil if current entry has planning timestamp (i.e. @@ -623,7 +635,7 @@ File: README.info, Node: Agenda-like views, Next: Listing / acting-on results, For use as a custom agenda block type in ‘org-agenda-custom-commands’. For example, you could define a custom series command like this, which would list all priority A - items tagged Emacs with to-do keyword SOMEDAY, followed by the + items tagged ‘Emacs’ with to-do keyword ‘SOMEDAY’, followed by the standard agenda view, in a single buffer: (setq org-agenda-custom-commands @@ -643,8 +655,8 @@ File: README.info, Node: Agenda-like views, Next: Listing / acting-on results, However, the ‘org-ql-block’ version runs in about 1/5th the time. - The variable org-ql-block-header may be bound to a string to use as - the block header, otherwise the header is formed automatically. + The variable ‘org-ql-block-header’ may be bound to a string to use + as the block header, otherwise the header is formed automatically.  File: README.info, Node: Listing / acting-on results, Next: Custom predicates, Prev: Agenda-like views, Up: Functions / Macros @@ -792,7 +804,7 @@ File: README.info, Node: Custom predicates, Prev: Listing / acting-on results, • See: Custom predicate tutorial (examples/defpred.org) - 1. Macro: org-ql-defpred + 1. Macro: ‘org-ql-defpred’ _Arguments:_ ‘(name args docstring &key body preambles normalizers)’ @@ -881,9 +893,9 @@ supported: • Each column may also be specified as a list with the second element being a header string. For example, to abbreviate the priority column: ‘(priority "P")’. - • For certain columns, like property, arguments may be passed by - specifying the column type itself as a list. For example, to - display a column showing the values of a ‘property’ named + • For certain columns, like ‘property’, arguments may be passed + by specifying the column type itself as a list. For example, + to display a column showing the values of a ‘property’ named ‘milestone’, with the header being abbreviated to ‘M’: ‘((property "milestone") "M")’. • ‘:sort’ One or a list of Org QL sorting methods (see @@ -898,7 +910,7 @@ in the following example). For example, this dynamic block shows the first seven headings that are to-do items with priority A or B, sorted by deadline then priority, -with certain columns (including the value of the agenda-group property +with certain columns (including the value of the ‘agenda-group’ property with a custom header) and timestamp format: #+BEGIN: org-ql :query "todo: priority:A,B" :columns (todo (priority "P") ((property "agenda-group") "Group") deadline heading) :sort (deadline priority) :take 7 :ts-format "%Y-%m-%d %H:%M" @@ -941,7 +953,7 @@ File: README.info, Node: Tips, Prev: Links, Up: Usage ======== • Org QL View buffers can be bookmarked with Emacs bookmark commands, - e.g. C-x r m. This also integrates with org-sidebar + e.g. ‘C-x r m’. This also integrates with org-sidebar (https://github.com/alphapapa/org-sidebar) and Burly (https://github.com/alphapapa/burly.el). @@ -960,6 +972,7 @@ releases. * Menu: * 0.7-pre: 07-pre. +* 0.6.2: 062. * 0.6.1: 061. * 0.6: 06. * 0.5.2: 052. @@ -985,17 +998,32 @@ releases. * 0.1: 01.  -File: README.info, Node: 07-pre, Next: 061, Up: Changelog +File: README.info, Node: 07-pre, Next: 062, Up: Changelog 5.1 0.7-pre =========== -Nothing new yet. +*Added* + • Commands ‘org-ql-find’, ‘org-ql-find-heading’, and + ‘org-ql-find-path’, which jump to entries selected using Emacs’s + built-in completion facilities and Org QL queries (like + ‘helm-org-ql’, but doesn’t require Helm.).  -File: README.info, Node: 061, Next: 06, Prev: 07-pre, Up: Changelog +File: README.info, Node: 062, Next: 061, Prev: 07-pre, Up: Changelog -5.2 0.6.1 +5.2 0.6.2 +========= + +*Fixed* + • ‘link’ predicate when used in an ‘or’’ed query. (#279 + (https://github.com/alphapapa/org-ql/issues/279). Thanks to Marc + Fargas (https://github.com/telenieko) for reporting.) + + +File: README.info, Node: 061, Next: 06, Prev: 062, Up: Changelog + +5.3 0.6.1 ========= *Fixed* @@ -1013,11 +1041,11 @@ File: README.info, Node: 061, Next: 06, Prev: 07-pre, Up: Changelog  File: README.info, Node: 06, Next: 052, Prev: 061, Up: Changelog -5.3 0.6 +5.4 0.6 ======= *Added* - • Macro org-ql-defpred, used to define search predicates. (See + • Macro ‘org-ql-defpred’, used to define search predicates. (See tutorial (examples/defpred.org).) • Predicate ‘effort’. • Predicate ‘heading-regexp’, which matches regular expressions @@ -1038,21 +1066,21 @@ File: README.info, Node: 06, Next: 052, Prev: 061, Up: Changelog For example, ‘:sort '(todo priority date)’ now does what ‘:sort '(date priority todo)’ did in earlier versions. (This change is made to enable the new ‘reverse’ sorting method.) Users who have - customized org-ql-views will need to update the stored views’ + customized ‘org-ql-views’ will need to update the stored views’ sorting methods to preserve the desired sort order. - • Helm support (including the command helm-org-ql) has been moved to - a separate package, helm-org-ql. + • Helm support (including the command ‘helm-org-ql’) has been moved + to a separate package, ‘helm-org-ql’. • Predicate ‘heading’ now matches plain strings instead of regular expressions. - • Update dash dependency, and remove dependency on obsolete - dash-functional. (Fixes #179 + • Update ‘dash’ dependency, and remove dependency on obsolete + ‘dash-functional’. (Fixes #179 (https://github.com/alphapapa/org-ql/issues/179), #209 (https://github.com/alphapapa/org-ql/issues/209). Thanks to Mark Hudnall (https://github.com/landakram), Akira Komamura (https://github.com/akirak), Nathanael kinfe (https://github.com/natask), Pablo Stafforini (https://github.com/benthamite), Jason May - (https://github.com/jmay), and Basil L. Contovounesios + (https://github.com/jmay), and Basil L. Contovounesios (https://github.com/basil-conto).) *Removed* @@ -1067,7 +1095,7 @@ File: README.info, Node: 06, Next: 052, Prev: 061, Up: Changelog *Internal* • Predicates are now defined more cleanly with a macro - (org-ql-defpred) that consolidates functionality related to each + (‘org-ql-defpred’) that consolidates functionality related to each predicate. This will also allow users to more easily define custom predicates. • Version 1.0 of library ‘peg’ is now required. @@ -1080,7 +1108,7 @@ File: README.info, Node: 06, Next: 052, Prev: 061, Up: Changelog  File: README.info, Node: 052, Next: 051, Prev: 06, Up: Changelog -5.4 0.5.2 +5.5 0.5.2 ========= *Fixed* @@ -1091,12 +1119,12 @@ File: README.info, Node: 052, Next: 051, Prev: 06, Up: Changelog  File: README.info, Node: 051, Next: 05, Prev: 052, Up: Changelog -5.5 0.5.1 +5.6 0.5.1 ========= *Fixed* • Custom sorting functions could corrupt the cache, causing items to - disappear after refreshing an org-ql-search buffer. (#186 + disappear after refreshing an ‘org-ql-search’ buffer. (#186 (https://github.com/alphapapa/org-ql/issues/186), #187 (https://github.com/alphapapa/org-ql/issues/187). Thanks to Nathanael kinfe (https://github.com/natask).) @@ -1104,19 +1132,19 @@ File: README.info, Node: 051, Next: 05, Prev: 052, Up: Changelog  File: README.info, Node: 05, Next: 049, Prev: 051, Up: Changelog -5.6 0.5 +5.7 0.5 ======= *Added* - • View dispatcher using transient.el (like Magit), bound to v in + • View dispatcher using ‘transient.el’ (like Magit), bound to ‘v’ in search/view buffers. - • Predicate link, which matches descriptions and targets in Org + • Predicate ‘link’, which matches descriptions and targets in Org links. • Predicate ‘tags-regexp’ (alias: ‘tags*’), which matches regexps against entry tags (e.g, helpful when a tag might end in "s"). • Emacs bookmark support: Org QL View buffers can be bookmarked with, - e.g. C-x r m and shown with, e.g. C-x r b. (This also enables - view restoration with Burly + e.g. ‘C-x r m’ and shown with, e.g. ‘C-x r b’. (This also + enables view restoration with Burly (https://github.com/alphapapa/burly.el).) • Dynamic block support. • Org link support (storing and opening links to Org QL View @@ -1124,7 +1152,7 @@ File: README.info, Node: 05, Next: 049, Prev: 051, Up: Changelog • Mascot. *Changed* - • Binding to refresh search/view buffers changed to r. + • Binding to refresh search/view buffers changed to ‘r’. *Internal* • When formatting entries for Org QL View buffers, use internal @@ -1133,9 +1161,9 @@ File: README.info, Node: 05, Next: 049, Prev: 051, Up: Changelog Org versions before 9.3. *Deprecated* - • Macro org-ql is marked obsolete. It will be removed in v0.7. - Functions org-ql-select and org-ql-query should be used instead. - (The macro serves only to confuse with regard to quoting + • Macro ‘org-ql’ is marked obsolete. It will be removed in v0.7. + Functions ‘org-ql-select’ and ‘org-ql-query’ should be used + instead. (The macro serves only to confuse with regard to quoting arguments.) *Acknowledgments* @@ -1145,18 +1173,18 @@ File: README.info, Node: 05, Next: 049, Prev: 051, Up: Changelog  File: README.info, Node: 049, Next: 048, Prev: 05, Up: Changelog -5.7 0.4.9 +5.8 0.4.9 ========= *Fixed* - • Agenda restriction in org-ql-block. (Fixes #84 + • Agenda restriction in ‘org-ql-block’. (Fixes #84 (https://github.com/alphapapa/org-ql/issues/84). Thanks to Ihor Radchenko (https://github.com/yantar92).)  File: README.info, Node: 048, Next: 047, Prev: 049, Up: Changelog -5.8 0.4.8 +5.9 0.4.8 ========= *Fixed* @@ -1168,12 +1196,12 @@ File: README.info, Node: 048, Next: 047, Prev: 049, Up: Changelog  File: README.info, Node: 047, Next: 046, Prev: 048, Up: Changelog -5.9 0.4.7 -========= +5.10 0.4.7 +========== *Fixed* - • Give a useful error if org-ql-search-directories-files is called - without a directories argument and org-directory doesn’t exist. + • Give a useful error if ‘org-ql-search-directories-files’ is called + without a directories argument and ‘org-directory’ doesn’t exist. (Fixes #139 (https://github.com/alphapapa/org-ql/issues/139). Thanks to Matt Huszagh (https://github.com/matthuszagh) for reporting.) @@ -1181,12 +1209,12 @@ File: README.info, Node: 047, Next: 046, Prev: 048, Up: Changelog  File: README.info, Node: 046, Next: 045, Prev: 047, Up: Changelog -5.10 0.4.6 +5.11 0.4.6 ========== *Fixed* - • Compatibility with newer versions of the peg library, which removed - a macro used by this package. (Fixes #75 + • Compatibility with newer versions of the ‘peg’ library, which + removed a macro used by this package. (Fixes #75 (https://github.com/alphapapa/org-ql/issues/75). Thanks to Karl Voit (https://github.com/novoid) and @karlicoss (https://github.com/karlicoss) for reporting.) @@ -1194,7 +1222,7 @@ File: README.info, Node: 046, Next: 045, Prev: 047, Up: Changelog  File: README.info, Node: 045, Next: 044, Prev: 046, Up: Changelog -5.11 0.4.5 +5.12 0.4.5 ========== *Fixed* @@ -1206,7 +1234,7 @@ File: README.info, Node: 045, Next: 044, Prev: 046, Up: Changelog  File: README.info, Node: 044, Next: 043, Prev: 045, Up: Changelog -5.12 0.4.4 +5.13 0.4.4 ========== *Fixed* @@ -1218,17 +1246,17 @@ File: README.info, Node: 044, Next: 043, Prev: 045, Up: Changelog  File: README.info, Node: 043, Next: 042, Prev: 044, Up: Changelog -5.13 0.4.3 +5.14 0.4.3 ========== *Fixed* - • When org-ql-view-refresh is called, ensure the buffer is an Org QL - View buffer. + • When ‘org-ql-view-refresh’ is called, ensure the buffer is an Org + QL View buffer.  File: README.info, Node: 042, Next: 041, Prev: 043, Up: Changelog -5.14 0.4.2 +5.15 0.4.2 ========== *Fixed* @@ -1237,24 +1265,24 @@ File: README.info, Node: 042, Next: 041, Prev: 043, Up: Changelog  File: README.info, Node: 041, Next: 04, Prev: 042, Up: Changelog -5.15 0.4.1 +5.16 0.4.1 ========== *Fixed* - • level predicate used with arguments in plain queries. (Thanks to + • ‘level’ predicate used with arguments in plain queries. (Thanks to Akira Komamura (https://github.com/akirak) for reporting.)  File: README.info, Node: 04, Next: 032, Prev: 041, Up: Changelog -5.16 0.4 +5.17 0.4 ======== _Note:_ The next release, 0.5, may include changes which will require minor updates to written queries (e.g. a few predicates may be renamed). Users who wish to avoid those changes happening unexpectedly -in their configs should avoid upgrading org-ql beyond 0.4 automatically, -as they will be pushed to the master branch when ready. +in their configs should avoid upgrading ‘org-ql’ beyond 0.4 +automatically, as they will be pushed to the ‘master’ branch when ready. *Added* • *Commands* @@ -1268,13 +1296,13 @@ as they will be pushed to the master branch when ready. • *Queries* • Negation of terms in plain queries using ‘!’. For example, ‘tags:space !moon’ to exclude entries which contain ‘moon’. - • Predicates outline-path (alias olp) and outline-path-segment - (alias olps). + • Predicates ‘outline-path’ (alias ‘olp’) and + ‘outline-path-segment’ (alias ‘olps’). • Predicate ‘src’, which matches Org Babel source blocks. - • Predicates parent and ancestors. (Thanks to Josh Moller-Mara - (https://github.com/mm--).) - • Alias h for heading predicate. - • Alias r for regexp predicate. (Thanks to Feng Shu + • Predicates ‘parent’ and ‘ancestors’. (Thanks to Josh + Moller-Mara (https://github.com/mm--).) + • Alias ‘h’ for ‘heading’ predicate. + • Alias ‘r’ for ‘regexp’ predicate. (Thanks to Feng Shu (https://github.com/tumashu).) • Info manual. • Function ‘helm-org-ql-source’, which returns a Helm source that @@ -1282,40 +1310,40 @@ as they will be pushed to the master branch when ready. for custom Helm commands that search certain files. • Display a message when views are refreshed. (Thanks to xeijin (https://github.com/xeijin).) - • Respect Org Agenda restriction in org-ql-block. (Thanks to Ihor + • Respect Org Agenda restriction in ‘org-ql-block’. (Thanks to Ihor Radchenko (https://github.com/yantar92) for reporting.) - • Option org-ql-view-sidebar-sort-views. - • Mouseover help-echo text for org-ql-views default view names. - • "Dangling tasks" default view in org-ql-views. (Users who have - modified org-ql-views from the default will not see the new view + • Option ‘org-ql-view-sidebar-sort-views’. + • Mouseover ‘help-echo’ text for ‘org-ql-views’ default view names. + • "Dangling tasks" default view in ‘org-ql-views’. (Users who have + modified ‘org-ql-views’ from the default will not see the new view unless they copy it into their config.) *Changed* - • Some default org-ql-view views (users who have modified - org-ql-views from the default will not see the new views unless + • Some default ‘org-ql-view’ views (users who have modified + ‘org-ql-views’ from the default will not see the new views unless they copy them into their config): • Rename some views. - • "Stuck projects" view (now uses descendants instead of - children, which is more useful. + • "Stuck projects" view (now uses ‘descendants’ instead of + ‘children’, which is more useful. *Fixed* - • Inherit file tags when org-tag-inheritance is enabled. (Fixes #55 - (https://github.com/alphapapa/org-ql/issues/55). Thanks to Mikhail - Skorzhinskiy (https://github.com/mskorzhinskiy).) - • Call helm-make-source directly instead of using - helm-build-sync-source macro. (Fixes #60 + • Inherit file tags when ‘org-tag-inheritance’ is enabled. (Fixes + #55 (https://github.com/alphapapa/org-ql/issues/55). Thanks to + Mikhail Skorzhinskiy (https://github.com/mskorzhinskiy).) + • Call ‘helm-make-source’ directly instead of using + ‘helm-build-sync-source’ macro. (Fixes #60 (https://github.com/alphapapa/org-ql/issues/60). Thanks to Matt Huszagh (https://github.com/matthuszagh) for reporting.) • Search/view buffers now always end with a newline, which prevents - side-scrolling of the window when calling end-of-buffer. - • Face for done to-do keywords in org-ql-view buffers. (Thanks to + side-scrolling of the window when calling ‘end-of-buffer’. + • Face for done to-do keywords in ‘org-ql-view’ buffers. (Thanks to Yiming Chen (https://github.com/dsdshcym).) • Make view buffers read-only. (Fixes #72 (https://github.com/alphapapa/org-ql/issues/72). Thanks to xeijin (https://github.com/xeijin).) • Sorting with single sorter specified as an atom. (Thanks to Jeff Filipovits (https://github.com/legalnonsense).) - • Autoload for org-ql-block agenda block. (Fixes #53 + • Autoload for ‘org-ql-block’ agenda block. (Fixes #53 (https://github.com/alphapapa/org-ql/issues/53). Thanks to reports from Gus Cantieni (https://github.com/gcantieni), Karl Voit (https://github.com/novoid), rieje (https://github.com/rieje), and @@ -1328,20 +1356,20 @@ as they will be pushed to the master branch when ready.  File: README.info, Node: 032, Next: 031, Prev: 04, Up: Changelog -5.17 0.3.2 +5.18 0.3.2 ========== *Fixed* - • In org-ql-search, accept symbol as ‘:super-groups’ argument. - • In the This week and Next week default org-ql-views views, set - timestamps for beginning-of-week to 00:00:00 and end-of-week to + • In ‘org-ql-search’, accept symbol as ‘:super-groups’ argument. + • In the ‘This week’ and ‘Next week’ default ‘org-ql-views’ views, + set timestamps for beginning-of-week to 00:00:00 and end-of-week to 23:59:59. • Plain quoted-phrases in non-sexp queries.  File: README.info, Node: 031, Next: 03, Prev: 032, Up: Changelog -5.18 0.3.1 +5.19 0.3.1 ========== *Fixed* @@ -1351,103 +1379,106 @@ File: README.info, Node: 031, Next: 03, Prev: 032, Up: Changelog  File: README.info, Node: 03, Next: 023, Prev: 031, Up: Changelog -5.19 0.3 +5.20 0.3 ======== *Added* - • Alternative, non-sexp query syntax for commands org-ql-search and - helm-org-ql. See . - • Command helm-org-ql. - • Command org-ql-sparse-tree, like org-sparse-tree for org-ql + • Alternative, non-sexp query syntax for commands ‘org-ql-search’ and + ‘helm-org-ql’. See . + • Command ‘helm-org-ql’. + • Command ‘org-ql-sparse-tree’, like ‘org-sparse-tree’ for ‘org-ql’ queries. (Thanks to Akira Komamura (https://github.com/akirak).) - • Command org-ql-view-sidebar. + • Command ‘org-ql-view-sidebar’. • Per-buffer, per-heading tag caching, which increases the speed of tags-related queries by 6-7x. • More tags-related predicates and aliases: - • For inherited tags: tags-inherited, inherited-tags, tags-i, - itags. - • For heading-local tags: tags-local, local-tags, tags-l, ltags. - • tags-all, tags&: Matches all given tags using boolean AND - (rather than boolean OR, which the tags predicate uses). - • Variable org-ql-block-header, which overrides the default header in - org-ql-block agenda blocks. - • Predicate (path). - • Option org-ql-views may now be customized in a guided, structured - way with the customization UI (e.g. - M-x customize-option RET org-ql-views RET, or press c in the - org-ql-view-sidebar buffer). - • Enable more Org Agenda commands in org-ql-view buffers (e.g. + • For inherited tags: ‘tags-inherited’, ‘inherited-tags’, + ‘tags-i’, ‘itags’. + • For heading-local tags: ‘tags-local’, ‘local-tags’, ‘tags-l’, + ‘ltags’. + • ‘tags-all’, ‘tags&’: Matches all given tags using boolean + ‘AND’ (rather than boolean ‘OR’, which the ‘tags’ predicate + uses). + • Variable ‘org-ql-block-header’, which overrides the default header + in ‘org-ql-block’ agenda blocks. + • Predicate ‘(path)’. + • Option ‘org-ql-views’ may now be customized in a guided, structured + way with the customization UI (e.g. ‘M-x customize-option RET + org-ql-views RET’, or press ‘c’ in the ‘org-ql-view-sidebar’ + buffer). + • Enable more Org Agenda commands in ‘org-ql-view’ buffers (e.g. setting deadlines and scheduling). (Fixes #35 (https://github.com/alphapapa/org-ql/issues/35). Thanks to Milan Zamazal (https://github.com/mz-pdm) and Mikhail Skorzhinskii (https://github.com/mskorzhinskiy).) - • Function org-ql-select’s buffers-files argument can be a function - which returns a list of buffers and/or files. + • Function ‘org-ql-select’’s ‘buffers-files’ argument can be a + function which returns a list of buffers and/or files. *Changed* - • Predicate heading now accepts multiple regexps, which are matched - with boolean AND. - • Predicate regexp now matches its regexp arguments with boolean AND. - • Package org-super-agenda is now a dependency. This removes the + • Predicate ‘heading’ now accepts multiple regexps, which are matched + with boolean ‘AND’. + • Predicate ‘regexp’ now matches its regexp arguments with boolean + ‘AND’. + • Package ‘org-super-agenda’ is now a dependency. This removes the need for awkward code to handle the case where it’s not installed, and makes grouping features always available. Of course, the - global minor mode org-super-agenda-mode is not activated by org-ql, - so no behavior is changed in Org Agenda or org-ql; it only means - that commands like org-ql-search will always provide grouping when - called with the appropriate arguments. + global minor mode ‘org-super-agenda-mode’ is not activated by + ‘org-ql’, so no behavior is changed in Org Agenda or ‘org-ql’; it + only means that commands like ‘org-ql-search’ will always provide + grouping when called with the appropriate arguments. *Removed* - • Macro org-ql-agenda. Instead, use function org-ql-search. See - also command org-ql-view, etc. + • Macro ‘org-ql-agenda’. Instead, use function ‘org-ql-search’. See + also command ‘org-ql-view’, etc. *Fixed* - • Predicate heading now matches only against heading text, i.e. not - including tags at the end of the line, to-do keyword, etc. - • Predicate todo now matches case-sensitively, avoiding - non-todo-keyword matches (e.g. a heading which begins Waiting on - will no longer match for a todo keyword WAITING). - • Interactive completion in org-ql-search. + • Predicate ‘heading’ now matches only against heading text, i.e. + not including tags at the end of the line, to-do keyword, etc. + • Predicate ‘todo’ now matches case-sensitively, avoiding + non-todo-keyword matches (e.g. a heading which begins ‘Waiting on’ + will no longer match for a todo keyword ‘WAITING’). + • Interactive completion in ‘org-ql-search’. *Internal* - • Refactored code from file org-ql-agenda.el into files - org-ql-search.el and org-ql-view.el. Function and variable names - have been changed accordingly. + • Refactored code from file ‘org-ql-agenda.el’ into files + ‘org-ql-search.el’ and ‘org-ql-view.el’. Function and variable + names have been changed accordingly.  File: README.info, Node: 023, Next: 022, Prev: 03, Up: Changelog -5.20 0.2.3 +5.21 0.2.3 ========== *Fixed* • Priority queries could fail to match headings whose to-do keywords - had non-alphabetic characters, like TO-READ. + had non-alphabetic characters, like ‘TO-READ’.  File: README.info, Node: 022, Next: 021, Prev: 023, Up: Changelog -5.21 0.2.2 +5.22 0.2.2 ========== *Fixed* - • (deadline auto) selector matched entries whose deadlines had a + • ‘(deadline auto)’ selector matched entries whose deadlines had a warning period that had not yet been entered - (org-deadline-warning-days too soon). + (‘org-deadline-warning-days’ too soon).  File: README.info, Node: 021, Next: 02, Prev: 022, Up: Changelog -5.22 0.2.1 +5.23 0.2.1 ========== *Fixed* - • (descendants) selector matched against parent heading instead of + • ‘(descendants)’ selector matched against parent heading instead of only descendants.  File: README.info, Node: 02, Next: 01, Prev: 021, Up: Changelog -5.23 0.2 +5.24 0.2 ======== *Added* @@ -1468,7 +1499,7 @@ File: README.info, Node: 02, Next: 01, Prev: 021, Up: Changelog • Selectors ‘ts-a’ and ‘ts-i’, aliases for ‘ts-active’ and ‘ts-inactive’. • Selector ‘ts’ now accepts a ‘:type’ argument. - • Face org-ql-agenda-due-date. + • Face ‘org-ql-agenda-due-date’. • Selectors ‘(children)’ and ‘(descendants)’. • Function ‘org-ql-search’ and macro ‘org-ql-agenda’ accept a ‘:title’ argument, which is displayed in the header. @@ -1477,11 +1508,11 @@ File: README.info, Node: 02, Next: 01, Prev: 021, Up: Changelog • Customization group ‘org-ql’. • Command ‘org-ql-view’, which displays views saved to variable ‘org-ql-views’, which can be saved from ‘org-ql-search’ buffers - with command ‘org-ql-search-save’, which is bound to C-x C-s in + with command ‘org-ql-search-save’, which is bound to ‘C-x C-s’ in view buffers. • Variable ‘org-ql-view-map’, active in view buffers displayed by ‘org-ql-search’, ‘org-ql-agenda’, and ‘org-ql-view’. - • random sort method. + • ‘random’ sort method. • Save position when refreshing search buffers. *Changed* @@ -1500,14 +1531,14 @@ File: README.info, Node: 02, Next: 01, Prev: 021, Up: Changelog *Fixed* • Handle date ranges in date-based selectors. (Thanks to Cody - Goodman (https://github.com/codygman), Samuel W. Flint + Goodman (https://github.com/codygman), Samuel W. Flint (https://github.com/swflint), and Vikas Rawal (https://github.com/vikasrawal).) - • Don’t overwrite bindings in org-agenda-mode-map. + • Don’t overwrite bindings in ‘org-agenda-mode-map’. • Don’t search buffers without headings, and show a message if the user attempts it. • Don’t search hidden/special buffers. - • Properly accept arbitrary sort functions in org-ql-select, etc. + • Properly accept arbitrary sort functions in ‘org-ql-select’, etc. (Fixes #37 (https://github.com/alphapapa/org-ql/issues/37). Thanks to Milan Zamazal (https://github.com/mz-pdm).) • Planning-line-related predicates searched too far into entries. @@ -1521,7 +1552,7 @@ File: README.info, Node: 02, Next: 01, Prev: 021, Up: Changelog (https://github.com/dakra).) *Internal* - • Optimizations for some query selectors, e.g. regexp and todo. + • Optimizations for some query selectors, e.g. ‘regexp’ and ‘todo’. These can provide a significant improvement for some queries. See benchmarks in notes.org (notes.org). • Library ts (https://github.com/alphapapa/ts.el) is now used for @@ -1530,7 +1561,7 @@ File: README.info, Node: 02, Next: 01, Prev: 021, Up: Changelog  File: README.info, Node: 01, Prev: 02, Up: Changelog -5.24 0.1 +5.25 0.1 ======== First tagged release. @@ -1561,10 +1592,10 @@ in the advanced searching tutorial it would require using ‘org-search-view’ with a query with specific regular expression syntax, like this: -+lisp +{^\*+\s-+TO-READ\s-} + +lisp +{^\*+\s-+TO-READ\s-} - But with org-ql-search, you would write a query like -lisp todo:TO-READ, or in Lisp syntax, ‘(and "lisp" (todo "TO-READ"))’. + But with ‘org-ql-search’, you would write a query like ‘lisp +todo:TO-READ’, or in Lisp syntax, ‘(and "lisp" (todo "TO-READ"))’.  File: README.info, Node: org-sidebar, Prev: Comparison with Org Agenda searches, Up: Notes @@ -1588,60 +1619,68 @@ GPLv3  Tag Table: Node: Top225 -Node: Contents1724 -Node: Screenshots1847 -Node: Installation1965 -Node: Quelpa2461 -Node: Helm support2989 -Node: Usage3380 -Node: Commands3778 -Node: org-ql-search4251 -Node: helm-org-ql5901 -Node: org-ql-view6261 -Node: org-ql-view-sidebar6761 -Node: org-ql-view-recent-items7117 -Node: org-ql-sparse-tree7601 -Node: Queries8401 -Node: Non-sexp query syntax9512 -Node: General predicates11236 -Node: Ancestor/descendant predicates17457 -Node: Date/time predicates18585 -Node: Functions / Macros21673 -Node: Agenda-like views21971 -Node: Listing / acting-on results23376 -Node: Custom predicates29012 -Node: Dynamic block32671 -Node: Links35383 -Node: Tips36070 -Node: Changelog36388 -Node: 07-pre37141 -Node: 06137248 -Node: 0637819 -Node: 05240830 -Node: 05141130 -Node: 0541547 -Node: 04943021 -Node: 04843295 -Node: 04743642 -Node: 04644037 -Node: 04544439 -Node: 04444800 -Node: 04345159 -Node: 04245356 -Node: 04145517 -Node: 0445758 -Node: 03249691 -Node: 03150070 -Node: 0350267 -Node: 02353242 -Node: 02253470 -Node: 02153738 -Node: 0253937 -Node: 0157972 -Node: Notes58073 -Node: Comparison with Org Agenda searches58235 -Node: org-sidebar59107 -Node: License59386 +Node: Contents1764 +Node: Screenshots1887 +Node: Installation2005 +Node: Quelpa2519 +Node: Helm support3047 +Node: Usage3450 +Node: Commands3848 +Node: org-ql-find4353 +Node: org-ql-search4867 +Node: helm-org-ql6561 +Node: org-ql-view6939 +Node: org-ql-view-sidebar7469 +Node: org-ql-view-recent-items7849 +Node: org-ql-sparse-tree8345 +Node: Queries9145 +Node: Non-sexp query syntax10262 +Node: General predicates12021 +Node: Ancestor/descendant predicates18260 +Node: Date/time predicates19388 +Node: Functions / Macros22512 +Node: Agenda-like views22810 +Ref: Function ‘org-ql-block’22972 +Node: Listing / acting-on results24233 +Ref: Caching24441 +Ref: Function ‘org-ql-select’25354 +Ref: Function ‘org-ql-query’27780 +Ref: Macro ‘org-ql’ (deprecated)29554 +Node: Custom predicates29869 +Ref: Macro ‘org-ql-defpred’30093 +Node: Dynamic block33534 +Node: Links36258 +Node: Tips36945 +Node: Changelog37269 +Node: 07-pre38037 +Node: 06238392 +Node: 06138700 +Node: 0639268 +Node: 05242322 +Node: 05142622 +Node: 0543045 +Node: 04944574 +Node: 04844854 +Node: 04745201 +Node: 04645610 +Node: 04546018 +Node: 04446379 +Node: 04346738 +Node: 04246941 +Node: 04147102 +Node: 0447349 +Node: 03251450 +Node: 03151853 +Node: 0352050 +Node: 02355350 +Node: 02255584 +Node: 02155864 +Node: 0256069 +Node: 0160147 +Node: Notes60248 +Node: Comparison with Org Agenda searches60410 +Node: org-sidebar61299 +Node: License61578  End Tag Table From 67506a56f8e84af80af922e40797f8dc861243e4 Mon Sep 17 00:00:00 2001 From: Adam Porter Date: Tue, 31 May 2022 12:01:19 -0500 Subject: [PATCH 08/40] Change: (org-ql--byte-compile-warning) More useful error message e.g. if a predicate only accepts one argument, but the query gives two, now the error will say, "PREDICATE called with 2 arguments, but accepts only 1" (so maybe the predicate definition just needs "&rest"). --- README.org | 3 +++ org-ql.el | 4 ++-- org-ql.info | 59 ++++++++++++++++++++++++++++------------------------- 3 files changed, 36 insertions(+), 30 deletions(-) diff --git a/README.org b/README.org index 507c328..64233d0 100644 --- a/README.org +++ b/README.org @@ -540,6 +540,9 @@ Simple links may also be written manually in either sexp or non-sexp form, like: *Added* + Commands ~org-ql-find~, ~org-ql-find-heading~, and ~org-ql-find-path~, which jump to entries selected using Emacs's built-in completion facilities and Org QL queries (like ~helm-org-ql~, but doesn't require Helm.). +*Changed* ++ Give more useful error message for invalid queries. + ** 0.6.2 *Fixed* diff --git a/org-ql.el b/org-ql.el index 0e6965b..d92c1f6 100644 --- a/org-ql.el +++ b/org-ql.el @@ -765,12 +765,12 @@ respectively." "#+end_src") t)) -(defun org-ql--byte-compile-warning (_string _pos _fill level) +(defun org-ql--byte-compile-warning (string _pos _fill level) "Signal an `org-ql-invalid-query' error. Arguments STRING, POS, FILL, and LEVEL are according to `byte-compile-log-warning-function'." ;; Used as the `byte-compile-log-warning-function' in `org-ql--query-preamble'. - (signal 'org-ql-invalid-query level)) + (signal 'org-ql-invalid-query (list string level))) (defun org-ql--query-predicate (query) "Return predicate function for QUERY." diff --git a/org-ql.info b/org-ql.info index 0576cf5..89eede3 100644 --- a/org-ql.info +++ b/org-ql.info @@ -1009,6 +1009,9 @@ File: README.info, Node: 07-pre, Next: 062, Up: Changelog built-in completion facilities and Org QL queries (like ‘helm-org-ql’, but doesn’t require Helm.). + *Changed* + • Give more useful error message for invalid queries. +  File: README.info, Node: 062, Next: 061, Prev: 07-pre, Up: Changelog @@ -1653,34 +1656,34 @@ Node: Links36258 Node: Tips36945 Node: Changelog37269 Node: 07-pre38037 -Node: 06238392 -Node: 06138700 -Node: 0639268 -Node: 05242322 -Node: 05142622 -Node: 0543045 -Node: 04944574 -Node: 04844854 -Node: 04745201 -Node: 04645610 -Node: 04546018 -Node: 04446379 -Node: 04346738 -Node: 04246941 -Node: 04147102 -Node: 0447349 -Node: 03251450 -Node: 03151853 -Node: 0352050 -Node: 02355350 -Node: 02255584 -Node: 02155864 -Node: 0256069 -Node: 0160147 -Node: Notes60248 -Node: Comparison with Org Agenda searches60410 -Node: org-sidebar61299 -Node: License61578 +Node: 06238465 +Node: 06138773 +Node: 0639341 +Node: 05242395 +Node: 05142695 +Node: 0543118 +Node: 04944647 +Node: 04844927 +Node: 04745274 +Node: 04645683 +Node: 04546091 +Node: 04446452 +Node: 04346811 +Node: 04247014 +Node: 04147175 +Node: 0447422 +Node: 03251523 +Node: 03151926 +Node: 0352123 +Node: 02355423 +Node: 02255657 +Node: 02155937 +Node: 0256142 +Node: 0160220 +Node: Notes60321 +Node: Comparison with Org Agenda searches60483 +Node: org-sidebar61372 +Node: License61651  End Tag Table From 64a9234733904af20fd495861792f1d5c2b37190 Mon Sep 17 00:00:00 2001 From: Adam Porter Date: Thu, 2 Jun 2022 07:43:08 -0500 Subject: [PATCH 09/40] Add: (org-ql-find) :query-filter Also, tidy docstring. --- org-ql-find.el | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/org-ql-find.el b/org-ql-find.el index 7585f75..20b6c57 100644 --- a/org-ql-find.el +++ b/org-ql-find.el @@ -51,15 +51,21 @@ ;;;; Functions ;;;###autoload -(cl-defun org-ql-find (buffers-files &key query-prefix +(cl-defun org-ql-find (buffers-files &key query-prefix query-filter (prompt "Find entry: ")) "Go to an Org entry in BUFFERS-FILES selected by searching entries with `org-ql'. Interactively, with universal prefix, select multiple buffers to search with completion. -If QUERY-PREFIX, prepend it to the query (e.g. use \"heading:\" -to only search headings, easily creating a custom command that -saves the user from having to type it)." +QUERY-PREFIX may be a string to prepend to the query (e.g. use +\"heading:\" to only search headings, easily creating a custom +command that saves the user from having to type it). + +QUERY-FILTER may be a function through which the query the user +types is filtered before execution (e.g. it could replace spaces +with commas to turn multiple tokens, which would normally be +treated as multiple predicates, into multiple arguments to a +single predicate)." (interactive (list (if current-prefix-arg (mapcar #'get-buffer @@ -126,6 +132,8 @@ saves the user from having to type it)." (cons 'affixation-function #'affix) (cons 'annotation-function #'annotate))) (`t (unless (string-empty-p str) + (when query-filter + (setf str (funcall query-filter str))) (org-ql-select buffers-files (org-ql--query-string-to-sexp (concat query-prefix str)) :action #'action)))))) (let* ((completion-styles '(org-ql-find)) From 36e97d51cf373d8d27ba05c1a3cb108c268d8aab Mon Sep 17 00:00:00 2001 From: Adam Porter Date: Thu, 9 Jun 2022 18:10:39 -0500 Subject: [PATCH 10/40] Tests: Fix non-file-backed-buffer test Somehow a Unicode "RIGHT SINGLE QUOTATION MARK" replaced the ASCII apostrophe in this string, which broke the test. Weird! --- tests/test-org-ql.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test-org-ql.el b/tests/test-org-ql.el index 0b7f2bb..95988c0 100644 --- a/tests/test-org-ql.el +++ b/tests/test-org-ql.el @@ -2105,7 +2105,7 @@ with keyword arg NOW in PLIST." (it "Refuses to link to non-file-backed buffer" (expect (var-after-link-save-open 'org-ql-view-buffers-files link-buffer query :buffer link-buffer) - :to-throw 'user-error '("Views that search non-file-backed buffers can’t be linked to")))))) + :to-throw 'user-error '("Views that search non-file-backed buffers can't be linked to")))))) ;; MAYBE: Also test `org-ql-views', although I already know it works now. ;; (describe "org-ql-views") From c7fab9dfce66602deaa8c333b5125747e5c3a5f9 Mon Sep 17 00:00:00 2001 From: Adam Porter Date: Thu, 9 Jun 2022 21:21:15 -0500 Subject: [PATCH 11/40] Tests: Wrap bare (expect) forms in (it) forms --- tests/test-org-ql.el | 72 +++++++++++++++++++++++++------------------- 1 file changed, 41 insertions(+), 31 deletions(-) diff --git a/tests/test-org-ql.el b/tests/test-org-ql.el index 95988c0..0839688 100644 --- a/tests/test-org-ql.el +++ b/tests/test-org-ql.el @@ -252,18 +252,24 @@ with keyword arg NOW in PLIST." :to-equal '(org-ql--predicate-outline-path "a\\." "b")))) (describe "(tags-inherited)" - (expect (org-ql--normalize-query '(tags-inherited)) - :to-equal '(tags-inherited)) - (expect (org-ql--normalize-query '(itags)) - :to-equal '(tags-inherited)) - (expect (org-ql--normalize-query '(tags-inherited "foo")) - :to-equal '(tags-inherited "foo")) - (expect (org-ql--normalize-query '(itags "foo")) - :to-equal '(tags-inherited "foo")) - (expect (org-ql--normalize-query '(tags-inherited "foo" "bar")) - :to-equal '(tags-inherited "foo" "bar")) - (expect (org-ql--normalize-query '(itags "foo" "bar")) - :to-equal '(tags-inherited "foo" "bar"))) + (it "handles 0 arguments" + (expect (org-ql--normalize-query '(tags-inherited)) + :to-equal '(tags-inherited))) + (it "handles 1 argument" + (expect (org-ql--normalize-query '(tags-inherited "foo")) + :to-equal '(tags-inherited "foo"))) + (it "handles 2 arguments" + (expect (org-ql--normalize-query '(tags-inherited "foo" "bar")) + :to-equal '(tags-inherited "foo" "bar"))) + (it "aliases to `itags'" + (expect (org-ql--normalize-query '(itags)) + :to-equal '(tags-inherited))) + (it "aliases to `itags' with one argument" + (expect (org-ql--normalize-query '(itags "foo")) + :to-equal '(tags-inherited "foo"))) + (it "aliases to `itags' with two arguments" + (expect (org-ql--normalize-query '(itags "foo" "bar")) + :to-equal '(tags-inherited "foo" "bar")))) (describe "timestamp predicates" ;; NOTE: (clocked) and (closed) don't accept :with-time arguments. @@ -456,25 +462,29 @@ with keyword arg NOW in PLIST." :to-equal `(ts :from ,(ts-inc 'day 1 beg-of-today-ts) :to ,(ts-inc 'day 1 end-of-today-ts))))))) - (expect (org-ql--normalize-query '(and "string1" "string2")) - :to-equal '(and (regexp "string1") (regexp "string2"))) - (expect (org-ql--normalize-query '(or "string1" "string2")) - :to-equal '(or (regexp "string1") (regexp "string2"))) - (expect (org-ql--normalize-query '(and (todo "TODO") - (or "string1" "string2"))) - :to-equal '(and (todo "TODO") (or (regexp "string1") (regexp "string2")))) - (expect (org-ql--normalize-query '(when (todo "TODO") - (or "string1" "string2"))) - :to-equal '(when (todo "TODO") (or (regexp "string1") (regexp "string2")))) - (expect (org-ql--normalize-query '(when "string-cond1" - (or "string1" "string2"))) - :to-equal '(when (regexp "string-cond1") (or (regexp "string1") (regexp "string2")))) - (expect (org-ql--normalize-query '(when (and "string-cond1" "string-cond2") - (or "string1" "string2"))) - :to-equal '(when (and (regexp "string-cond1") (regexp "string-cond2")) (or (regexp "string1") (regexp "string2")))) - (expect (org-ql--normalize-query '(unless (and "stringcondition1" "stringcond2") - (or "string1" "string2"))) - :to-equal '(unless (and (regexp "stringcondition1") (regexp "stringcond2")) (or (regexp "string1") (regexp "string2")))) + (describe "Plain strings" + (it "normalizes plain strings to the default predicate (using AND)" + (expect (org-ql--normalize-query '(and "string1" "string2")) + :to-equal '(and (regexp "string1") (regexp "string2")))) + (it "normalizes plain strings to the default predicate (using OR)" + (expect (org-ql--normalize-query '(or "string1" "string2")) + :to-equal '(or (regexp "string1") (regexp "string2")))) + (it "normalizes plain strings within sub-expressions to the default predicate" + (expect (org-ql--normalize-query '(and (todo "TODO") + (or "string1" "string2"))) + :to-equal '(and (todo "TODO") (or (regexp "string1") (regexp "string2")))) + (expect (org-ql--normalize-query '(when (todo "TODO") + (or "string1" "string2"))) + :to-equal '(when (todo "TODO") (or (regexp "string1") (regexp "string2")))) + (expect (org-ql--normalize-query '(when "string-cond1" + (or "string1" "string2"))) + :to-equal '(when (regexp "string-cond1") (or (regexp "string1") (regexp "string2")))) + (expect (org-ql--normalize-query '(when (and "string-cond1" "string-cond2") + (or "string1" "string2"))) + :to-equal '(when (and (regexp "string-cond1") (regexp "string-cond2")) (or (regexp "string1") (regexp "string2")))) + (expect (org-ql--normalize-query '(unless (and "stringcondition1" "stringcond2") + (or "string1" "string2"))) + :to-equal '(unless (and (regexp "stringcondition1") (regexp "stringcond2")) (or (regexp "string1") (regexp "string2")))))) ;; FIXME: This test fails, but only on GitHub CI; it works fine ;; locally. It seems to be something to do with Buttercup and From d4f2eb760e2404b98e4b131c4ca472a7f22703b7 Mon Sep 17 00:00:00 2001 From: Adam Porter Date: Thu, 9 Jun 2022 22:10:27 -0500 Subject: [PATCH 12/40] Fix: (src) Normalization Some argument combinations weren't normalized correctly and could cause an infinite loop. This commit doesn't update any corresponding tests, because they are being rearranged in the WIP branch where this commit originates, and teasing them out wouldn't be worth it. --- org-ql.el | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/org-ql.el b/org-ql.el index d92c1f6..ebd3f89 100644 --- a/org-ql.el +++ b/org-ql.el @@ -1668,22 +1668,24 @@ If keyword argument LANG is non-nil, the block must be in that language." :normalizers ((`(,predicate-names . ,args) ;; Rewrite to use keyword args. - (-let (regexps lang keyword-index) - (cond ((plist-get args :lang) - ;; Lang given first, or only lang given. - (setf lang (plist-get args :lang) - regexps (seq-difference args (list :lang lang)))) - ((setf keyword-index (-find-index #'keywordp args)) - ;; Regexps and lang given. - (setf lang (plist-get (cl-subseq args keyword-index) :lang) - regexps (cl-subseq args 0 keyword-index))) - (t ;; Only regexps given. - (setf regexps args))) - (when regexps - ;; This feels awkward and wrong, but we have to quote lists - ;; and avoid quoting nil. There must be a better way. - (setf regexps `(',regexps))) - `(src :lang ,lang :regexps ,@regexps)))) + (cond ((cl-every #'stringp args) + ;; No keywords, only regexps. + `(src :regexps ,args)) + ((and (stringp (car args)) (cl-some #'keywordp args)) + ;; Regexp as first arg with keyword later. + (let* ((keyword-pos (cl-position :lang args)) + (regexps (cl-subseq args 0 keyword-pos)) + ;; We assume that if :lang is given, the string argument follows. + (lang (nth (1+ (cl-position :lang args)) args))) + `(src :lang ,lang + :regexps ,regexps))) + ((keywordp (car args)) + ;; All plist args. + `(src ,@(delq nil + (append (when (plist-get args :lang) + (list :lang (plist-get args :lang))) + (when (plist-get args :regexps) + (list :regexps (plist-get args :regexps)))))))))) :preambles ((`(,predicate-names . ,args) (list :regexp (org-ql--format-src-block-regexp (plist-get args :lang)) ;; Always check contents with predicate. From c8c89be7bce2547e8ff24468680a0eb28f33449c Mon Sep 17 00:00:00 2001 From: Adam Porter Date: Thu, 9 Jun 2022 22:24:53 -0500 Subject: [PATCH 13/40] Meta: Update makem.sh, Makefile --- Makefile | 6 +- makem.sh | 175 +++++++++++++++++++++++++++++++++++++++++++++---------- 2 files changed, 148 insertions(+), 33 deletions(-) diff --git a/Makefile b/Makefile index 9ba1358..64c4516 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ # * makem.sh/Makefile --- Script to aid building and testing Emacs Lisp packages # URL: https://github.com/alphapapa/makem.sh -# Version: 0.3 +# Version: 0.5 # * Arguments @@ -38,7 +38,9 @@ endif verbose = $(v) -ifneq (,$(findstring vv,$(verbose))) +ifneq (,$(findstring vvv,$(verbose))) + VERBOSE = "-vvv" +else ifneq (,$(findstring vv,$(verbose))) VERBOSE = "-vv" else ifneq (,$(findstring v,$(verbose))) VERBOSE = "-v" diff --git a/makem.sh b/makem.sh index b9fc062..d06bca3 100755 --- a/makem.sh +++ b/makem.sh @@ -3,11 +3,11 @@ # * makem.sh --- Script to aid building and testing Emacs Lisp packages # URL: https://github.com/alphapapa/makem.sh -# Version: 0.3 +# Version: 0.6-pre # * Commentary: -# makem.sh is a script helps to build, lint, and test Emacs Lisp +# makem.sh is a script that helps to build, lint, and test Emacs Lisp # packages. It aims to make linting and testing as simple as possible # without requiring per-package configuration. @@ -79,7 +79,7 @@ Rules: Options: -d, --debug Print debug info. -h, --help I need somebody! - -v, --verbose Increase verbosity, up to -vv. + -v, --verbose Increase verbosity, up to -vvv. --no-color Disable color output. --debug-load-path Print load-path from inside Emacs. @@ -136,6 +136,27 @@ EOF echo $file } +function elisp-elint-file { + local file=$(mktemp) + cat >$file <"$file" </dev/null) if [[ $file_pkg ]] @@ -496,6 +570,8 @@ function sandbox { args_sandbox=( --title "makem.sh: $(basename $(pwd)) (sandbox: $sandbox_dir)" --eval "(setq user-emacs-directory (file-truename \"$sandbox_dir\"))" + --load package + --eval "(setq package-user-dir (expand-file-name \"elpa\" user-emacs-directory))" --eval "(setq user-init-file (file-truename \"$init_file\"))" ) @@ -658,7 +734,8 @@ function verbose { if [[ $verbose -ge $1 ]] then [[ $1 -eq 1 ]] && local color_name=blue - [[ $1 -ge 2 ]] && local color_name=cyan + [[ $1 -eq 2 ]] && local color_name=cyan + [[ $1 -ge 3 ]] && local color_name=white shift log_color $color_name "$@" >&2 @@ -706,9 +783,7 @@ function compile-batch { verbose 2 "Batch-compiling files..." debug "Byte-compile files: ${files_project_byte_compile[@]}" - batch-byte-compile "${files_project_byte_compile[@]}" \ - && success "Compiling finished without errors." \ - || error "Compilation failed." + batch-byte-compile "${files_project_byte_compile[@]}" } function compile-each { @@ -726,9 +801,7 @@ function compile-each { || compile_errors=t done - ! [[ $compile_errors ]] \ - && success "Compiling finished without errors." \ - || error "Compilation failed." + [[ ! $compile_errors ]] } function compile { @@ -738,6 +811,18 @@ function compile { else compile-each "$@" fi + local status=$? + + if [[ $compile_error_on_warn ]] + then + # Linting: just return status code, because lint rule will print messages. + [[ $status = 0 ]] + else + # Not linting: print messages here. + [[ $status = 0 ]] \ + && success "Compiling finished without errors." \ + || error "Compiling failed." + fi } function batch { @@ -752,12 +837,15 @@ function batch { function interactive { # Run Emacs interactively. Most useful with --sandbox and --install-deps. + local load_file_args=$(args-load-files "${files_project_feature[@]}" "${files_project_test[@]}") verbose 1 "Running Emacs interactively..." - verbose 2 "Loading files:" "${files_project_feature[@]}" "${files_project_test[@]}" + verbose 2 "Loading files: ${load_file_args//--load /}" + + [[ $compile ]] && compile unset arg_batch run_emacs \ - $(args-load-files "${files_project_feature[@]}" "${files_project_test[@]}") \ + $load_file_args \ --eval "(load user-init-file)" \ "${args_batch_interactive[@]}" arg_batch="--batch" @@ -769,6 +857,9 @@ function lint { lint-checkdoc lint-compile lint-declare + # NOTE: Elint doesn't seem very useful at the moment. See comment + # in lint-elint function. + # lint-elint lint-indent lint-package lint-regexps @@ -825,6 +916,28 @@ function lint-elsa { || error "Linting with Elsa failed." } +function lint-elint { + # NOTE: Elint gives a lot of spurious warnings, apparently because it doesn't load files + # that are `require'd, so its output isn't very useful. But in case it's improved in + # the future, and since this wrapper code already works, we might as well leave it in. + verbose 1 "Linting with Elint..." + + local errors=0 + for file in "${files_project_feature[@]}" + do + verbose 2 "Linting with Elint: $file..." + run_emacs \ + --load "$(elisp-elint-file)" \ + --eval "(makem-elint-file \"$file\")" \ + && verbose 3 "Linting with Elint found no errors." \ + || { error "Linting with Elint failed: $file"; ((errors++)) ; } + done + + [[ $errors = 0 ]] \ + && success "Linting with Elint finished without errors." \ + || error "Linting with Elint failed." +} + function lint-indent { verbose 1 "Linting indentation..." From 0057972069a39fa03f941ee63aa211db3e2177fc Mon Sep 17 00:00:00 2001 From: Adam Porter Date: Thu, 9 Jun 2022 22:45:51 -0500 Subject: [PATCH 14/40] Add: (org-ql-default-predicate) --- README.org | 1 + org-ql.el | 12 +++++++++-- org-ql.info | 59 ++++++++++++++++++++++++++++------------------------- 3 files changed, 42 insertions(+), 30 deletions(-) diff --git a/README.org b/README.org index 64233d0..2a07b87 100644 --- a/README.org +++ b/README.org @@ -539,6 +539,7 @@ Simple links may also be written manually in either sexp or non-sexp form, like: *Added* + Commands ~org-ql-find~, ~org-ql-find-heading~, and ~org-ql-find-path~, which jump to entries selected using Emacs's built-in completion facilities and Org QL queries (like ~helm-org-ql~, but doesn't require Helm.). ++ Option ~org-ql-default-predicate~, applied to plain-string query tokens (before, the ~regexp~ predicate was always used, but now it may be customized). *Changed* + Give more useful error message for invalid queries. diff --git a/org-ql.el b/org-ql.el index ebd3f89..56ab9ea 100644 --- a/org-ql.el +++ b/org-ql.el @@ -298,6 +298,14 @@ See Info node `(org-ql)Queries'." :type 'boolean :risky t) +(defcustom org-ql-default-predicate 'regexp + "Predicate used for plain-string tokens without a specified predicate." + :type '(choice (const heading) + (const heading-regexp) + (const regexp) + (const outline-path) + (const outline-path-segment))) + ;;;; Functions ;;;;; Query execution @@ -862,7 +870,7 @@ value of `org-ql-predicates')." positive-term)) (positive-term (or (and predicate-with-args `(pred args -- (cons (intern pred) args))) (and predicate-without-args `(pred -- (list (intern pred)))) - (and plain-string `(s -- (list 'regexp s))))) + (and plain-string `(s -- (list org-ql-default-predicate s))))) (plain-string (or quoted-arg unquoted-arg)) (predicate-with-args (substring predicate) ":" args) (predicate-without-args (substring predicate) ":") @@ -945,7 +953,7 @@ defined in `org-ql-predicates' by calling `org-ql-defpred'." (`(unless ,condition . ,clauses) `(unless ,(rec condition) ,@(mapcar #'rec clauses))) ;; TODO: Combine (regexp) when appropriate (i.e. inside an OR, not an AND). - ((pred stringp) `(regexp ,element)) + ((pred stringp) `(,org-ql-default-predicate ,element)) ,@normalizer-patterns diff --git a/org-ql.info b/org-ql.info index 89eede3..5205d5c 100644 --- a/org-ql.info +++ b/org-ql.info @@ -1008,6 +1008,9 @@ File: README.info, Node: 07-pre, Next: 062, Up: Changelog ‘org-ql-find-path’, which jump to entries selected using Emacs’s built-in completion facilities and Org QL queries (like ‘helm-org-ql’, but doesn’t require Helm.). + • Option ‘org-ql-default-predicate’, applied to plain-string query + tokens (before, the ‘regexp’ predicate was always used, but now it + may be customized). *Changed* • Give more useful error message for invalid queries. @@ -1656,34 +1659,34 @@ Node: Links36258 Node: Tips36945 Node: Changelog37269 Node: 07-pre38037 -Node: 06238465 -Node: 06138773 -Node: 0639341 -Node: 05242395 -Node: 05142695 -Node: 0543118 -Node: 04944647 -Node: 04844927 -Node: 04745274 -Node: 04645683 -Node: 04546091 -Node: 04446452 -Node: 04346811 -Node: 04247014 -Node: 04147175 -Node: 0447422 -Node: 03251523 -Node: 03151926 -Node: 0352123 -Node: 02355423 -Node: 02255657 -Node: 02155937 -Node: 0256142 -Node: 0160220 -Node: Notes60321 -Node: Comparison with Org Agenda searches60483 -Node: org-sidebar61372 -Node: License61651 +Node: 06238642 +Node: 06138950 +Node: 0639518 +Node: 05242572 +Node: 05142872 +Node: 0543295 +Node: 04944824 +Node: 04845104 +Node: 04745451 +Node: 04645860 +Node: 04546268 +Node: 04446629 +Node: 04346988 +Node: 04247191 +Node: 04147352 +Node: 0447599 +Node: 03251700 +Node: 03152103 +Node: 0352300 +Node: 02355600 +Node: 02255834 +Node: 02156114 +Node: 0256319 +Node: 0160397 +Node: Notes60498 +Node: Comparison with Org Agenda searches60660 +Node: org-sidebar61549 +Node: License61828  End Tag Table From 968a249ae36574f0f4f623363b6a7c2e27af1fc8 Mon Sep 17 00:00:00 2001 From: Adam Porter Date: Thu, 9 Jun 2022 22:54:57 -0500 Subject: [PATCH 15/40] Add: Query coalescing for AND clauses A later commit will update and add tests. --- README.org | 3 ++ org-ql.el | 92 ++++++++++++++++++++++++++++++++++++++++++++++++++--- org-ql.info | 60 ++++++++++++++++++---------------- 3 files changed, 123 insertions(+), 32 deletions(-) diff --git a/README.org b/README.org index 2a07b87..25f3257 100644 --- a/README.org +++ b/README.org @@ -544,6 +544,9 @@ Simple links may also be written manually in either sexp or non-sexp form, like: *Changed* + Give more useful error message for invalid queries. +*Internal* ++ Certain query predicates, when called multiple times in an ~and~ sub-expression, are optimized to a single call. + ** 0.6.2 *Fixed* diff --git a/org-ql.el b/org-ql.el index 56ab9ea..1cb78ad 100644 --- a/org-ql.el +++ b/org-ql.el @@ -694,6 +694,56 @@ returns nil." ;; partially typed queries in the Helm commands. (define-error 'org-ql-invalid-query "Invalid Org QL query" 'user-error) +(defun org-ql--coalesce-ands (query) + "Return QUERY having coalesced any AND'ed clauses' predicates. +Multiple calls to the same predicate within an `and' expression +are coalesced into a single call to the predicate. + +Note that this is a relatively simple function which does not +comprehensively coalesce every call that could be. For example, +if QUERY contained four calls to the `src' predicate with two +unique language arguments, only the calls for one language would +be coalesced." + ;; TODO: Use a per-predicate alist-getting function that accounts + ;; for arguments which must be unique...maybe...someday... + + ;; NOTE: This implentation can sometimes reorder sub-expressions, + ;; like: + ;; + ;; (and (src :regexps ("foo") :lang "elisp") (src :regexps ("bar"))) + ;; + ;; becomes: + ;; + ;; (and (src :regexps ("bar")) (src :regexps ("foo") :lang "elisp")) + ;; + ;; because the first one could be coalescable, but the second one + ;; can't be coalesced with it since they don't specify the same + ;; language. That could be fixed, but it's probably not worth it. + (cl-labels ((rec (sexp) + (pcase sexp + (`(,(and boolean (or 'or 'not)) . ,sexps) + `(,boolean ,@(mapcar #'rec sexps))) + (`(and . ,sexps) + (anded sexps)) + (_ sexp))) + (anded (sexps) + (let (anded-predicates new-sexp) + (dolist (sexp sexps) + (pcase sexp + (`(,(or 'or 'not) . ,_) + (push (rec sexp) new-sexp)) + (`(,predicate . ,args) + (pcase-exhaustive (plist-get (alist-get predicate org-ql-predicates) :coalesce-multiple-calls) + (`nil (push sexp new-sexp)) + (`t (setf (alist-get predicate anded-predicates) + (append (alist-get predicate anded-predicates) args))) + ((and fn (pred functionp)) + (if-let (new-args (funcall fn (alist-get predicate anded-predicates) args)) + (setf (alist-get predicate anded-predicates) new-args) + (push sexp new-sexp))))))) + (delq nil `(and ,@(nreverse new-sexp) ,@(nreverse anded-predicates)))))) + (rec query))) + (defun org-ql--sanity-check-form (form) "Signal error if any forms in FORM do not have preconditions met. Or, when possible, fix the problem." @@ -967,7 +1017,7 @@ defined in `org-ql-predicates' by calling `org-ql-defpred'." (setf query new-query) (when (eq (cl-incf count) limit) (error "Query normalization limit exceeded: QUERY:%S" query))) - finally return new-query))))))) + finally return (org-ql--coalesce-ands new-query)))))))) (defun org-ql--define-query-preamble-fn (predicates) "Define function `org-ql--query-preamble' for PREDICATES. @@ -1037,7 +1087,7 @@ defined in `org-ql-predicates' by calling `org-ql-defpred'." ;; function still works. But to avoid the warning, we byte-compile it afterward. (byte-compile 'org-ql--query-preamble))) -(cl-defmacro org-ql-defpred (name args docstring &key body preambles normalizers) +(cl-defmacro org-ql-defpred (name args docstring &key body preambles normalizers coalesce-multiple-calls) "Define an `org-ql' selector predicate named `org-ql--predicate-NAME'. NAME may be a symbol or a list of symbols: if a list, the first is used as NAME and the rest are aliases. A function is only @@ -1091,6 +1141,22 @@ to variables bound in the pattern: query expression with no work to do, which improves performance. +When COALESCE-MULTIPLE-CALLS is t, multiple calls to this +predicate within a query clause may be combined into a single +call to this predicate (so it is expected that this predicate +treats multiple arguments as being boolean AND'ed together). + +This value may also be a function called to do coalescing of two +predicate expressions. It is called with two arguments: the list +of already-coalesced arguments to an expression, and the list of +arguments to the call being coalesced (note that a query's +arguments are normalized before the query is coalesced). If it +returns nil, the expression is not coalesced; otherwise, it +should return a new list of arguments coalescing the given +arguments, with new arguments being first. (This is useful, +e.g. when a predicate takes keyword arguments, which means that +multiple calls to it can't be simply appended.) + For convenience, within the `pcase' patterns, the symbol `predicate-names' is a special form which is replaced with a pattern matching any of the predicate's name and aliases. For @@ -1107,6 +1173,8 @@ It would be expanded to: ((`(,(or 'heading 'h) . ,args) `(heading ,@args)))" + ;; FIXME: Update defpred tutorial to include :coalesce-multiple-calls. + ;; NOTE: The debug form works, completely! For example, use `edebug-defun' ;; on the `heading' predicate, then evaluate this form: ;; (let* ((query '(heading "HEADING")) @@ -1116,7 +1184,8 @@ It would be expanded to: ;; :normalized normalized ;; :preamble preamble)) (declare (debug ([&or symbolp listp] listp stringp - &rest [&or [":body" def-body] + &rest [&or [":coalesce-multiple-calls" form] + [":body" def-body] [":normalizers" (&rest (sexp def-body))] [":preambles" (&rest (sexp def-body))]])) (indent defun)) @@ -1137,7 +1206,8 @@ It would be expanded to: ;; SOMEDAY: Use `map-elt' here, after map 2.1 can be automatically installed in CI sandbox... (setf (alist-get ',predicate-name org-ql-predicates) `(:name ,',name :aliases ,',aliases :fn ,',fn-name :docstring ,(\, docstring) :args ,',args - :normalizers ,',normalizers :preambles ,',preambles)) + :normalizers ,',normalizers :preambles ,',preambles + :coalesce-multiple-calls ,,coalesce-multiple-calls)) (unless org-ql-defpred-defer ;; Reversing preserves the order in which predicates were defined. (org-ql--define-normalize-query-fn (reverse org-ql-predicates)) @@ -1320,6 +1390,7 @@ Org effort string, like \"5\" or \"0:05\"." (org-ql-defpred (heading h) (&rest strings) "Return non-nil if current entry's heading matches all STRINGS. Matching is done case-insensitively." + :coalesce-multiple-calls t :normalizers ((`(,predicate-names . ,args) ;; "h" alias. `(heading ,@args))) @@ -1346,6 +1417,7 @@ Matching is done case-insensitively." (org-ql-defpred (heading-regexp h*) (&rest regexps) "Return non-nil if current entry's heading matches all REGEXPS (regexp strings). Matching is done case-insensitively." + :coalesce-multiple-calls t :normalizers ((`(,predicate-names . ,args) ;; "h" alias. `(heading-regexp ,@args))) @@ -1498,6 +1570,7 @@ the following queries: (olp \"Food\" \"Fruits\") (olp \"Fruits\" \"Grapes\") (olp \"Food\" \"Grapes\")" + :coalesce-multiple-calls t :normalizers ((`(,predicate-names . ,strings) ;; Regexp quote headings. `(org-ql--predicate-outline-path ,@(mapcar #'regexp-quote strings)))) @@ -1652,6 +1725,7 @@ priority B)." (org-ql-defpred (regexp r) (&rest regexps) "Return non-nil if current entry matches all of REGEXPS (regexp strings)." + :coalesce-multiple-calls t :normalizers ((`(,predicate-names . ,args) `(regexp ,@args))) ;; MAYBE: Separate case-sensitive (Regexp) predicate. @@ -1674,6 +1748,15 @@ priority B)." "Return non-nil if current entry contains an Org source block matching all of REGEXPS. If keyword argument LANG is non-nil, the block must be in that language." + :coalesce-multiple-calls (lambda (coalesced-args current-args) + (when (or (not coalesced-args) + (equal (plist-get current-args :lang) + (plist-get coalesced-args :lang))) + (setf (plist-get coalesced-args :regexps) + (append (plist-get coalesced-args :regexps) + (plist-get current-args :regexps)) + (plist-get coalesced-args :lang) (plist-get current-args :lang)) + coalesced-args)) :normalizers ((`(,predicate-names . ,args) ;; Rewrite to use keyword args. (cond ((cl-every #'stringp args) @@ -1739,6 +1822,7 @@ Tests both inherited and local tags." (org-ql-defpred (tags-all tags&) (&rest tags) "Return non-nil if current heading has all of TAGS (a list of strings). Tests both inherited and local tags." + :coalesce-multiple-calls t ;; MAYBE: -all versions for inherited and local. :normalizers ((`(,predicate-names . ,tags) `(and ,@(--map `(tags ,it) tags)))) diff --git a/org-ql.info b/org-ql.info index 5205d5c..684a19e 100644 --- a/org-ql.info +++ b/org-ql.info @@ -1015,6 +1015,10 @@ File: README.info, Node: 07-pre, Next: 062, Up: Changelog *Changed* • Give more useful error message for invalid queries. + *Internal* + • Certain query predicates, when called multiple times in an ‘and’ + sub-expression, are optimized to a single call. +  File: README.info, Node: 062, Next: 061, Prev: 07-pre, Up: Changelog @@ -1659,34 +1663,34 @@ Node: Links36258 Node: Tips36945 Node: Changelog37269 Node: 07-pre38037 -Node: 06238642 -Node: 06138950 -Node: 0639518 -Node: 05242572 -Node: 05142872 -Node: 0543295 -Node: 04944824 -Node: 04845104 -Node: 04745451 -Node: 04645860 -Node: 04546268 -Node: 04446629 -Node: 04346988 -Node: 04247191 -Node: 04147352 -Node: 0447599 -Node: 03251700 -Node: 03152103 -Node: 0352300 -Node: 02355600 -Node: 02255834 -Node: 02156114 -Node: 0256319 -Node: 0160397 -Node: Notes60498 -Node: Comparison with Org Agenda searches60660 -Node: org-sidebar61549 -Node: License61828 +Node: 06238786 +Node: 06139094 +Node: 0639662 +Node: 05242716 +Node: 05143016 +Node: 0543439 +Node: 04944968 +Node: 04845248 +Node: 04745595 +Node: 04646004 +Node: 04546412 +Node: 04446773 +Node: 04347132 +Node: 04247335 +Node: 04147496 +Node: 0447743 +Node: 03251844 +Node: 03152247 +Node: 0352444 +Node: 02355744 +Node: 02255978 +Node: 02156258 +Node: 0256463 +Node: 0160541 +Node: Notes60642 +Node: Comparison with Org Agenda searches60804 +Node: org-sidebar61693 +Node: License61972  End Tag Table From 4f29bcfd67aa88bf8f45b9b580e8f3ac84465700 Mon Sep 17 00:00:00 2001 From: Adam Porter Date: Thu, 9 Jun 2022 23:03:48 -0500 Subject: [PATCH 16/40] Add: (rifle/smart) Predicate --- README.org | 4 ++ org-ql.el | 26 ++++++++++++- org-ql.info | 106 ++++++++++++++++++++++++++++++---------------------- 3 files changed, 90 insertions(+), 46 deletions(-) diff --git a/README.org b/README.org index 25f3257..b89765a 100644 --- a/README.org +++ b/README.org @@ -236,6 +236,9 @@ Arguments are listed next to predicate names, where applicable. + =property (property &optional value)= :: Return non-nil if current entry has ~PROPERTY~ (a string), and optionally ~VALUE~ (a string). Note that property inheritance is currently /not/ enabled for this predicate. If you need to test with inheritance, you could use a custom predicate form, like ~(org-entry-get (point) "PROPERTY" 'inherit)~. + =regexp (&rest regexps)= :: Return non-nil if current entry matches all of ~REGEXPS~ (regexp strings). Matches against entire entry, from beginning of its heading to the next heading. - Aliases: =r=. ++ =rifle (&rest strings)= :: Return non-nil if each string is found in either the entry or its outline path. Works like =org-rifle=. This is probably the most useful, intuitive, general-purpose predicate. + - Aliases: ~smart~. + - *Note:* By default, this is the default predicate used for plain-string query tokens (i.e. given without a specified predicate). This can be customized with the option ~org-ql-default-predicate~. + ~src (&key lang regexps)~ :: Return non-nil if current entry contains an Org Babel source block. If ~LANG~ is non-nil, match blocks of that language. If ~REGEXPS~ is non-nil, require that block's contents match all regexps. + =tags (&optional tags)= :: Return non-nil if current heading has one or more of ~TAGS~ (a list of strings). Tests both inherited and local tags. + =tags-inherited (&optional tags)= :: Return non-nil if current heading's inherited tags include one or more of ~TAGS~ (a list of strings). If ~TAGS~ is nil, return non-nil if heading has any inherited tags. @@ -539,6 +542,7 @@ Simple links may also be written manually in either sexp or non-sexp form, like: *Added* + Commands ~org-ql-find~, ~org-ql-find-heading~, and ~org-ql-find-path~, which jump to entries selected using Emacs's built-in completion facilities and Org QL queries (like ~helm-org-ql~, but doesn't require Helm.). ++ Predicate ~rifle~, which matches an entry if each of the given arguments is found in either the entry's contents or its outline path. This provides very intuitive results, mimicing the behavior of [[https://github.com/alphapapa/org-rifle][=org-rifle=]]. In fact, the results are so useful that it's now the default predicate for plain-string query tokens. (It is also aliased to ~smart~, since it's so "smart," and not all users have used =org-rifle=.) + Option ~org-ql-default-predicate~, applied to plain-string query tokens (before, the ~regexp~ predicate was always used, but now it may be customized). *Changed* diff --git a/org-ql.el b/org-ql.el index 1cb78ad..3e54283 100644 --- a/org-ql.el +++ b/org-ql.el @@ -298,11 +298,13 @@ See Info node `(org-ql)Queries'." :type 'boolean :risky t) -(defcustom org-ql-default-predicate 'regexp +(defcustom org-ql-default-predicate 'rifle "Predicate used for plain-string tokens without a specified predicate." :type '(choice (const heading) (const heading-regexp) (const regexp) + (const rifle) + (const smart) (const outline-path) (const outline-path-segment))) @@ -1555,6 +1557,28 @@ any link is found." (string-match-p description-or-target (match-string org-ql-link-description-group))))))))) +(org-ql-defpred (rifle smart) (&rest strings) + "Return non-nil if each string argument is found in either the entry or its outline path. +Works like `org-rifle'. This is probably the most useful, +intuitive, general-purpose predicate." + ;; NOTE: This predicate advertises that it takes strings, but they + ;; are normalized to regexps. Because of that, we must use a + ;; coalescing function. + :coalesce-multiple-calls (lambda (coalesced-args current-args) + (setf (plist-get coalesced-args :regexps) + (list 'quote (append (cadr (plist-get coalesced-args :regexps)) + (cadr (plist-get current-args :regexps))))) + coalesced-args) + :normalizers ((`(,predicate-names . ,(and rest (guard (cl-every #'stringp rest)))) + ;; If this doesn't match, it's already normalized. + `(rifle :regexps ',(mapcar #'regexp-quote rest)))) + :preambles ((`(,predicate-names :regexps ',regexps) + (list :regexp (rx-to-string `(seq bow (or ,@(mapcar (lambda (s) `(regexp ,s)) regexps)))) + :case-fold t :query query))) + :body (cl-loop for regexp in (plist-get strings :regexps) + always (or (org-ql--predicate-regexp regexp) + (org-ql--predicate-outline-path regexp)))) + ;; MAYBE: Preambles for outline-path predicates. Not sure if possible without complicated logic. ;; FIXME: These preds say they accept regexps but the strings get regexp-quoted. They should probably just take strings. diff --git a/org-ql.info b/org-ql.info index 684a19e..25ccf74 100644 --- a/org-ql.info +++ b/org-ql.info @@ -483,6 +483,15 @@ Arguments are listed next to predicate names, where applicable. strings). Matches against entire entry, from beginning of its heading to the next heading. • Aliases: ‘r’. +‘rifle (&rest strings)’ + Return non-nil if each string is found in either the entry or its + outline path. Works like ‘org-rifle’. This is probably the most + useful, intuitive, general-purpose predicate. + • Aliases: ‘smart’. + • *Note:* By default, this is the default predicate used for + plain-string query tokens (i.e. given without a specified + predicate). This can be customized with the option + ‘org-ql-default-predicate’. ‘src (&key lang regexps)’ Return non-nil if current entry contains an Org Babel source block. If ‘LANG’ is non-nil, match blocks of that language. If ‘REGEXPS’ @@ -1008,6 +1017,13 @@ File: README.info, Node: 07-pre, Next: 062, Up: Changelog ‘org-ql-find-path’, which jump to entries selected using Emacs’s built-in completion facilities and Org QL queries (like ‘helm-org-ql’, but doesn’t require Helm.). + • Predicate ‘rifle’, which matches an entry if each of the given + arguments is found in either the entry’s contents or its outline + path. This provides very intuitive results, mimicing the behavior + of ‘org-rifle’ (https://github.com/alphapapa/org-rifle). In fact, + the results are so useful that it’s now the default predicate for + plain-string query tokens. (It is also aliased to ‘smart’, since + it’s so "smart," and not all users have used ‘org-rifle’.) • Option ‘org-ql-default-predicate’, applied to plain-string query tokens (before, the ‘regexp’ predicate was always used, but now it may be customized). @@ -1646,51 +1662,51 @@ Node: org-ql-sparse-tree8345 Node: Queries9145 Node: Non-sexp query syntax10262 Node: General predicates12021 -Node: Ancestor/descendant predicates18260 -Node: Date/time predicates19388 -Node: Functions / Macros22512 -Node: Agenda-like views22810 -Ref: Function ‘org-ql-block’22972 -Node: Listing / acting-on results24233 -Ref: Caching24441 -Ref: Function ‘org-ql-select’25354 -Ref: Function ‘org-ql-query’27780 -Ref: Macro ‘org-ql’ (deprecated)29554 -Node: Custom predicates29869 -Ref: Macro ‘org-ql-defpred’30093 -Node: Dynamic block33534 -Node: Links36258 -Node: Tips36945 -Node: Changelog37269 -Node: 07-pre38037 -Node: 06238786 -Node: 06139094 -Node: 0639662 -Node: 05242716 -Node: 05143016 -Node: 0543439 -Node: 04944968 -Node: 04845248 -Node: 04745595 -Node: 04646004 -Node: 04546412 -Node: 04446773 -Node: 04347132 -Node: 04247335 -Node: 04147496 -Node: 0447743 -Node: 03251844 -Node: 03152247 -Node: 0352444 -Node: 02355744 -Node: 02255978 -Node: 02156258 -Node: 0256463 -Node: 0160541 -Node: Notes60642 -Node: Comparison with Org Agenda searches60804 -Node: org-sidebar61693 -Node: License61972 +Node: Ancestor/descendant predicates18763 +Node: Date/time predicates19891 +Node: Functions / Macros23015 +Node: Agenda-like views23313 +Ref: Function ‘org-ql-block’23475 +Node: Listing / acting-on results24736 +Ref: Caching24944 +Ref: Function ‘org-ql-select’25857 +Ref: Function ‘org-ql-query’28283 +Ref: Macro ‘org-ql’ (deprecated)30057 +Node: Custom predicates30372 +Ref: Macro ‘org-ql-defpred’30596 +Node: Dynamic block34037 +Node: Links36761 +Node: Tips37448 +Node: Changelog37772 +Node: 07-pre38540 +Node: 06239801 +Node: 06140109 +Node: 0640677 +Node: 05243731 +Node: 05144031 +Node: 0544454 +Node: 04945983 +Node: 04846263 +Node: 04746610 +Node: 04647019 +Node: 04547427 +Node: 04447788 +Node: 04348147 +Node: 04248350 +Node: 04148511 +Node: 0448758 +Node: 03252859 +Node: 03153262 +Node: 0353459 +Node: 02356759 +Node: 02256993 +Node: 02157273 +Node: 0257478 +Node: 0161556 +Node: Notes61657 +Node: Comparison with Org Agenda searches61819 +Node: org-sidebar62708 +Node: License62987  End Tag Table From 73ef71ced7cf003955ecb311441802623aa6741a Mon Sep 17 00:00:00 2001 From: Adam Porter Date: Thu, 9 Jun 2022 22:09:46 -0500 Subject: [PATCH 17/40] Comment: Add FIXME --- org-ql.el | 1 + 1 file changed, 1 insertion(+) diff --git a/org-ql.el b/org-ql.el index 3e54283..e89108e 100644 --- a/org-ql.el +++ b/org-ql.el @@ -1628,6 +1628,7 @@ contiguous segment of the outline path: (org-ql-defpred path (&rest regexps) "Return non-nil if current heading's buffer's filename path matches any of REGEXPS (regexp strings). Without arguments, return non-nil if buffer is file-backed." + ;; FIXME: This should AND the regexps together, not OR them. :body (when (buffer-file-name) (cl-typecase regexps (null t) From 6602f32165ae638a040b252acd3d17263b6974e0 Mon Sep 17 00:00:00 2001 From: Adam Porter Date: Thu, 9 Jun 2022 23:21:07 -0500 Subject: [PATCH 18/40] Tests: Update (add, reorganize) They all pass now (with a temporary, uncommitted workaround in makem.sh for a Buttercup bug: ). --- tests/test-org-ql.el | 999 +++++++++++++++++++++++-------------------- 1 file changed, 542 insertions(+), 457 deletions(-) diff --git a/tests/test-org-ql.el b/tests/test-org-ql.el index 0839688..41ba1f6 100644 --- a/tests/test-org-ql.el +++ b/tests/test-org-ql.el @@ -205,475 +205,556 @@ with keyword arg NOW in PLIST." (expect (org-ql--value-at (point-min) #'org-get-local-tags) :to-be nil)))) - (describe "Query functions/macros" - - (it "org-ql-select" - (expect (length (org-ql-select org-ql-test-buffer - '(category) - :sort 'deadline)) - :to-equal org-ql-test-num-headings)) - (it "org-ql-query" - (expect (length (org-ql-query :select 'element - :from org-ql-test-buffer - :where '(category) - :order-by 'date)) - :to-equal org-ql-test-num-headings))) - (describe "Query pre-processing" - (describe "(level)" - (it "with one level" - (expect (org-ql--normalize-query '(level "1")) - :to-equal '(level 1))) - (it "with two levels" - (expect (org-ql--normalize-query '(level "1" "2")) - :to-equal '(level 1 2))) - (it "with a comparator and a level" - (expect (org-ql--normalize-query '(level ">" "1")) - :to-equal '(level > 1)))) + (describe "Coalescing" + ;; NOTE: Queries are expected to be normalized before being coalesced, + ;; and `org-ql--normalize-query' calls `org-ql--coalesce-ands' as its + ;; final step, so in these tests we just call the former. + (it "coalesces a single AND clause that uses one predicate (and preserves argument order)" + (expect (org-ql--normalize-query '(and (rifle "foo") (rifle "bar"))) + :to-equal '(and (rifle :regexps '("foo" "bar"))))) + (it "coalesces a single AND clause that uses two predicates (and preserves predicate order)" + (expect (org-ql--normalize-query '(and (rifle "foo") (rifle "bar") + (heading "baz") (heading "buz"))) + :to-equal '(and (rifle :regexps '("foo" "bar")) (heading "baz" "buz")))) + (it "preserves independent OR clauses" + (expect (org-ql--normalize-query '(and (or (rifle "foo") (rifle "bar")) + (or (rifle "baz") (rifle "buz")))) + :to-equal '(and (or (rifle :regexps '("foo")) (rifle :regexps '("bar"))) (or (rifle :regexps '("baz")) (rifle :regexps '("buz")))))) + (it "coalesces an AND clause within an OR clause" + (expect (org-ql--normalize-query '(or (regexp "bar") (and (rifle "foo") (rifle "bar")))) + :to-equal '(or (regexp "bar") (and (rifle :regexps '("foo" "bar")))))) + (it "coalesces multiple AND clauses within an OR clause" + (expect (org-ql--normalize-query '(or (and (rifle "foo") (rifle "bar")) + (and (rifle "baz") (rifle "buz")))) + :to-equal '(or (and (rifle :regexps '("foo" "bar"))) (and (rifle :regexps '("baz" "buz")))))) + (it "coalesces arguments to predicates which use coalescing functions and whose calls are eligible for coalescing" + (expect (org-ql--normalize-query '(and (src "foo") (src "bar"))) + :to-equal '(and (src :lang nil :regexps ("foo" "bar")))) + (expect (org-ql--normalize-query '(and (src "foo" :lang "elisp") (src "bar" :lang "elisp"))) + :to-equal '(and (src :lang "elisp" :regexps ("foo" "bar"))))) + (it "does not coalesce arguments to predicates which use coalescing functions and whose calls are ineligible for coalescing" + (expect (org-ql--normalize-query '(and (src "foo" :lang "elisp") (src "bar"))) + ;; NOTE: The current implementation of `org-ql--normalize-query' + ;; reorders clauses in this case. Fixing that would probably + ;; not be worth the effort in code or runtime. + :to-equal '(and (src :regexps ("bar")) (src :lang "elisp" :regexps ("foo")))) + (expect (org-ql--normalize-query '(and (src "foo" :lang "elisp") (src "bar" :lang "python"))) + :to-equal '(and (src :lang "python" :regexps ("bar")) (src :lang "elisp" :regexps ("foo")))))) - (describe "(link)" - (it "with one argument" - (expect (org-ql--normalize-query '(link "DESC-OR-TARGET")) - :to-equal '(link "DESC-OR-TARGET"))) - (it "with one argument and :regexp-p" - (expect (org-ql--normalize-query '(link "DESC-OR-TARGET" :regexp-p t)) - :to-equal '(link "DESC-OR-TARGET" :regexp-p t))) - (it "with keyword arguments" - (expect (org-ql--normalize-query '(link :description "DESCRIPTION" :target "TARGET" - :regexp-p t)) - :to-equal '(link :description "DESCRIPTION" :target "TARGET" - :regexp-p t)))) + (describe "Normalization" - (describe "(outline-path)" - (it "with a regexp metacharacter" - ;; Ensures that normalizer doesn't infinitely loop. - (expect (org-ql--normalize-query '(olp "a." "b")) - :to-equal '(org-ql--predicate-outline-path "a\\." "b")))) + (it "Default predicate" + (expect (org-ql--normalize-query "scheduled") + ;; No colon after keyword, so not a predicate query. + :to-equal '(rifle :regexps '("scheduled"))) + (expect (org-ql--normalize-query "\"quoted phrase\"") + :to-equal '(rifle :regexps '("\"quoted phrase\"")))) - (describe "(tags-inherited)" - (it "handles 0 arguments" - (expect (org-ql--normalize-query '(tags-inherited)) - :to-equal '(tags-inherited))) - (it "handles 1 argument" - (expect (org-ql--normalize-query '(tags-inherited "foo")) - :to-equal '(tags-inherited "foo"))) - (it "handles 2 arguments" - (expect (org-ql--normalize-query '(tags-inherited "foo" "bar")) - :to-equal '(tags-inherited "foo" "bar"))) - (it "aliases to `itags'" - (expect (org-ql--normalize-query '(itags)) - :to-equal '(tags-inherited))) - (it "aliases to `itags' with one argument" - (expect (org-ql--normalize-query '(itags "foo")) - :to-equal '(tags-inherited "foo"))) - (it "aliases to `itags' with two arguments" - (expect (org-ql--normalize-query '(itags "foo" "bar")) - :to-equal '(tags-inherited "foo" "bar")))) + (describe "Plain strings" + (it "normalizes plain strings to the default predicate (using AND)" + (expect (org-ql--normalize-query '(and "string1" "string2")) + :to-equal '(and (rifle :regexps '("string1" "string2"))))) + (it "normalizes plain strings to the default predicate (using OR)" + (expect (org-ql--normalize-query '(or "string1" "string2")) + :to-equal '(or (rifle :regexps '("string1")) (rifle :regexps '("string2"))))) + (it "normalizes plain strings within sub-expressions to the default predicate" + (expect (org-ql--normalize-query '(and (todo "TODO") + (or "string1" "string2"))) + :to-equal '(and (todo "TODO") (or (rifle :regexps '("string1")) (rifle :regexps '("string2"))))) + (expect (org-ql--normalize-query '(when (todo "TODO") + (or "string1" "string2"))) + :to-equal '(when (todo "TODO") (or (rifle :regexps '("string1")) (rifle :regexps '("string2"))))) + (expect (org-ql--normalize-query '(when "string-cond1" + (or "string1" "string2"))) + :to-equal '(when (rifle :regexps '("string-cond1")) (or (rifle :regexps '("string1")) (rifle :regexps '("string2"))))) + (expect (org-ql--normalize-query '(when (and "string-cond1" "string-cond2") + (or "string1" "string2"))) + :to-equal '(when (and (rifle :regexps '("string-cond1")) (rifle :regexps '("string-cond2"))) (or (rifle :regexps '("string1")) (rifle :regexps '("string2"))))) + (expect (org-ql--normalize-query '(unless (and "stringcondition1" "stringcond2") + (or "string1" "string2"))) + :to-equal '(unless (and (rifle :regexps '("stringcondition1")) (rifle :regexps '("stringcond2"))) (or (rifle :regexps '("string1")) (rifle :regexps '("string2"))))))) - (describe "timestamp predicates" - ;; NOTE: (clocked) and (closed) don't accept :with-time arguments. - (describe "(clocked)" - (let ((beg-of-today-ts (->> (ts-now) - (ts-apply :hour 0 :minute 0 :second 0))) - (end-of-today-ts (->> (ts-now) - (ts-apply :hour 23 :minute 59 :second 59)))) - ;; MAYBE: While it seems helpful for (clocked) and (close) to - ;; implicitly look into the past (because entries can't be - ;; clocked or closed in the future), it makes the API - ;; inconsistent. It would be better to be consistent and - ;; require the user to pass these predicates a negative number. - (it "with a number" - (expect (org-ql--normalize-query '(clocked 0)) - :to-equal `(clocked :from ,beg-of-today-ts)) - (expect (org-ql--normalize-query '(clocked 1)) - :to-equal `(clocked :from ,(ts-inc 'day -1 beg-of-today-ts)))) - (it ":from/:to/:on" - (expect (org-ql--normalize-query '(clocked :on today)) - :to-equal `(clocked :from ,beg-of-today-ts :to ,end-of-today-ts)) - (expect (org-ql--normalize-query '(clocked :on 0)) - :to-equal `(clocked :from ,beg-of-today-ts :to ,end-of-today-ts)) - (expect (org-ql--normalize-query '(clocked :on 1)) - :to-equal `(clocked :from ,(ts-inc 'day 1 beg-of-today-ts) - :to ,(ts-inc 'day 1 end-of-today-ts)))))) - (describe "(closed)" - (let ((beg-of-today-ts (->> (ts-now) - (ts-apply :hour 0 :minute 0 :second 0))) - (end-of-today-ts (->> (ts-now) - (ts-apply :hour 23 :minute 59 :second 59)))) - (it "with a number" - (expect (org-ql--normalize-query '(closed 0)) - :to-equal `(closed :from ,beg-of-today-ts)) - (expect (org-ql--normalize-query '(closed 1)) - :to-equal `(closed :from ,(ts-inc 'day -1 beg-of-today-ts)))) - (it ":from/:to/:on" - (expect (org-ql--normalize-query '(closed :on today)) - :to-equal `(closed :from ,beg-of-today-ts :to ,end-of-today-ts)) - (expect (org-ql--normalize-query '(closed :on 0)) - :to-equal `(closed :from ,beg-of-today-ts :to ,end-of-today-ts)) - (expect (org-ql--normalize-query '(closed :on 1)) - :to-equal `(closed :from ,(ts-inc 'day 1 beg-of-today-ts) - :to ,(ts-inc 'day 1 end-of-today-ts)))))) + (describe "(rifle)" + (it "with one argument" + (expect (org-ql--normalize-query '(rifle "foo.")) + :to-equal '(rifle :regexps '("foo\\.")))) + (it "with two arguments" + (expect (org-ql--normalize-query '(rifle "foo." "bar")) + :to-equal '(rifle :regexps '("foo\\." "bar"))))) - ;; NOTE: The rest of them do accept :with-time. - (describe "(deadline)" - (let ((beg-of-today-ts (->> (ts-now) - (ts-apply :hour 0 :minute 0 :second 0))) - (end-of-today-ts (->> (ts-now) - (ts-apply :hour 23 :minute 59 :second 59)))) - (it "with auto" - (let ((auto-ts (->> (ts-now) - (ts-adjust 'day org-deadline-warning-days) - (ts-apply :hour 23 :minute 59 :second 59)))) - (expect (org-ql--normalize-query '(deadline auto)) - :to-equal `(deadline-warning :to ,auto-ts)) - (expect (org-ql--normalize-query '(deadline auto :with-time t)) - :to-equal `(deadline-warning :to ,auto-ts :with-time t)) - (expect (org-ql--normalize-query '(deadline auto :with-time nil)) - :to-equal `(deadline-warning :to ,auto-ts :with-time nil)))) - (it "with a number" - (expect (org-ql--normalize-query '(deadline 0)) - :to-equal `(deadline :to ,end-of-today-ts)) - (expect (org-ql--normalize-query '(deadline 0 :with-time t)) - :to-equal `(deadline :to ,end-of-today-ts :with-time t)) - (expect (org-ql--normalize-query '(deadline 0 :with-time nil)) - :to-equal `(deadline :to ,end-of-today-ts :with-time nil)) - (expect (org-ql--normalize-query '(deadline 2)) - :to-equal `(deadline :to ,(ts-inc 'day 2 end-of-today-ts))) - (expect (org-ql--normalize-query '(deadline 2 :with-time nil)) - :to-equal `(deadline :to ,(ts-inc 'day 2 end-of-today-ts) - :with-time nil)) - (expect (org-ql--normalize-query '(deadline 2 :with-time t)) - :to-equal `(deadline :to ,(ts-inc 'day 2 end-of-today-ts) - :with-time t))) - (it ":from/:to/:on" - (expect (org-ql--normalize-query '(deadline :on today)) - :to-equal `(deadline :from ,beg-of-today-ts :to ,end-of-today-ts)) - (expect (org-ql--normalize-query '(deadline :on today :with-time t)) - :to-equal `(deadline :from ,beg-of-today-ts :to ,end-of-today-ts - :with-time t)) - (expect (org-ql--normalize-query '(deadline :on today :with-time nil)) - :to-equal `(deadline :from ,beg-of-today-ts :to ,end-of-today-ts - :with-time nil)) - (expect (org-ql--normalize-query '(deadline :on 0)) - :to-equal `(deadline :from ,beg-of-today-ts :to ,end-of-today-ts)) - (expect (org-ql--normalize-query '(deadline :on 0 :with-time t)) - :to-equal `(deadline :from ,beg-of-today-ts :to ,end-of-today-ts - :with-time t)) - (expect (org-ql--normalize-query '(deadline :on 1)) - :to-equal `(deadline :from ,(ts-inc 'day 1 beg-of-today-ts) - :to ,(ts-inc 'day 1 end-of-today-ts)))))) - (describe "(scheduled)" - (let ((beg-of-today-ts (->> (ts-now) - (ts-apply :hour 0 :minute 0 :second 0))) - (end-of-today-ts (->> (ts-now) - (ts-apply :hour 23 :minute 59 :second 59)))) - (it "with a number" - (expect (org-ql--normalize-query '(scheduled 0)) - :to-equal `(scheduled :from ,beg-of-today-ts :to ,end-of-today-ts)) - (expect (org-ql--normalize-query '(scheduled 0 :with-time t)) - :to-equal `(scheduled :from ,beg-of-today-ts :to ,end-of-today-ts - :with-time t)) - (expect (org-ql--normalize-query '(scheduled 0 :with-time nil)) - :to-equal `(scheduled :from ,beg-of-today-ts :to ,end-of-today-ts - :with-time nil)) - (expect (org-ql--normalize-query '(scheduled 1)) - :to-equal `(scheduled :from ,(ts-inc 'day 1 beg-of-today-ts) - :to ,(ts-inc 'day 1 end-of-today-ts)))) - (it ":from/:to/:on" - (expect (org-ql--normalize-query '(scheduled :on today)) - :to-equal `(scheduled :from ,beg-of-today-ts :to ,end-of-today-ts)) - (expect (org-ql--normalize-query '(scheduled :on today :with-time t)) - :to-equal `(scheduled :from ,beg-of-today-ts :to ,end-of-today-ts - :with-time t)) - (expect (org-ql--normalize-query '(scheduled :on today :with-time nil)) - :to-equal `(scheduled :from ,beg-of-today-ts :to ,end-of-today-ts - :with-time nil)) - (expect (org-ql--normalize-query '(scheduled :on 0)) - :to-equal `(scheduled :from ,beg-of-today-ts :to ,end-of-today-ts)) - (expect (org-ql--normalize-query '(scheduled :on 0 :with-time t)) - :to-equal `(scheduled :from ,beg-of-today-ts :to ,end-of-today-ts - :with-time t)) - (expect (org-ql--normalize-query '(scheduled :on 1)) - :to-equal `(scheduled :from ,(ts-inc 'day 1 beg-of-today-ts) + (describe "(level)" + (it "with one level" + (expect (org-ql--normalize-query '(level "1")) + :to-equal '(level 1))) + (it "with two levels" + (expect (org-ql--normalize-query '(level "1" "2")) + :to-equal '(level 1 2))) + (it "with a comparator and a level" + (expect (org-ql--normalize-query '(level ">" "1")) + :to-equal '(level > 1)))) + + (describe "(link)" + (it "with one argument" + (expect (org-ql--normalize-query '(link "DESC-OR-TARGET")) + :to-equal '(link "DESC-OR-TARGET"))) + (it "with one argument and :regexp-p" + (expect (org-ql--normalize-query '(link "DESC-OR-TARGET" :regexp-p t)) + :to-equal '(link "DESC-OR-TARGET" :regexp-p t))) + (it "with keyword arguments" + (expect (org-ql--normalize-query '(link :description "DESCRIPTION" :target "TARGET" + :regexp-p t)) + :to-equal '(link :description "DESCRIPTION" :target "TARGET" + :regexp-p t)))) + + (describe "(outline-path)" + (it "with a regexp metacharacter" + ;; Ensures that normalizer doesn't infinitely loop. + (expect (org-ql--normalize-query '(olp "a." "b")) + :to-equal '(org-ql--predicate-outline-path "a\\." "b")))) + + (describe "(src)" + (it "normalizes a non-keyword arg to keywords" + (expect (org-ql--normalize-query '(src "foo")) + :to-equal '(src :regexps ("foo")))) + (it "normalizes non-keyword args to keywords" + (expect (org-ql--normalize-query '(src "foo" "bar")) + :to-equal '(src :regexps ("foo" "bar")))) + (it "normalizes a non-keyword arg with a :lang keyword arg to keywords" + (expect (org-ql--normalize-query '(src "foo" :lang "bar")) + :to-equal '(src :lang "bar" :regexps ("foo")))) + (it "normalizes non-keyword args with a :lang keyword arg to keywords" + (expect (org-ql--normalize-query '(src "foo" "bar" :lang "baz")) + :to-equal '(src :lang "baz" :regexps ("foo" "bar")))) + (it "normalizes all-keyword args without looping" + (expect (org-ql--normalize-query '(src :regexps ("foo") :lang "bar")) + :to-equal '(src :lang "bar" :regexps ("foo"))) + (expect (org-ql--normalize-query '(src :regexps ("foo") :lang)) + :to-equal '(src :regexps ("foo"))))) + + (describe "(tags-inherited)" + (it "handles 0 arguments" + (expect (org-ql--normalize-query '(tags-inherited)) + :to-equal '(tags-inherited))) + (it "handles 1 argument" + (expect (org-ql--normalize-query '(tags-inherited "foo")) + :to-equal '(tags-inherited "foo"))) + (it "handles 2 arguments" + (expect (org-ql--normalize-query '(tags-inherited "foo" "bar")) + :to-equal '(tags-inherited "foo" "bar"))) + (it "aliases to `itags'" + (expect (org-ql--normalize-query '(itags)) + :to-equal '(tags-inherited))) + (it "aliases to `itags' with one argument" + (expect (org-ql--normalize-query '(itags "foo")) + :to-equal '(tags-inherited "foo"))) + (it "aliases to `itags' with two arguments" + (expect (org-ql--normalize-query '(itags "foo" "bar")) + :to-equal '(tags-inherited "foo" "bar")))) + + (describe "timestamp predicates" + ;; NOTE: (clocked) and (closed) don't accept :with-time arguments. + (describe "(clocked)" + (let ((beg-of-today-ts (->> (ts-now) + (ts-apply :hour 0 :minute 0 :second 0))) + (end-of-today-ts (->> (ts-now) + (ts-apply :hour 23 :minute 59 :second 59)))) + ;; MAYBE: While it seems helpful for (clocked) and (close) to + ;; implicitly look into the past (because entries can't be + ;; clocked or closed in the future), it makes the API + ;; inconsistent. It would be better to be consistent and + ;; require the user to pass these predicates a negative number. + (it "with a number" + (expect (org-ql--normalize-query '(clocked 0)) + :to-equal `(clocked :from ,beg-of-today-ts)) + (expect (org-ql--normalize-query '(clocked 1)) + :to-equal `(clocked :from ,(ts-inc 'day -1 beg-of-today-ts)))) + (it ":from/:to/:on" + (expect (org-ql--normalize-query '(clocked :on today)) + :to-equal `(clocked :from ,beg-of-today-ts :to ,end-of-today-ts)) + (expect (org-ql--normalize-query '(clocked :on 0)) + :to-equal `(clocked :from ,beg-of-today-ts :to ,end-of-today-ts)) + (expect (org-ql--normalize-query '(clocked :on 1)) + :to-equal `(clocked :from ,(ts-inc 'day 1 beg-of-today-ts) :to ,(ts-inc 'day 1 end-of-today-ts)))))) - (describe "(planning)" - (let ((beg-of-today-ts (->> (ts-now) - (ts-apply :hour 0 :minute 0 :second 0))) - (end-of-today-ts (->> (ts-now) - (ts-apply :hour 23 :minute 59 :second 59)))) - (it "with a number" - (expect (org-ql--normalize-query '(planning 0)) - :to-equal `(planning :to ,end-of-today-ts)) - (expect (org-ql--normalize-query '(planning 0 :with-time t)) - :to-equal `(planning :to ,end-of-today-ts :with-time t)) - (expect (org-ql--normalize-query '(planning 0 :with-time nil)) - :to-equal `(planning :to ,end-of-today-ts :with-time nil)) - (expect (org-ql--normalize-query '(planning 1)) - :to-equal `(planning :to ,(ts-inc 'day 1 end-of-today-ts)))) - (it ":from/:to/:on" - (expect (org-ql--normalize-query '(planning :on today)) - :to-equal `(planning :from ,beg-of-today-ts :to ,end-of-today-ts)) - (expect (org-ql--normalize-query '(planning :on today :with-time t)) - :to-equal `(planning :from ,beg-of-today-ts :to ,end-of-today-ts - :with-time t)) - (expect (org-ql--normalize-query '(planning :on today :with-time nil)) - :to-equal `(planning :from ,beg-of-today-ts :to ,end-of-today-ts - :with-time nil)) - (expect (org-ql--normalize-query '(planning :on 0)) - :to-equal `(planning :from ,beg-of-today-ts :to ,end-of-today-ts)) - (expect (org-ql--normalize-query '(planning :on 0 :with-time t)) - :to-equal `(planning :from ,beg-of-today-ts :to ,end-of-today-ts - :with-time t)) - (expect (org-ql--normalize-query '(planning :on 1)) - :to-equal `(planning :from ,(ts-inc 'day 1 beg-of-today-ts) + (describe "(closed)" + (let ((beg-of-today-ts (->> (ts-now) + (ts-apply :hour 0 :minute 0 :second 0))) + (end-of-today-ts (->> (ts-now) + (ts-apply :hour 23 :minute 59 :second 59)))) + (it "with a number" + (expect (org-ql--normalize-query '(closed 0)) + :to-equal `(closed :from ,beg-of-today-ts)) + (expect (org-ql--normalize-query '(closed 1)) + :to-equal `(closed :from ,(ts-inc 'day -1 beg-of-today-ts)))) + (it ":from/:to/:on" + (expect (org-ql--normalize-query '(closed :on today)) + :to-equal `(closed :from ,beg-of-today-ts :to ,end-of-today-ts)) + (expect (org-ql--normalize-query '(closed :on 0)) + :to-equal `(closed :from ,beg-of-today-ts :to ,end-of-today-ts)) + (expect (org-ql--normalize-query '(closed :on 1)) + :to-equal `(closed :from ,(ts-inc 'day 1 beg-of-today-ts) :to ,(ts-inc 'day 1 end-of-today-ts)))))) - (describe "(ts)" - (let ((beg-of-today-ts (->> (ts-now) - (ts-apply :hour 0 :minute 0 :second 0))) - (end-of-today-ts (->> (ts-now) - (ts-apply :hour 23 :minute 59 :second 59)))) - (it "with a number" - ;; NOTE: For consistency with the other ts-related predicates, a number argument means ":to NUMBER". - ;; MAYBE: Mention this in docs. Or remove this, because it's ambiguous. - (expect (org-ql--normalize-query '(ts 0)) - :to-equal `(ts :to ,end-of-today-ts)) - (expect (org-ql--normalize-query '(ts 0 :with-time t)) - :to-equal `(ts :to ,end-of-today-ts :with-time t)) - (expect (org-ql--normalize-query '(ts 0 :with-time nil)) - :to-equal `(ts :to ,end-of-today-ts :with-time nil)) - (expect (org-ql--normalize-query '(ts 1)) - :to-equal `(ts :to ,(ts-inc 'day 1 end-of-today-ts)))) - (it ":from/:to/:on" - (expect (org-ql--normalize-query '(ts :on today)) - :to-equal `(ts :from ,beg-of-today-ts :to ,end-of-today-ts)) - (expect (org-ql--normalize-query '(ts :on today :with-time t)) - :to-equal `(ts :from ,beg-of-today-ts :to ,end-of-today-ts - :with-time t)) - (expect (org-ql--normalize-query '(ts :on today :with-time nil)) - :to-equal `(ts :from ,beg-of-today-ts :to ,end-of-today-ts - :with-time nil)) - (expect (org-ql--normalize-query '(ts :on 0)) - :to-equal `(ts :from ,beg-of-today-ts :to ,end-of-today-ts)) - (expect (org-ql--normalize-query '(ts :on 0 :with-time t)) - :to-equal `(ts :from ,beg-of-today-ts :to ,end-of-today-ts - :with-time t)) - (expect (org-ql--normalize-query '(ts :on 1)) - :to-equal `(ts :from ,(ts-inc 'day 1 beg-of-today-ts) - :to ,(ts-inc 'day 1 end-of-today-ts))))))) - (describe "Plain strings" - (it "normalizes plain strings to the default predicate (using AND)" - (expect (org-ql--normalize-query '(and "string1" "string2")) - :to-equal '(and (regexp "string1") (regexp "string2")))) - (it "normalizes plain strings to the default predicate (using OR)" - (expect (org-ql--normalize-query '(or "string1" "string2")) - :to-equal '(or (regexp "string1") (regexp "string2")))) - (it "normalizes plain strings within sub-expressions to the default predicate" - (expect (org-ql--normalize-query '(and (todo "TODO") - (or "string1" "string2"))) - :to-equal '(and (todo "TODO") (or (regexp "string1") (regexp "string2")))) - (expect (org-ql--normalize-query '(when (todo "TODO") - (or "string1" "string2"))) - :to-equal '(when (todo "TODO") (or (regexp "string1") (regexp "string2")))) - (expect (org-ql--normalize-query '(when "string-cond1" - (or "string1" "string2"))) - :to-equal '(when (regexp "string-cond1") (or (regexp "string1") (regexp "string2")))) - (expect (org-ql--normalize-query '(when (and "string-cond1" "string-cond2") - (or "string1" "string2"))) - :to-equal '(when (and (regexp "string-cond1") (regexp "string-cond2")) (or (regexp "string1") (regexp "string2")))) - (expect (org-ql--normalize-query '(unless (and "stringcondition1" "stringcond2") - (or "string1" "string2"))) - :to-equal '(unless (and (regexp "stringcondition1") (regexp "stringcond2")) (or (regexp "string1") (regexp "string2")))))) + ;; NOTE: The rest of them do accept :with-time. + (describe "(deadline)" + (let ((beg-of-today-ts (->> (ts-now) + (ts-apply :hour 0 :minute 0 :second 0))) + (end-of-today-ts (->> (ts-now) + (ts-apply :hour 23 :minute 59 :second 59)))) + (it "with auto" + (let ((auto-ts (->> (ts-now) + (ts-adjust 'day org-deadline-warning-days) + (ts-apply :hour 23 :minute 59 :second 59)))) + (expect (org-ql--normalize-query '(deadline auto)) + :to-equal `(deadline-warning :to ,auto-ts)) + (expect (org-ql--normalize-query '(deadline auto :with-time t)) + :to-equal `(deadline-warning :to ,auto-ts :with-time t)) + (expect (org-ql--normalize-query '(deadline auto :with-time nil)) + :to-equal `(deadline-warning :to ,auto-ts :with-time nil)))) + (it "with a number" + (expect (org-ql--normalize-query '(deadline 0)) + :to-equal `(deadline :to ,end-of-today-ts)) + (expect (org-ql--normalize-query '(deadline 0 :with-time t)) + :to-equal `(deadline :to ,end-of-today-ts :with-time t)) + (expect (org-ql--normalize-query '(deadline 0 :with-time nil)) + :to-equal `(deadline :to ,end-of-today-ts :with-time nil)) + (expect (org-ql--normalize-query '(deadline 2)) + :to-equal `(deadline :to ,(ts-inc 'day 2 end-of-today-ts))) + (expect (org-ql--normalize-query '(deadline 2 :with-time nil)) + :to-equal `(deadline :to ,(ts-inc 'day 2 end-of-today-ts) + :with-time nil)) + (expect (org-ql--normalize-query '(deadline 2 :with-time t)) + :to-equal `(deadline :to ,(ts-inc 'day 2 end-of-today-ts) + :with-time t))) + (it ":from/:to/:on" + (expect (org-ql--normalize-query '(deadline :on today)) + :to-equal `(deadline :from ,beg-of-today-ts :to ,end-of-today-ts)) + (expect (org-ql--normalize-query '(deadline :on today :with-time t)) + :to-equal `(deadline :from ,beg-of-today-ts :to ,end-of-today-ts + :with-time t)) + (expect (org-ql--normalize-query '(deadline :on today :with-time nil)) + :to-equal `(deadline :from ,beg-of-today-ts :to ,end-of-today-ts + :with-time nil)) + (expect (org-ql--normalize-query '(deadline :on 0)) + :to-equal `(deadline :from ,beg-of-today-ts :to ,end-of-today-ts)) + (expect (org-ql--normalize-query '(deadline :on 0 :with-time t)) + :to-equal `(deadline :from ,beg-of-today-ts :to ,end-of-today-ts + :with-time t)) + (expect (org-ql--normalize-query '(deadline :on 1)) + :to-equal `(deadline :from ,(ts-inc 'day 1 beg-of-today-ts) + :to ,(ts-inc 'day 1 end-of-today-ts)))))) + (describe "(scheduled)" + (let ((beg-of-today-ts (->> (ts-now) + (ts-apply :hour 0 :minute 0 :second 0))) + (end-of-today-ts (->> (ts-now) + (ts-apply :hour 23 :minute 59 :second 59)))) + (it "with a number" + (expect (org-ql--normalize-query '(scheduled 0)) + :to-equal `(scheduled :from ,beg-of-today-ts :to ,end-of-today-ts)) + (expect (org-ql--normalize-query '(scheduled 0 :with-time t)) + :to-equal `(scheduled :from ,beg-of-today-ts :to ,end-of-today-ts + :with-time t)) + (expect (org-ql--normalize-query '(scheduled 0 :with-time nil)) + :to-equal `(scheduled :from ,beg-of-today-ts :to ,end-of-today-ts + :with-time nil)) + (expect (org-ql--normalize-query '(scheduled 1)) + :to-equal `(scheduled :from ,(ts-inc 'day 1 beg-of-today-ts) + :to ,(ts-inc 'day 1 end-of-today-ts)))) + (it ":from/:to/:on" + (expect (org-ql--normalize-query '(scheduled :on today)) + :to-equal `(scheduled :from ,beg-of-today-ts :to ,end-of-today-ts)) + (expect (org-ql--normalize-query '(scheduled :on today :with-time t)) + :to-equal `(scheduled :from ,beg-of-today-ts :to ,end-of-today-ts + :with-time t)) + (expect (org-ql--normalize-query '(scheduled :on today :with-time nil)) + :to-equal `(scheduled :from ,beg-of-today-ts :to ,end-of-today-ts + :with-time nil)) + (expect (org-ql--normalize-query '(scheduled :on 0)) + :to-equal `(scheduled :from ,beg-of-today-ts :to ,end-of-today-ts)) + (expect (org-ql--normalize-query '(scheduled :on 0 :with-time t)) + :to-equal `(scheduled :from ,beg-of-today-ts :to ,end-of-today-ts + :with-time t)) + (expect (org-ql--normalize-query '(scheduled :on 1)) + :to-equal `(scheduled :from ,(ts-inc 'day 1 beg-of-today-ts) + :to ,(ts-inc 'day 1 end-of-today-ts)))))) + (describe "(planning)" + (let ((beg-of-today-ts (->> (ts-now) + (ts-apply :hour 0 :minute 0 :second 0))) + (end-of-today-ts (->> (ts-now) + (ts-apply :hour 23 :minute 59 :second 59)))) + (it "with a number" + (expect (org-ql--normalize-query '(planning 0)) + :to-equal `(planning :to ,end-of-today-ts)) + (expect (org-ql--normalize-query '(planning 0 :with-time t)) + :to-equal `(planning :to ,end-of-today-ts :with-time t)) + (expect (org-ql--normalize-query '(planning 0 :with-time nil)) + :to-equal `(planning :to ,end-of-today-ts :with-time nil)) + (expect (org-ql--normalize-query '(planning 1)) + :to-equal `(planning :to ,(ts-inc 'day 1 end-of-today-ts)))) + (it ":from/:to/:on" + (expect (org-ql--normalize-query '(planning :on today)) + :to-equal `(planning :from ,beg-of-today-ts :to ,end-of-today-ts)) + (expect (org-ql--normalize-query '(planning :on today :with-time t)) + :to-equal `(planning :from ,beg-of-today-ts :to ,end-of-today-ts + :with-time t)) + (expect (org-ql--normalize-query '(planning :on today :with-time nil)) + :to-equal `(planning :from ,beg-of-today-ts :to ,end-of-today-ts + :with-time nil)) + (expect (org-ql--normalize-query '(planning :on 0)) + :to-equal `(planning :from ,beg-of-today-ts :to ,end-of-today-ts)) + (expect (org-ql--normalize-query '(planning :on 0 :with-time t)) + :to-equal `(planning :from ,beg-of-today-ts :to ,end-of-today-ts + :with-time t)) + (expect (org-ql--normalize-query '(planning :on 1)) + :to-equal `(planning :from ,(ts-inc 'day 1 beg-of-today-ts) + :to ,(ts-inc 'day 1 end-of-today-ts)))))) + (describe "(ts)" + (let ((beg-of-today-ts (->> (ts-now) + (ts-apply :hour 0 :minute 0 :second 0))) + (end-of-today-ts (->> (ts-now) + (ts-apply :hour 23 :minute 59 :second 59)))) + (it "with a number" + ;; NOTE: For consistency with the other ts-related predicates, a number argument means ":to NUMBER". + ;; MAYBE: Mention this in docs. Or remove this, because it's ambiguous. + (expect (org-ql--normalize-query '(ts 0)) + :to-equal `(ts :to ,end-of-today-ts)) + (expect (org-ql--normalize-query '(ts 0 :with-time t)) + :to-equal `(ts :to ,end-of-today-ts :with-time t)) + (expect (org-ql--normalize-query '(ts 0 :with-time nil)) + :to-equal `(ts :to ,end-of-today-ts :with-time nil)) + (expect (org-ql--normalize-query '(ts 1)) + :to-equal `(ts :to ,(ts-inc 'day 1 end-of-today-ts)))) + (it ":from/:to/:on" + (expect (org-ql--normalize-query '(ts :on today)) + :to-equal `(ts :from ,beg-of-today-ts :to ,end-of-today-ts)) + (expect (org-ql--normalize-query '(ts :on today :with-time t)) + :to-equal `(ts :from ,beg-of-today-ts :to ,end-of-today-ts + :with-time t)) + (expect (org-ql--normalize-query '(ts :on today :with-time nil)) + :to-equal `(ts :from ,beg-of-today-ts :to ,end-of-today-ts + :with-time nil)) + (expect (org-ql--normalize-query '(ts :on 0)) + :to-equal `(ts :from ,beg-of-today-ts :to ,end-of-today-ts)) + (expect (org-ql--normalize-query '(ts :on 0 :with-time t)) + :to-equal `(ts :from ,beg-of-today-ts :to ,end-of-today-ts + :with-time t)) + (expect (org-ql--normalize-query '(ts :on 1)) + :to-equal `(ts :from ,(ts-inc 'day 1 beg-of-today-ts) + :to ,(ts-inc 'day 1 end-of-today-ts))) - ;; FIXME: This test fails, but only on GitHub CI; it works fine - ;; locally. It seems to be something to do with Buttercup and - ;; comparing the structs. It only started happening after I moved - ;; the predicate argument processing into the newer - ;; --normalize-from-to-on macro. I can't explain why it works - ;; fine locally, even in a clean sandbox with newly installed - ;; packages, yet fails entirely on GitHub CI. The only - ;; possibility I can think of might be a difference in how the - ;; Emacs being installed into CI is built, but that seems very - ;; unlikely, so I don't know. For now, I have no alternative but - ;; to comment out the test. But it doesn't matter much, anyway, - ;; because I know it works properly. + ;; FIXME: This test fails, but only on GitHub CI; it works fine + ;; locally. It seems to be something to do with Buttercup and + ;; comparing the structs. It only started happening after I moved + ;; the predicate argument processing into the newer + ;; --normalize-from-to-on macro. I can't explain why it works + ;; fine locally, even in a clean sandbox with newly installed + ;; packages, yet fails entirely on GitHub CI. The only + ;; possibility I can think of might be a difference in how the + ;; Emacs being installed into CI is built, but that seems very + ;; unlikely, so I don't know. For now, I have no alternative but + ;; to comment out the test. But it doesn't matter much, anyway, + ;; because I know it works properly. - ;; (expect (org-ql--normalize-query '(or (ts-active :on "2019-01-01") - ;; (ts-a :on "2019-01-01") - ;; (ts-inactive :on "2019-01-01") - ;; (ts-i :on "2019-01-01"))) - ;; :to-equal `(or (ts :type active - ;; :from ,(make-ts :unix 1546322400.0) - ;; :to ,(make-ts :unix 1546408799.0)) - ;; (ts :type active - ;; :from ,(make-ts :unix 1546322400.0) - ;; :to ,(make-ts :unix 1546408799.0)) - ;; (ts :type inactive - ;; :from ,(make-ts :unix 1546322400.0) - ;; :to ,(make-ts :unix 1546408799.0)) - ;; (ts :type inactive - ;; :from ,(make-ts :unix 1546322400.0) - ;; :to ,(make-ts :unix 1546408799.0)))) - ) + ;; (expect (org-ql--normalize-query '(or (ts-active :on "2019-01-01") + ;; (ts-a :on "2019-01-01") + ;; (ts-inactive :on "2019-01-01") + ;; (ts-i :on "2019-01-01"))) + ;; :to-equal `(or (ts :type active + ;; :from ,(make-ts :unix 1546322400.0) + ;; :to ,(make-ts :unix 1546408799.0)) + ;; (ts :type active + ;; :from ,(make-ts :unix 1546322400.0) + ;; :to ,(make-ts :unix 1546408799.0)) + ;; (ts :type inactive + ;; :from ,(make-ts :unix 1546322400.0) + ;; :to ,(make-ts :unix 1546408799.0)) + ;; (ts :type inactive + ;; :from ,(make-ts :unix 1546322400.0) + ;; :to ,(make-ts :unix 1546408799.0))))) + ))))) - (describe "Query preambles" + (describe "Query preambles" - ;; TODO: Other predicates. + ;; TODO: Other predicates. - (describe "(clocked)" - (it "without arguments" - (expect (org-ql--query-preamble '(clocked)) - :to-equal (list :query t - :preamble org-ql-clock-regexp - :preamble-case-fold nil))) - (it "with a number of days" - (expect (org-ql--query-preamble '(clocked 1)) - :to-equal (list :query t - :preamble org-ql-clock-regexp - :preamble-case-fold nil))) - ;; TODO: Other arguments for (clocked). - ) + (describe "(rifle)" + (it "with one argument" + (expect (org-ql--query-preamble (org-ql--normalize-query '(rifle "foo."))) + :to-equal (list :query '(rifle :regexps '("foo\\.")) + :preamble "\\(?:\\<\\(?:foo\\.\\)\\)" + :preamble-case-fold t))) + (it "with two arguments" + (expect (org-ql--query-preamble (org-ql--normalize-query '(rifle "foo." "bar"))) + :to-equal (list :query '(rifle :regexps '("foo\\." "bar")) + :preamble "\\(?:\\<\\(?:foo\\.\\|bar\\)\\)" + :preamble-case-fold t)))) - (describe "(level)" - (it "with a number" - (expect (org-ql--query-preamble '(level 2)) - :to-equal (list :query t - :preamble (rx bol (repeat 2 "*") " ") - :preamble-case-fold t))) - (it "with two numbers" - (expect (org-ql--query-preamble '(level 2 4)) - :to-equal (list :query t - :preamble (rx bol (repeat 2 4 "*") " ") - :preamble-case-fold t))) - (it "<" - (expect (org-ql--query-preamble '(level < 3)) - :to-equal (list :query t - :preamble (rx bol (repeat 1 2 "*") " ") - :preamble-case-fold t))) - (it "<=" - (expect (org-ql--query-preamble '(level <= 2)) - :to-equal (list :query t - :preamble (rx bol (repeat 1 2 "*") " ") - :preamble-case-fold t))) - (it ">" - (expect (org-ql--query-preamble '(level > 2)) - :to-equal (list :query t - :preamble (rx bol (>= 3 "*") " ") - :preamble-case-fold t))) - (it ">=" - (expect (org-ql--query-preamble '(level >= 2)) - :to-equal (list :query t - :preamble (rx bol (>= 2 "*") " ") - :preamble-case-fold t))))) + (describe "(clocked)" + (it "without arguments" + (expect (org-ql--query-preamble '(clocked)) + :to-equal (list :query t + :preamble org-ql-clock-regexp + :preamble-case-fold nil))) + (it "with a number of days" + (expect (org-ql--query-preamble '(clocked 1)) + :to-equal (list :query t + :preamble org-ql-clock-regexp + :preamble-case-fold nil))) + ;; TODO: Other arguments for (clocked). + ) - (describe "Plain query parsing" + (describe "(level)" + (it "with a number" + (expect (org-ql--query-preamble '(level 2)) + :to-equal (list :query t + :preamble (rx bol (repeat 2 "*") " ") + :preamble-case-fold t))) + (it "with two numbers" + (expect (org-ql--query-preamble '(level 2 4)) + :to-equal (list :query t + :preamble (rx bol (repeat 2 4 "*") " ") + :preamble-case-fold t))) + (it "<" + (expect (org-ql--query-preamble '(level < 3)) + :to-equal (list :query t + :preamble (rx bol (repeat 1 2 "*") " ") + :preamble-case-fold t))) + (it "<=" + (expect (org-ql--query-preamble '(level <= 2)) + :to-equal (list :query t + :preamble (rx bol (repeat 1 2 "*") " ") + :preamble-case-fold t))) + (it ">" + (expect (org-ql--query-preamble '(level > 2)) + :to-equal (list :query t + :preamble (rx bol (>= 3 "*") " ") + :preamble-case-fold t))) + (it ">=" + (expect (org-ql--query-preamble '(level >= 2)) + :to-equal (list :query t + :preamble (rx bol (>= 2 "*") " ") + :preamble-case-fold t))))) - ;; TODO: Other predicates. + (describe "Plain query parsing" - (it "Negated terms" - (expect (org-ql--query-string-to-sexp "todo: !todo:CHECK,SOMEDAY") - :to-equal '(and (todo) (not (todo "CHECK" "SOMEDAY")))) - (expect (org-ql--query-string-to-sexp "!todo:CHECK,SOMEDAY todo:") - :to-equal '(and (not (todo "CHECK" "SOMEDAY")) (todo))) - (expect (org-ql--query-string-to-sexp "tags:universe !moon") - :to-equal '(and (tags "universe") (not (regexp "moon")))) - (expect (org-ql--query-string-to-sexp "!moon tags:universe") - :to-equal '(and (not (regexp "moon")) (tags "universe"))) - (expect (org-ql--query-string-to-sexp "mars !ts:on=today") - :to-equal '(and (regexp "mars") (not (ts :on "today")))) - (expect (org-ql--query-string-to-sexp "!\"quoted phrase\"") - :to-equal '(not (regexp "quoted phrase")))) - (it "Regexp predicates" - (expect (org-ql--query-string-to-sexp "scheduled") - ;; No colon after keyword, so not a predicate query. - :to-equal '(regexp "scheduled")) - (expect (org-ql--query-string-to-sexp "\"quoted phrase\"") - :to-equal '(regexp "quoted phrase")) - (expect (org-ql--query-string-to-sexp "regexp:word") - :to-equal '(regexp "word")) - (expect (org-ql--query-string-to-sexp "regexp:\"quoted phrase\"") - :to-equal '(regexp "quoted phrase"))) - (it "Timestamp-based predicates" - (expect (org-ql--query-string-to-sexp "scheduled:on=2017-07-07") - :to-equal '(scheduled :on "2017-07-07")) - (expect (org-ql--query-string-to-sexp "deadline:from=2017-07-07,to=2017-07-09") - :to-equal '(deadline :from "2017-07-07" :to "2017-07-09")) - (expect (org-ql--query-string-to-sexp "planning:from=2017-07-07") - :to-equal '(planning :from "2017-07-07")) - (expect (org-ql--query-string-to-sexp "closed:from=2017-07-07") - :to-equal '(closed :from "2017-07-07")) - (expect (org-ql--query-string-to-sexp "ts-active:to=2017-07-07") - :to-equal '(ts-active :to "2017-07-07")) - (expect (org-ql--query-string-to-sexp "ts-inactive:to=2017-07-07") - :to-equal '(ts-inactive :to "2017-07-07")) - (expect (org-ql--query-string-to-sexp "ts-a:to=2017-07-07") - :to-equal '(ts-a :to "2017-07-07")) - (expect (org-ql--query-string-to-sexp "ts-i:on=2017-07-07") - :to-equal '(ts-i :on "2017-07-07")) - (expect (org-ql--query-string-to-sexp "ts:") - :to-equal '(ts)) - (expect (org-ql--query-string-to-sexp "clocked:") - :to-equal '(clocked))) - (it "To-do predicates" - (expect (org-ql--query-string-to-sexp "todo:") - :to-equal '(todo)) - (expect (org-ql--query-string-to-sexp "todo:TODO") - :to-equal '(todo "TODO")) - (expect (org-ql--query-string-to-sexp "todo:TODO,SOMEDAY") - :to-equal '(todo "TODO" "SOMEDAY"))) - (it "Compound queries" - (expect (org-ql--query-string-to-sexp "todo:SOMEDAY ts-a:from=2020-01-01,to=2021-01-01") - :to-equal '(and (todo "SOMEDAY") (ts-a :from "2020-01-01" :to "2021-01-01"))) - (expect (org-ql--query-string-to-sexp "regexp:\"quoted phrase\" todo:SOMEDAY") - :to-equal '(and (regexp "quoted phrase") (todo "SOMEDAY"))))) + ;; TODO: Other predicates. - (describe "Convert sexp queries to non-sexp queries" + (it "Negated terms" + (expect (org-ql--query-string-to-sexp "todo: !todo:CHECK,SOMEDAY") + :to-equal '(and (todo) (not (todo "CHECK" "SOMEDAY")))) + (expect (org-ql--query-string-to-sexp "!todo:CHECK,SOMEDAY todo:") + :to-equal '(and (not (todo "CHECK" "SOMEDAY")) (todo))) + (expect (org-ql--query-string-to-sexp "tags:universe !moon") + :to-equal '(and (tags "universe") (not (rifle "moon")))) + (expect (org-ql--query-string-to-sexp "!moon tags:universe") + :to-equal '(and (not (rifle "moon")) (tags "universe"))) + (expect (org-ql--query-string-to-sexp "mars !ts:on=today") + :to-equal '(and (rifle "mars") (not (ts :on "today")))) + (expect (org-ql--query-string-to-sexp "!\"quoted phrase\"") + :to-equal '(not (rifle "quoted phrase")))) + (it "Regexp predicates" + (expect (org-ql--query-string-to-sexp "regexp:word") + :to-equal '(regexp "word")) + (expect (org-ql--query-string-to-sexp "regexp:\"quoted phrase\"") + :to-equal '(regexp "quoted phrase"))) + (it "Timestamp-based predicates" + (expect (org-ql--query-string-to-sexp "scheduled:on=2017-07-07") + :to-equal '(scheduled :on "2017-07-07")) + (expect (org-ql--query-string-to-sexp "deadline:from=2017-07-07,to=2017-07-09") + :to-equal '(deadline :from "2017-07-07" :to "2017-07-09")) + (expect (org-ql--query-string-to-sexp "planning:from=2017-07-07") + :to-equal '(planning :from "2017-07-07")) + (expect (org-ql--query-string-to-sexp "closed:from=2017-07-07") + :to-equal '(closed :from "2017-07-07")) + (expect (org-ql--query-string-to-sexp "ts-active:to=2017-07-07") + :to-equal '(ts-active :to "2017-07-07")) + (expect (org-ql--query-string-to-sexp "ts-inactive:to=2017-07-07") + :to-equal '(ts-inactive :to "2017-07-07")) + (expect (org-ql--query-string-to-sexp "ts-a:to=2017-07-07") + :to-equal '(ts-a :to "2017-07-07")) + (expect (org-ql--query-string-to-sexp "ts-i:on=2017-07-07") + :to-equal '(ts-i :on "2017-07-07")) + (expect (org-ql--query-string-to-sexp "ts:") + :to-equal '(ts)) + (expect (org-ql--query-string-to-sexp "clocked:") + :to-equal '(clocked))) + (it "To-do predicates" + (expect (org-ql--query-string-to-sexp "todo:") + :to-equal '(todo)) + (expect (org-ql--query-string-to-sexp "todo:TODO") + :to-equal '(todo "TODO")) + (expect (org-ql--query-string-to-sexp "todo:TODO,SOMEDAY") + :to-equal '(todo "TODO" "SOMEDAY"))) + (it "Compound queries" + (expect (org-ql--query-string-to-sexp "todo:SOMEDAY ts-a:from=2020-01-01,to=2021-01-01") + :to-equal '(and (todo "SOMEDAY") (ts-a :from "2020-01-01" :to "2021-01-01"))) + (expect (org-ql--query-string-to-sexp "regexp:\"quoted phrase\" todo:SOMEDAY") + :to-equal '(and (regexp "quoted phrase") (todo "SOMEDAY"))))) - ;; FIXME: Test (src) after converting it is implemented. - ;; (src :lang "elisp" :regexps ("defun")) + (describe "Convert sexp queries to non-sexp queries" - ;; MAYBE: Other predicates? Or should these cover the other - ;; cases, because the others use the same format? + ;; FIXME: Test (src) after converting it is implemented. + ;; (src :lang "elisp" :regexps ("defun")) - (it "(heading)" - (expect (org-ql--query-sexp-to-string '(heading "quoted phrase" "word")) - :to-equal "heading:word,\"quoted phrase\"")) - (it "(priority)" - (expect (org-ql--query-sexp-to-string '(priority >= B)) - :to-equal "priority:A,B") - (expect (org-ql--query-sexp-to-string '(priority > B)) - :to-equal "priority:A") - (expect (org-ql--query-sexp-to-string '(priority < B)) - :to-equal "priority:C") - (expect (org-ql--query-sexp-to-string '(priority < A)) - :to-equal "priority:B,C") - (expect (org-ql--query-sexp-to-string '(priority <= B)) - :to-equal "priority:B,C") - (expect (org-ql--query-sexp-to-string '(priority = A)) - :to-equal "priority:A")) - (it "(todo)" - (expect (org-ql--query-sexp-to-string '(todo)) - :to-equal "todo:") - (expect (org-ql--query-sexp-to-string '(todo "TODO")) - :to-equal "todo:TODO") - (expect (org-ql--query-sexp-to-string '(todo "TODO" "NEXT")) - :to-equal "todo:NEXT,TODO")) - (it "(ts)" - (expect (org-ql--query-sexp-to-string '(ts :from -1 :to 1)) - :to-equal "ts:from=-1,to=1") - (expect (org-ql--query-sexp-to-string '(ts :on today)) - :to-equal "ts:on=today") - (expect (org-ql--query-sexp-to-string '(ts-active :from "2017-01-01" :to "2018-01-01")) - :to-equal "ts-active:from=2017-01-01,to=2018-01-01")) - (it "(and ...)" - (expect (org-ql--query-sexp-to-string '(and (tags "book" "books") (priority "A"))) - :to-equal "tags:books,book priority:A") - (expect (org-ql--query-sexp-to-string '(and (tags "space") (not (regexp "moon")))) - :to-equal "tags:space !regexp:moon")) - (it "(or ...)" - (expect (org-ql--query-sexp-to-string '(or (tags "book" "books") (priority "A"))) - :to-equal nil))) + ;; MAYBE: Other predicates? Or should these cover the other + ;; cases, because the others use the same format? + + (it "(heading)" + (expect (org-ql--query-sexp-to-string '(heading "quoted phrase" "word")) + :to-equal "heading:word,\"quoted phrase\"")) + (it "(priority)" + (expect (org-ql--query-sexp-to-string '(priority >= B)) + :to-equal "priority:A,B") + (expect (org-ql--query-sexp-to-string '(priority > B)) + :to-equal "priority:A") + (expect (org-ql--query-sexp-to-string '(priority < B)) + :to-equal "priority:C") + (expect (org-ql--query-sexp-to-string '(priority < A)) + :to-equal "priority:B,C") + (expect (org-ql--query-sexp-to-string '(priority <= B)) + :to-equal "priority:B,C") + (expect (org-ql--query-sexp-to-string '(priority = A)) + :to-equal "priority:A")) + (it "(todo)" + (expect (org-ql--query-sexp-to-string '(todo)) + :to-equal "todo:") + (expect (org-ql--query-sexp-to-string '(todo "TODO")) + :to-equal "todo:TODO") + (expect (org-ql--query-sexp-to-string '(todo "TODO" "NEXT")) + :to-equal "todo:NEXT,TODO")) + (it "(ts)" + (expect (org-ql--query-sexp-to-string '(ts :from -1 :to 1)) + :to-equal "ts:from=-1,to=1") + (expect (org-ql--query-sexp-to-string '(ts :on today)) + :to-equal "ts:on=today") + (expect (org-ql--query-sexp-to-string '(ts-active :from "2017-01-01" :to "2018-01-01")) + :to-equal "ts-active:from=2017-01-01,to=2018-01-01")) + (it "(and ...)" + (expect (org-ql--query-sexp-to-string '(and (tags "book" "books") (priority "A"))) + :to-equal "tags:books,book priority:A") + (expect (org-ql--query-sexp-to-string '(and (tags "space") (not (regexp "moon")))) + :to-equal "tags:space !regexp:moon")) + (it "(or ...)" + (expect (org-ql--query-sexp-to-string '(or (tags "book" "books") (priority "A"))) + :to-equal nil)))) + + (describe "Query functions" + + (describe "org-ql-select" + (it "returns matching entries" + (expect (length (org-ql-select org-ql-test-buffer + '(category) + :sort 'deadline)) + :to-equal org-ql-test-num-headings))) + (describe "org-ql-query" + (it "returns matching entries" + (expect (length (org-ql-query :select 'element + :from org-ql-test-buffer + :where '(category) + :order-by 'date)) + :to-equal org-ql-test-num-headings)))) (describe "Query results" @@ -729,7 +810,7 @@ with keyword arg NOW in PLIST." (org-ql-expect ('(descendants (todo "CHECK"))) '("Recurring"))) (org-ql-it "with granddescendants query" - (org-ql-expect ('(descendants (descendants "moon"))) + (org-ql-expect ('(descendants (descendants (regexp "moon")))) '("Take over the universe"))) (org-ql-it "with query that should not match parent" ;; This test would fail if the `descendants' predicate did not properly exclude @@ -1176,6 +1257,7 @@ with keyword arg NOW in PLIST." (org-ql-it ">= a priority" (org-ql-expect ('(priority >= "B")) '("Take over the universe" "Take over the world" "Skype with president of Antarctica" "Take over Mars" "Renew membership in supervillain club" "Learn universal sign language" "Internet" "Spaceship lease" "Fix flux capacitor")))) + (describe "(property)" ;; MAYBE: Add support for (property) without arguments. @@ -1202,16 +1284,6 @@ with keyword arg NOW in PLIST." :sort 'todo) '("Take over the universe"))) - (org-ql-it "with a plain string" - (org-ql-expect ("Take over" - :sort 'todo) - '("Take over the universe" "Take over the world" "Take over Mars" "Take over the moon" "Get haircut"))) - - (org-ql-it "with two plain strings in an OR" - (org-ql-expect ('(or "Take over" "universe") - :sort 'todo) - '("Take over the universe" "Take over the world" "Take over Mars" "Take over the moon" "Get haircut"))) - (org-ql-it "case-folding predicate with non-case-folding preamble" ;; e.g. the (todo) predicate disables case-folding in its preamble, but that ;; should not prevent case-folding in this and other predicates (issue #114). @@ -1219,6 +1291,19 @@ with keyword arg NOW in PLIST." :sort 'todo) '("Take over the universe" "Take over the world" "Take over Mars" "Take over the moon" "Get haircut")))) + (describe "(rifle)" + (org-ql-it "with one argument" + (org-ql-expect ('(rifle "weekend")) + ;; In entry text. + '("Take over the world")) + (org-ql-expect ('(rifle "moon")) + ;; In outline path. + '("Take over the moon" "Visit the moon"))) + (org-ql-it "with two arguments" + (org-ql-expect ('(rifle "Take" "world")) + ;; In entry text (including tags) and/or outline path. + '("Take over the world" "Skype with president of Antarctica" "Get haircut")))) + (describe "(scheduled)" (org-ql-it "without arguments" From 1ddc5059772154c219a8890e903df6757c0afc10 Mon Sep 17 00:00:00 2001 From: Adam Porter Date: Fri, 10 Jun 2022 01:20:07 -0500 Subject: [PATCH 19/40] Change: Rename coalesce-multiple-calls to coalesce --- org-ql.el | 76 ++++++++++++++++++++++++++++--------------------------- 1 file changed, 39 insertions(+), 37 deletions(-) diff --git a/org-ql.el b/org-ql.el index e89108e..3b832c6 100644 --- a/org-ql.el +++ b/org-ql.el @@ -735,7 +735,7 @@ be coalesced." (`(,(or 'or 'not) . ,_) (push (rec sexp) new-sexp)) (`(,predicate . ,args) - (pcase-exhaustive (plist-get (alist-get predicate org-ql-predicates) :coalesce-multiple-calls) + (pcase-exhaustive (plist-get (alist-get predicate org-ql-predicates) :coalesce) (`nil (push sexp new-sexp)) (`t (setf (alist-get predicate anded-predicates) (append (alist-get predicate anded-predicates) args))) @@ -1089,7 +1089,7 @@ defined in `org-ql-predicates' by calling `org-ql-defpred'." ;; function still works. But to avoid the warning, we byte-compile it afterward. (byte-compile 'org-ql--query-preamble))) -(cl-defmacro org-ql-defpred (name args docstring &key body preambles normalizers coalesce-multiple-calls) +(cl-defmacro org-ql-defpred (name args docstring &key body preambles normalizers coalesce) "Define an `org-ql' selector predicate named `org-ql--predicate-NAME'. NAME may be a symbol or a list of symbols: if a list, the first is used as NAME and the rest are aliases. A function is only @@ -1143,21 +1143,23 @@ to variables bound in the pattern: query expression with no work to do, which improves performance. -When COALESCE-MULTIPLE-CALLS is t, multiple calls to this -predicate within a query clause may be combined into a single -call to this predicate (so it is expected that this predicate -treats multiple arguments as being boolean AND'ed together). +When COALESCE is t, multiple calls to this predicate within a +boolean AND query clause may be combined into a single call to +this predicate (so it is expected that this predicate treats +multiple arguments as being boolean AND'ed together; if it does +not, then it should not be set to coalesce). -This value may also be a function called to do coalescing of two -predicate expressions. It is called with two arguments: the list -of already-coalesced arguments to an expression, and the list of -arguments to the call being coalesced (note that a query's -arguments are normalized before the query is coalesced). If it -returns nil, the expression is not coalesced; otherwise, it +This argument may also be a function called to do coalescing of +two predicate expressions. It is called with two arguments: the +list of already-coalesced arguments to an expression, and the +list of arguments to the call being coalesced (note that a +query's arguments are normalized before the query is coalesced). +If it returns nil, the expression is not coalesced; otherwise, it should return a new list of arguments coalescing the given -arguments, with new arguments being first. (This is useful, -e.g. when a predicate takes keyword arguments, which means that -multiple calls to it can't be simply appended.) +arguments, with new arguments being first. (Using a +predicate-specific function to coalesce arguments is useful when, +e.g. a predicate takes keyword arguments, so arguments to +multiple calls can't be simply appended.) For convenience, within the `pcase' patterns, the symbol `predicate-names' is a special form which is replaced with a @@ -1175,7 +1177,7 @@ It would be expanded to: ((`(,(or 'heading 'h) . ,args) `(heading ,@args)))" - ;; FIXME: Update defpred tutorial to include :coalesce-multiple-calls. + ;; FIXME: Update defpred tutorial to include :coalesce. ;; NOTE: The debug form works, completely! For example, use `edebug-defun' ;; on the `heading' predicate, then evaluate this form: @@ -1186,7 +1188,7 @@ It would be expanded to: ;; :normalized normalized ;; :preamble preamble)) (declare (debug ([&or symbolp listp] listp stringp - &rest [&or [":coalesce-multiple-calls" form] + &rest [&or [":coalesce" form] [":body" def-body] [":normalizers" (&rest (sexp def-body))] [":preambles" (&rest (sexp def-body))]])) @@ -1209,7 +1211,7 @@ It would be expanded to: (setf (alist-get ',predicate-name org-ql-predicates) `(:name ,',name :aliases ,',aliases :fn ,',fn-name :docstring ,(\, docstring) :args ,',args :normalizers ,',normalizers :preambles ,',preambles - :coalesce-multiple-calls ,,coalesce-multiple-calls)) + :coalesce ,,coalesce)) (unless org-ql-defpred-defer ;; Reversing preserves the order in which predicates were defined. (org-ql--define-normalize-query-fn (reverse org-ql-predicates)) @@ -1392,7 +1394,7 @@ Org effort string, like \"5\" or \"0:05\"." (org-ql-defpred (heading h) (&rest strings) "Return non-nil if current entry's heading matches all STRINGS. Matching is done case-insensitively." - :coalesce-multiple-calls t + :coalesce t :normalizers ((`(,predicate-names . ,args) ;; "h" alias. `(heading ,@args))) @@ -1419,7 +1421,7 @@ Matching is done case-insensitively." (org-ql-defpred (heading-regexp h*) (&rest regexps) "Return non-nil if current entry's heading matches all REGEXPS (regexp strings). Matching is done case-insensitively." - :coalesce-multiple-calls t + :coalesce t :normalizers ((`(,predicate-names . ,args) ;; "h" alias. `(heading-regexp ,@args))) @@ -1564,11 +1566,11 @@ intuitive, general-purpose predicate." ;; NOTE: This predicate advertises that it takes strings, but they ;; are normalized to regexps. Because of that, we must use a ;; coalescing function. - :coalesce-multiple-calls (lambda (coalesced-args current-args) - (setf (plist-get coalesced-args :regexps) - (list 'quote (append (cadr (plist-get coalesced-args :regexps)) - (cadr (plist-get current-args :regexps))))) - coalesced-args) + :coalesce (lambda (coalesced-args current-args) + (setf (plist-get coalesced-args :regexps) + (list 'quote (append (cadr (plist-get coalesced-args :regexps)) + (cadr (plist-get current-args :regexps))))) + coalesced-args) :normalizers ((`(,predicate-names . ,(and rest (guard (cl-every #'stringp rest)))) ;; If this doesn't match, it's already normalized. `(rifle :regexps ',(mapcar #'regexp-quote rest)))) @@ -1594,7 +1596,7 @@ the following queries: (olp \"Food\" \"Fruits\") (olp \"Fruits\" \"Grapes\") (olp \"Food\" \"Grapes\")" - :coalesce-multiple-calls t + :coalesce t :normalizers ((`(,predicate-names . ,strings) ;; Regexp quote headings. `(org-ql--predicate-outline-path ,@(mapcar #'regexp-quote strings)))) @@ -1750,7 +1752,7 @@ priority B)." (org-ql-defpred (regexp r) (&rest regexps) "Return non-nil if current entry matches all of REGEXPS (regexp strings)." - :coalesce-multiple-calls t + :coalesce t :normalizers ((`(,predicate-names . ,args) `(regexp ,@args))) ;; MAYBE: Separate case-sensitive (Regexp) predicate. @@ -1773,15 +1775,15 @@ priority B)." "Return non-nil if current entry contains an Org source block matching all of REGEXPS. If keyword argument LANG is non-nil, the block must be in that language." - :coalesce-multiple-calls (lambda (coalesced-args current-args) - (when (or (not coalesced-args) - (equal (plist-get current-args :lang) - (plist-get coalesced-args :lang))) - (setf (plist-get coalesced-args :regexps) - (append (plist-get coalesced-args :regexps) - (plist-get current-args :regexps)) - (plist-get coalesced-args :lang) (plist-get current-args :lang)) - coalesced-args)) + :coalesce (lambda (coalesced-args current-args) + (when (or (not coalesced-args) + (equal (plist-get current-args :lang) + (plist-get coalesced-args :lang))) + (setf (plist-get coalesced-args :regexps) + (append (plist-get coalesced-args :regexps) + (plist-get current-args :regexps)) + (plist-get coalesced-args :lang) (plist-get current-args :lang)) + coalesced-args)) :normalizers ((`(,predicate-names . ,args) ;; Rewrite to use keyword args. (cond ((cl-every #'stringp args) @@ -1847,7 +1849,7 @@ Tests both inherited and local tags." (org-ql-defpred (tags-all tags&) (&rest tags) "Return non-nil if current heading has all of TAGS (a list of strings). Tests both inherited and local tags." - :coalesce-multiple-calls t + :coalesce t ;; MAYBE: -all versions for inherited and local. :normalizers ((`(,predicate-names . ,tags) `(and ,@(--map `(tags ,it) tags)))) From ede1a6b10b6869d05bbe0be38017997c2a4ae48e Mon Sep 17 00:00:00 2001 From: Adam Porter Date: Sat, 11 Jun 2022 09:19:35 -0500 Subject: [PATCH 20/40] Change: Don't use (setf (plist-get ...)) That expansion is new in Emacs 28, which we don't yet target as the minimum version. --- org-ql.el | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/org-ql.el b/org-ql.el index 3b832c6..39e4215 100644 --- a/org-ql.el +++ b/org-ql.el @@ -1567,10 +1567,9 @@ intuitive, general-purpose predicate." ;; are normalized to regexps. Because of that, we must use a ;; coalescing function. :coalesce (lambda (coalesced-args current-args) - (setf (plist-get coalesced-args :regexps) - (list 'quote (append (cadr (plist-get coalesced-args :regexps)) - (cadr (plist-get current-args :regexps))))) - coalesced-args) + (plist-put coalesced-args :regexps + (list 'quote (append (cadr (plist-get coalesced-args :regexps)) + (cadr (plist-get current-args :regexps)))))) :normalizers ((`(,predicate-names . ,(and rest (guard (cl-every #'stringp rest)))) ;; If this doesn't match, it's already normalized. `(rifle :regexps ',(mapcar #'regexp-quote rest)))) @@ -1779,11 +1778,9 @@ language." (when (or (not coalesced-args) (equal (plist-get current-args :lang) (plist-get coalesced-args :lang))) - (setf (plist-get coalesced-args :regexps) - (append (plist-get coalesced-args :regexps) - (plist-get current-args :regexps)) - (plist-get coalesced-args :lang) (plist-get current-args :lang)) - coalesced-args)) + (plist-put coalesced-args :regexps (append (plist-get coalesced-args :regexps) + (plist-get current-args :regexps))) + (plist-put coalesced-args :lang (plist-get current-args :lang)))) :normalizers ((`(,predicate-names . ,args) ;; Rewrite to use keyword args. (cond ((cl-every #'stringp args) From 6e7371ce61198859b34e3b3468add59f284bf851 Mon Sep 17 00:00:00 2001 From: Adam Porter Date: Sat, 11 Jun 2022 10:20:26 -0500 Subject: [PATCH 21/40] Add: (org-ql-find) Snippet functions The default is even more like org-rifle now. --- org-ql-find.el | 95 ++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 85 insertions(+), 10 deletions(-) diff --git a/org-ql-find.el b/org-ql-find.el index 20b6c57..69f0ad9 100644 --- a/org-ql-find.el +++ b/org-ql-find.el @@ -45,6 +45,40 @@ "Functions called when selecting an entry." :type 'hook) +(defcustom org-ql-find-snippet-function #'org-ql-find--snippet-simple + ;; TODO: I'd like to make the -regexp one the default, but with + ;; default Emacs completion affixation, it can sometimes be a bit + ;; slow, and I don't want that to be a user's first impression. It + ;; may be possible to further optimize the -regexp one so that it + ;; can be used by default. In the meantime, the -simple one seems + ;; fast enough for general use. + "Function used to annotate results in `org-ql-find'. +Function is called at entry beginning. (When set to +`org-ql-find--snippet-regexp', it is called with a regexp +matching plain query tokens.)" + :type '(choice (function-item :tag "Show context around search terms" org-ql-find--snippet-regexp) + (function-item :tag "Show first N characters" org-ql-find--snippet-simple) + (function :tag "Custom function"))) + +(defcustom org-ql-find-snippet-length 51 + "Size of snippets of entry content to include in `org-ql-find' annotations. +Only used when `org-ql-find-snippet-function' is set to +`org-ql-find--snippet-regexp'." + :type 'integer) + +(defcustom org-ql-find-snippet-minimum-token-length 3 + "Query tokens shorter than this many characters are ignored. +That is, they are not included when gathering entry snippets. +This avoids too-small tokens causing performance problems." + :type 'integer) + +(defcustom org-ql-find-snippet-prefix nil + "String prepended to snippets. +For an experience like `org-rifle', use a newline." + :type '(choice (const :tag "None (shown on same line)" nil) + (const :tag "New line (shown under heading)" "\n") + string)) + (defface org-ql-find-snippet '((t (:inherit font-lock-comment-face))) "Snippets.") @@ -81,7 +115,8 @@ single predicate)." ;; made possible by the example Clemens Radermacher shared at ;; . (let ((table (make-hash-table :test #'equal)) - (window-width (window-width))) + (window-width (window-width)) + query-tokens snippet-regexp) (cl-labels ((action () (font-lock-ensure (point-at-bol) (point-at-eol)) (let* ((path (thread-first (org-get-outline-path t t) @@ -106,17 +141,15 @@ single predicate)." "") collect (list completion todo-state snippet))) (annotate (candidate) - (or (snippet (gethash candidate table)) "")) + (while-no-input + ;; Using `while-no-input' here doesn't make it as + ;; responsive as, e.g. Helm while typing, but it seems to + ;; help a little when using the org-rifle-style snippets. + (or (snippet (gethash candidate table)) ""))) (snippet (marker) (org-with-point-at marker - (org-end-of-meta-data t) - (unless (org-at-heading-p) - (let ((end (min (+ (point) 51) - (org-entry-end-position)))) - (truncate-string-to-width - (replace-regexp-in-string "\n" " " (buffer-substring (point) end) - t t) - 50 nil nil t))))) + (or (funcall org-ql-find-snippet-function snippet-regexp) + (org-ql-find--snippet-simple)))) (group (candidate transform) (pcase transform (`nil (buffer-name (marker-buffer (gethash candidate table)))) @@ -134,6 +167,21 @@ single predicate)." (`t (unless (string-empty-p str) (when query-filter (setf str (funcall query-filter str))) + (pcase org-ql-find-snippet-function + ('org-ql-find--snippet-regexp + (setf query-tokens + ;; Remove any tokens that specify predicates or are too short. + (--select (not (or (string-match-p (rx bos (1+ (not (any ":"))) ":") it) + (< (length it) org-ql-find-snippet-minimum-token-length))) + (split-string str nil t (rx space))) + snippet-regexp (when query-tokens + ;; Limiting each context word to 15 characters + ;; prevents excessively long, non-word strings + ;; from ending up in snippets, which can + ;; adversely affect performance. + (rx-to-string `(seq (optional (repeat 1 3 (repeat 1 15 (not space)) (0+ space))) + bow (or ,@query-tokens) (0+ (not space)) + (optional (repeat 1 3 (0+ space) (repeat 1 15 (not space)))))))))) (org-ql-select buffers-files (org-ql--query-string-to-sexp (concat query-prefix str)) :action #'action)))))) (let* ((completion-styles '(org-ql-find)) @@ -181,6 +229,33 @@ multiple buffers to search with completion." (current-buffer)))) (org-ql-find buffers-files :prompt "Find outline path: " :query-prefix "outline-path:")) +(defun org-ql-find--snippet-simple (&optional _regexp) + "Return a snippet of the current entry. +Returns up to `org-ql-find-snippet-length' characters." + (org-end-of-meta-data t) + (unless (org-at-heading-p) + (let ((end (min (+ (point) org-ql-find-snippet-length) + (org-entry-end-position)))) + (concat org-ql-find-snippet-prefix + (truncate-string-to-width + (replace-regexp-in-string "\n" " " (buffer-substring (point) end) + t t) + 50 nil nil t))))) + +(defun org-ql-find--snippet-regexp (regexp) + "Return a snippet of the current entry's matches for REGEXP." + ;; REGEXP may be nil if there are no qualifying tokens in the query. + (when regexp + (org-end-of-meta-data t) + (unless (org-at-heading-p) + (let* ((end (org-entry-end-position)) + (snippets (cl-loop while (re-search-forward regexp end t) + concat (match-string 0) concat "…" + do (goto-char (match-end 0))))) + (unless (string-empty-p snippets) + (concat org-ql-find-snippet-prefix + (replace-regexp-in-string (rx (1+ "\n")) " " snippets t t))))))) + (provide 'org-ql-find) ;;; org-ql-find.el ends here From 675d333b9162e83163b98f6b4240a5087c2e212a Mon Sep 17 00:00:00 2001 From: Ihor Radchenko Date: Mon, 29 Mar 2021 08:54:50 +0800 Subject: [PATCH 22/40] Use buffer-chars-modified-tick instead of buffer-modified-tick --- org-ql.el | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/org-ql.el b/org-ql.el index 39e4215..52c9b72 100644 --- a/org-ql.el +++ b/org-ql.el @@ -481,7 +481,7 @@ NARROW corresponds to the `org-ql-select' argument NARROW." (if-let* ((buffer-cache (gethash (current-buffer) org-ql-cache)) (query-cache (cadr buffer-cache)) (modified-tick (car buffer-cache)) - (buffer-unmodified-p (eq (buffer-modified-tick) modified-tick)) + (buffer-unmodified-p (eq (buffer-chars-modified-tick) modified-tick)) (cached-result (gethash query-cache-key query-cache))) (pcase cached-result ('org-ql-nil nil) @@ -490,7 +490,7 @@ NARROW corresponds to the `org-ql-select' argument NARROW." (cond ((or (not query-cache) (not buffer-unmodified-p)) (puthash (current-buffer) - (list (buffer-modified-tick) + (list (buffer-chars-modified-tick) (let ((table (make-hash-table :test 'org-ql-hash-test))) (puthash query-cache-key (or new-result 'org-ql-nil) table) table)) @@ -545,7 +545,7 @@ Returns cons (INHERITED-TAGS . LOCAL-TAGS)." (if-let* ((buffer-cache (gethash (current-buffer) org-ql-tags-cache)) (modified-tick (car buffer-cache)) (tags-cache (cdr buffer-cache)) - (buffer-unmodified-p (eq (buffer-modified-tick) modified-tick)) + (buffer-unmodified-p (eq (buffer-chars-modified-tick) modified-tick)) (cached-result (gethash position tags-cache))) ;; Found in cache: return them. ;; FIXME: Isn't `cached-result' a list of (INHERITED . LOCAL)? It @@ -586,12 +586,12 @@ Returns cons (INHERITED-TAGS . LOCAL-TAGS)." (setf buffer-cache (gethash (current-buffer) org-ql-tags-cache) modified-tick (car buffer-cache) tags-cache (cdr buffer-cache) - buffer-unmodified-p (eq (buffer-modified-tick) modified-tick)) + buffer-unmodified-p (eq (buffer-chars-modified-tick) modified-tick)) (unless (and buffer-cache buffer-unmodified-p) ;; Buffer-local tags cache empty or invalid: make new one. (setf tags-cache (make-hash-table)) (puthash (current-buffer) - (cons (buffer-modified-tick) tags-cache) + (cons (buffer-chars-modified-tick) tags-cache) org-ql-tags-cache)) (puthash position all-tags tags-cache)))) @@ -622,7 +622,7 @@ Values compared with `equal'." (pcase (if-let* ((buffer-cache (gethash (current-buffer) org-ql-node-value-cache)) (modified-tick (car buffer-cache)) (position-cache (cdr buffer-cache)) - (buffer-unmodified-p (eq (buffer-modified-tick) modified-tick)) + (buffer-unmodified-p (eq (buffer-chars-modified-tick) modified-tick)) (value-cache (gethash position position-cache)) (cached-value (alist-get fn value-cache nil nil #'equal))) ;; Found in cache: return it. @@ -637,13 +637,13 @@ Values compared with `equal'." position-cache (cdr buffer-cache) value-cache (when position-cache (gethash position position-cache)) - buffer-unmodified-p (eq (buffer-modified-tick) modified-tick)) + buffer-unmodified-p (eq (buffer-chars-modified-tick) modified-tick)) (unless (and buffer-cache buffer-unmodified-p) ;; Buffer-local node cache empty or invalid: make new one. (setf position-cache (make-hash-table) value-cache (gethash position position-cache)) (puthash (current-buffer) - (cons (buffer-modified-tick) position-cache) + (cons (buffer-chars-modified-tick) position-cache) org-ql-node-value-cache)) (setf (alist-get fn value-cache nil nil #'equal) new-value) (puthash position value-cache position-cache) From e22890ad04ea5fd43a5665835b16070b0330749d Mon Sep 17 00:00:00 2001 From: Adam Porter Date: Mon, 13 Jun 2022 19:07:44 -0500 Subject: [PATCH 23/40] Docs: Update changelog --- README.org | 1 + org-ql.info | 58 +++++++++++++++++++++++++++-------------------------- 2 files changed, 31 insertions(+), 28 deletions(-) diff --git a/README.org b/README.org index b89765a..fe68a62 100644 --- a/README.org +++ b/README.org @@ -550,6 +550,7 @@ Simple links may also be written manually in either sexp or non-sexp form, like: *Internal* + Certain query predicates, when called multiple times in an ~and~ sub-expression, are optimized to a single call. ++ Use ~buffer-chars-modified-tick~ instead of ~buffer-modified-tick~. (Thanks to [[https://github.com/yantar92][Ihor Radchenko]].) ** 0.6.2 diff --git a/org-ql.info b/org-ql.info index 25ccf74..3a4c3c0 100644 --- a/org-ql.info +++ b/org-ql.info @@ -1034,6 +1034,8 @@ File: README.info, Node: 07-pre, Next: 062, Up: Changelog *Internal* • Certain query predicates, when called multiple times in an ‘and’ sub-expression, are optimized to a single call. + • Use ‘buffer-chars-modified-tick’ instead of ‘buffer-modified-tick’. + (Thanks to Ihor Radchenko (https://github.com/yantar92).)  File: README.info, Node: 062, Next: 061, Prev: 07-pre, Up: Changelog @@ -1679,34 +1681,34 @@ Node: Links36761 Node: Tips37448 Node: Changelog37772 Node: 07-pre38540 -Node: 06239801 -Node: 06140109 -Node: 0640677 -Node: 05243731 -Node: 05144031 -Node: 0544454 -Node: 04945983 -Node: 04846263 -Node: 04746610 -Node: 04647019 -Node: 04547427 -Node: 04447788 -Node: 04348147 -Node: 04248350 -Node: 04148511 -Node: 0448758 -Node: 03252859 -Node: 03153262 -Node: 0353459 -Node: 02356759 -Node: 02256993 -Node: 02157273 -Node: 0257478 -Node: 0161556 -Node: Notes61657 -Node: Comparison with Org Agenda searches61819 -Node: org-sidebar62708 -Node: License62987 +Node: 06239947 +Node: 06140255 +Node: 0640823 +Node: 05243877 +Node: 05144177 +Node: 0544600 +Node: 04946129 +Node: 04846409 +Node: 04746756 +Node: 04647165 +Node: 04547573 +Node: 04447934 +Node: 04348293 +Node: 04248496 +Node: 04148657 +Node: 0448904 +Node: 03253005 +Node: 03153408 +Node: 0353605 +Node: 02356905 +Node: 02257139 +Node: 02157419 +Node: 0257624 +Node: 0161702 +Node: Notes61803 +Node: Comparison with Org Agenda searches61965 +Node: org-sidebar62854 +Node: License63133  End Tag Table From 5768c685adc7c6e85c17cfec358aa8a0e368310b Mon Sep 17 00:00:00 2001 From: Adam Porter Date: Sat, 25 Jun 2022 10:53:29 -0500 Subject: [PATCH 24/40] Remove: org-ql-find-heading, org-ql-find-path These are virtually obsolete with the introduction of the "rifle" predicate as the default one. Having them now is just confusing. --- README.org | 2 -- org-ql-find.el | 32 -------------------------------- 2 files changed, 34 deletions(-) diff --git a/README.org b/README.org index fe68a62..05b4bfc 100644 --- a/README.org +++ b/README.org @@ -112,8 +112,6 @@ Lisp code examples are in [[examples.org]]. These commands jump to a heading selected using Emacs's built-in completion facilities with an Org QL query: - ~org-ql-find~ searches all entry content. -- ~org-ql-find-path~ searches only headings (using the ~outline-path:~ predicate). -- ~org-ql-find-heading~ searches only headings (using the ~heading:~ predicate). [[images/org-ql-find.png]] diff --git a/org-ql-find.el b/org-ql-find.el index 69f0ad9..bc92277 100644 --- a/org-ql-find.el +++ b/org-ql-find.el @@ -194,40 +194,8 @@ single predicate)." (run-hook-with-args 'org-ql-find-goto-hook)))))) ;;;###autoload -(defun org-ql-find-heading (buffers-files) - "Go to an Org entry in BUFFERS-FILES selected by searching with `org-ql'. -Only headings are searched (using the \"heading:\" predicate). -Interactively, with universal prefix, select multiple buffers to -search with completion." - (interactive - (list (if current-prefix-arg - (mapcar #'get-buffer - (completing-read-multiple - "Buffers: " - (mapcar #'buffer-name - (cl-remove-if-not (lambda (buffer) - (eq 'org-mode (buffer-local-value 'major-mode buffer))) - (buffer-list))) nil t)) - (current-buffer)))) - (org-ql-find buffers-files :prompt "Find heading: " :query-prefix "heading:")) ;;;###autoload -(defun org-ql-find-path (buffers-files) - "Go to an Org entry in BUFFERS-FILES selected by searching with `org-ql'. -Only outline paths are searched (using the \"outline-path:\" -predicate). Interactively, with universal prefix, select -multiple buffers to search with completion." - (interactive - (list (if current-prefix-arg - (mapcar #'get-buffer - (completing-read-multiple - "Buffers: " - (mapcar #'buffer-name - (cl-remove-if-not (lambda (buffer) - (eq 'org-mode (buffer-local-value 'major-mode buffer))) - (buffer-list))) nil t)) - (current-buffer)))) - (org-ql-find buffers-files :prompt "Find outline path: " :query-prefix "outline-path:")) (defun org-ql-find--snippet-simple (&optional _regexp) "Return a snippet of the current entry. From bbd164c6aeb7dcf2a1ae8c80fd1dc09963f60221 Mon Sep 17 00:00:00 2001 From: Adam Porter Date: Sat, 25 Jun 2022 10:54:38 -0500 Subject: [PATCH 25/40] Change/Fix: (org-ql-select) Warn instead of erroring on bad files This helps prevent unexpected errors from making it impossible to search, and displaying the messages in the warnings buffer makes them more noticeable and readable for the user. --- org-ql.el | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/org-ql.el b/org-ql.el index 52c9b72..3747950 100644 --- a/org-ql.el +++ b/org-ql.el @@ -362,18 +362,18 @@ each priority the newest items would appear first." (function (funcall buffers-or-files)) (list buffers-or-files) (otherwise (list buffers-or-files))) - (--map (cl-etypecase it - ;; NOTE: This etypecase is essential to opening links safely, - ;; as it rejects, e.g. lambdas in the buffers-files argument. - (buffer it) - (string (or (find-buffer-visiting it) - (when (file-readable-p it) - ;; It feels unintuitive that `find-file-noselect' returns - ;; a buffer if the filename doesn't exist. - (find-file-noselect it)) - (user-error "Can't open file: %s" it))))) - ;; Ignore special/hidden buffers. - (--remove (string-prefix-p " " (buffer-name it))))) + (--map (cl-etypecase it + ;; NOTE: This etypecase is essential to opening links safely, + ;; as it rejects, e.g. lambdas in the buffers-files argument. + (buffer it) + (string (or (find-buffer-visiting it) + (when (file-readable-p it) + ;; It feels unintuitive that `find-file-noselect' returns + ;; a buffer if the filename doesn't exist. + (find-file-noselect it)) + (display-warning 'org-ql-select (format "Can't open file: %s" it) :error))))) + ;; Ignore special/hidden buffers. + (--remove (string-prefix-p " " (buffer-name it))))) (query (org-ql--normalize-query query)) ((&plist :query :preamble :preamble-case-fold) (org-ql--query-preamble query)) (predicate (org-ql--query-predicate query)) @@ -409,12 +409,12 @@ each priority the newest items would appear first." (fset name fn))) ;; Run query on buffers. (->> buffers - (--map (with-current-buffer it - (unless (derived-mode-p 'org-mode) - (user-error "Not an Org buffer: %s" (buffer-name))) - (org-ql--select-cached :query query :preamble preamble :preamble-case-fold preamble-case-fold - :predicate predicate :action action :narrow narrow))) - (-flatten-n 1))) + (--map (with-current-buffer it + (unless (derived-mode-p 'org-mode) + (display-warning 'org-ql-select (format "Not an Org buffer: %s" (buffer-name)) :error)) + (org-ql--select-cached :query query :preamble preamble :preamble-case-fold preamble-case-fold + :predicate predicate :action action :narrow narrow))) + (-flatten-n 1))) (--each orig-fns ;; Restore original function mappings. (-let (((&plist :name :fn) it)) From dc02c59cada45759f096eed8010449767ee49fcd Mon Sep 17 00:00:00 2001 From: Adam Porter Date: Sat, 25 Jun 2022 10:56:18 -0500 Subject: [PATCH 26/40] Add: (org-ql--ensure-buffer) And use in org-ql-find. This seems to solve a problem I've been noticing in helm-org-ql for a long time: that it can apparently interrupt Org's initialization code, which can leave buffers unprepared for Org, even stuck in fundamental-mode. This should also be used in helm-org-ql... --- org-ql-find.el | 13 +++++++++++++ org-ql.el | 14 ++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/org-ql-find.el b/org-ql-find.el index bc92277..6a4e7c1 100644 --- a/org-ql-find.el +++ b/org-ql-find.el @@ -184,6 +184,19 @@ single predicate)." (optional (repeat 1 3 (0+ space) (repeat 1 15 (not space)))))))))) (org-ql-select buffers-files (org-ql--query-string-to-sexp (concat query-prefix str)) :action #'action)))))) + ;; NOTE: It seems that the `completing-read' machinery can call, + ;; abort, and re-call the collection function while the user is + ;; typing, which can interrupt the machinery Org uses to prepare + ;; an Org buffer when an Org file is loaded. This results in, + ;; e.g. the buffer being left in fundamental-mode, unprepared to + ;; be used as an Org buffer, which breaks many things and is + ;; very confusing for the user. Ideally, of course, we would + ;; solve this in `org-ql-select', and we already attempt to, but + ;; that function is called by the `completing-read' machinery, + ;; which interrupts it, so we must work around this problem by + ;; ensuring all of the BUFFERS-FILES are loaded and initialized + ;; before calling `completing-read'. + (mapc #'org-ql--ensure-buffer buffers-files) (let* ((completion-styles '(org-ql-find)) (completion-styles-alist (list (list 'org-ql-find #'try #'all "Org QL Find"))) (selected (completing-read prompt #'collection nil)) diff --git a/org-ql.el b/org-ql.el index 3747950..0cf6c8a 100644 --- a/org-ql.el +++ b/org-ql.el @@ -535,6 +535,20 @@ If NARROW is non-nil, buffer will not be widened." ;;;;; Helpers +(defun org-ql--ensure-buffer (file-or-buffer) + "Ensure a buffer is named or visiting FILE-OR-BUFFER. +If no such buffer exists with the name, and it is the name of a +readable file, `find-file-noselect' it into a buffer." + ;; See comment in `org-ql-find'. + ;; FIXME: Use this in `helm-org-ql' the same way it's used in + ;; `org-ql-find'. + (unless (or (get-buffer file-or-buffer) + (find-buffer-visiting file-or-buffer)) + (if (file-readable-p file-or-buffer) + (with-current-buffer (find-file-noselect file-or-buffer) + (cl-assert (eq 'org-mode major-mode) nil (format "Not an Org buffer: %S" file-or-buffer))) + (display-warning 'org-ql (format "Not a readable file: %S" file-or-buffer) :error)))) + (defun org-ql--tags-at (position) ;; FIXME: This function actually assumes that point is already at POSITION. "Return tags for POSITION in current buffer. From 5f058b9eee0ff36c33b47c25d94de7b4c5cbf0c4 Mon Sep 17 00:00:00 2001 From: Adam Porter Date: Sat, 25 Jun 2022 10:58:17 -0500 Subject: [PATCH 27/40] Add: (org-ql-find-in-agenda, org-ql-find-in-org-directory) --- README.org | 18 ++++--- org-ql-find.el | 9 ++++ org-ql.info | 132 +++++++++++++++++++++++-------------------------- 3 files changed, 81 insertions(+), 78 deletions(-) diff --git a/README.org b/README.org index 05b4bfc..10d6e07 100644 --- a/README.org +++ b/README.org @@ -95,15 +95,15 @@ Lisp code examples are in [[examples.org]]. :END: + *Jumping to an entry:* - - [[#org-ql-find][org-ql-find]] (command) - - [[#helm-org-ql][helm-org-ql]] (command) + - [[#org-ql-find][org-ql-find]] and related commands + - [[#helm-org-ql][helm-org-ql]] + *Showing an agenda-like view:* - - [[#org-ql-search][org-ql-search]] (command) - - [[#org-ql-view][org-ql-view]] (command) - - [[#org-ql-view-sidebar][org-ql-view-sidebar]] (command) - - [[#org-ql-view-recent-items][org-ql-view-recent-items]] (command) + - [[#org-ql-search][org-ql-search]] + - [[#org-ql-view][org-ql-view]] + - [[#org-ql-view-sidebar][org-ql-view-sidebar]] + - [[#org-ql-view-recent-items][org-ql-view-recent-items]] + *Showing a tree in a buffer:* - - [[#org-ql-sparse-tree][org-ql-sparse-tree]] (command) + - [[#org-ql-sparse-tree][org-ql-sparse-tree]] *** org-ql-find @@ -111,7 +111,9 @@ Lisp code examples are in [[examples.org]]. These commands jump to a heading selected using Emacs's built-in completion facilities with an Org QL query: -- ~org-ql-find~ searches all entry content. +- ~org-ql-find~ searches in the current buffer. +- ~org-ql-find-in-agenda~ searches in ~(org-agenda-files)~. +- ~org-ql-find-in-org-directory~ searches in ~org-directory~. [[images/org-ql-find.png]] diff --git a/org-ql-find.el b/org-ql-find.el index 6a4e7c1..5c3acee 100644 --- a/org-ql-find.el +++ b/org-ql-find.el @@ -30,6 +30,7 @@ (require 'org) (require 'org-ql) +(require 'org-ql-search) ;;;; Customization @@ -207,8 +208,16 @@ single predicate)." (run-hook-with-args 'org-ql-find-goto-hook)))))) ;;;###autoload +(defun org-ql-find-in-agenda () + "Call `org-ql-find' on `org-agenda-files'." + (interactive) + (org-ql-find (org-agenda-files))) ;;;###autoload +(defun org-ql-find-in-org-directory () + "Call `org-ql-find' on files in `org-directory'." + (interactive) + (org-ql-find (org-ql-search-directories-files))) (defun org-ql-find--snippet-simple (&optional _regexp) "Return a snippet of the current entry. diff --git a/org-ql.info b/org-ql.info index 3a4c3c0..cfb9896 100644 --- a/org-ql.info +++ b/org-ql.info @@ -194,16 +194,10 @@ File: README.info, Node: Commands, Next: Queries, Up: Usage ============ • *Jumping to an entry:* - • (command) - • (command) - • *Showing an agenda-like view:* - • (command) - • (command) - • (command) - • (command) - • *Showing a tree in a buffer:* - • (command) - + • and related commands + • • *Showing an agenda-like view:* + • • • • • *Showing a tree in a buffer:* + • * Menu: * org-ql-find:: @@ -225,11 +219,9 @@ _Note: These commands use ._ These commands jump to a heading selected using Emacs’s built-in completion facilities with an Org QL query: - • ‘org-ql-find’ searches all entry content. - • ‘org-ql-find-path’ searches only headings (using the - ‘outline-path:’ predicate). - • ‘org-ql-find-heading’ searches only headings (using the ‘heading:’ - predicate). + • ‘org-ql-find’ searches in the current buffer. + • ‘org-ql-find-in-agenda’ searches in ‘(org-agenda-files)’. + • ‘org-ql-find-in-org-directory’ searches in ‘org-directory’.  File: README.info, Node: org-ql-search, Next: helm-org-ql, Prev: org-ql-find, Up: Commands @@ -1654,61 +1646,61 @@ Node: Quelpa2519 Node: Helm support3047 Node: Usage3450 Node: Commands3848 -Node: org-ql-find4353 -Node: org-ql-search4867 -Node: helm-org-ql6561 -Node: org-ql-view6939 -Node: org-ql-view-sidebar7469 -Node: org-ql-view-recent-items7849 -Node: org-ql-sparse-tree8345 -Node: Queries9145 -Node: Non-sexp query syntax10262 -Node: General predicates12021 -Node: Ancestor/descendant predicates18763 -Node: Date/time predicates19891 -Node: Functions / Macros23015 -Node: Agenda-like views23313 -Ref: Function ‘org-ql-block’23475 -Node: Listing / acting-on results24736 -Ref: Caching24944 -Ref: Function ‘org-ql-select’25857 -Ref: Function ‘org-ql-query’28283 -Ref: Macro ‘org-ql’ (deprecated)30057 -Node: Custom predicates30372 -Ref: Macro ‘org-ql-defpred’30596 -Node: Dynamic block34037 -Node: Links36761 -Node: Tips37448 -Node: Changelog37772 -Node: 07-pre38540 -Node: 06239947 -Node: 06140255 -Node: 0640823 -Node: 05243877 -Node: 05144177 -Node: 0544600 -Node: 04946129 -Node: 04846409 -Node: 04746756 -Node: 04647165 -Node: 04547573 -Node: 04447934 -Node: 04348293 -Node: 04248496 -Node: 04148657 -Node: 0448904 -Node: 03253005 -Node: 03153408 -Node: 0353605 -Node: 02356905 -Node: 02257139 -Node: 02157419 -Node: 0257624 -Node: 0161702 -Node: Notes61803 -Node: Comparison with Org Agenda searches61965 -Node: org-sidebar62854 -Node: License63133 +Node: org-ql-find4274 +Node: org-ql-search4740 +Node: helm-org-ql6434 +Node: org-ql-view6812 +Node: org-ql-view-sidebar7342 +Node: org-ql-view-recent-items7722 +Node: org-ql-sparse-tree8218 +Node: Queries9018 +Node: Non-sexp query syntax10135 +Node: General predicates11894 +Node: Ancestor/descendant predicates18636 +Node: Date/time predicates19764 +Node: Functions / Macros22888 +Node: Agenda-like views23186 +Ref: Function ‘org-ql-block’23348 +Node: Listing / acting-on results24609 +Ref: Caching24817 +Ref: Function ‘org-ql-select’25730 +Ref: Function ‘org-ql-query’28156 +Ref: Macro ‘org-ql’ (deprecated)29930 +Node: Custom predicates30245 +Ref: Macro ‘org-ql-defpred’30469 +Node: Dynamic block33910 +Node: Links36634 +Node: Tips37321 +Node: Changelog37645 +Node: 07-pre38413 +Node: 06239820 +Node: 06140128 +Node: 0640696 +Node: 05243750 +Node: 05144050 +Node: 0544473 +Node: 04946002 +Node: 04846282 +Node: 04746629 +Node: 04647038 +Node: 04547446 +Node: 04447807 +Node: 04348166 +Node: 04248369 +Node: 04148530 +Node: 0448777 +Node: 03252878 +Node: 03153281 +Node: 0353478 +Node: 02356778 +Node: 02257012 +Node: 02157292 +Node: 0257497 +Node: 0161575 +Node: Notes61676 +Node: Comparison with Org Agenda searches61838 +Node: org-sidebar62727 +Node: License63006  End Tag Table From 77916a1c4f6dd45737db66756e17ea1c6bda0368 Mon Sep 17 00:00:00 2001 From: Adam Porter Date: Sat, 25 Jun 2022 10:58:33 -0500 Subject: [PATCH 28/40] Tidy: Indentation --- org-ql-find.el | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/org-ql-find.el b/org-ql-find.el index 5c3acee..bc1d492 100644 --- a/org-ql-find.el +++ b/org-ql-find.el @@ -175,14 +175,15 @@ single predicate)." (--select (not (or (string-match-p (rx bos (1+ (not (any ":"))) ":") it) (< (length it) org-ql-find-snippet-minimum-token-length))) (split-string str nil t (rx space))) - snippet-regexp (when query-tokens - ;; Limiting each context word to 15 characters - ;; prevents excessively long, non-word strings - ;; from ending up in snippets, which can - ;; adversely affect performance. - (rx-to-string `(seq (optional (repeat 1 3 (repeat 1 15 (not space)) (0+ space))) - bow (or ,@query-tokens) (0+ (not space)) - (optional (repeat 1 3 (0+ space) (repeat 1 15 (not space)))))))))) + snippet-regexp + (when query-tokens + ;; Limiting each context word to 15 characters + ;; prevents excessively long, non-word strings + ;; from ending up in snippets, which can + ;; adversely affect performance. + (rx-to-string `(seq (optional (repeat 1 3 (repeat 1 15 (not space)) (0+ space))) + bow (or ,@query-tokens) (0+ (not space)) + (optional (repeat 1 3 (0+ space) (repeat 1 15 (not space)))))))))) (org-ql-select buffers-files (org-ql--query-string-to-sexp (concat query-prefix str)) :action #'action)))))) ;; NOTE: It seems that the `completing-read' machinery can call, From c2b8af3181c6f1048f189b4d3d0958a73de52ed7 Mon Sep 17 00:00:00 2001 From: Adam Porter Date: Sun, 26 Jun 2022 22:55:20 -0500 Subject: [PATCH 29/40] Fix: (org-ql-find) Always map across a list --- org-ql-find.el | 3 +++ 1 file changed, 3 insertions(+) diff --git a/org-ql-find.el b/org-ql-find.el index bc1d492..080bb12 100644 --- a/org-ql-find.el +++ b/org-ql-find.el @@ -198,6 +198,9 @@ single predicate)." ;; which interrupts it, so we must work around this problem by ;; ensuring all of the BUFFERS-FILES are loaded and initialized ;; before calling `completing-read'. + (unless (listp buffers-files) + ;; Since we map across this argument, we ensure it's a list. + (setf buffers-files (list buffers-files))) (mapc #'org-ql--ensure-buffer buffers-files) (let* ((completion-styles '(org-ql-find)) (completion-styles-alist (list (list 'org-ql-find #'try #'all "Org QL Find"))) From d46e69b0fc71566f848faf9eee5fad88bfc6fda5 Mon Sep 17 00:00:00 2001 From: Adam Porter Date: Thu, 14 Jul 2022 12:52:44 -0500 Subject: [PATCH 30/40] Fix: (org-ql-find) Always include query-prefix Thanks to Thomas Bergheim (@bergheim) for his help! --- org-ql-find.el | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/org-ql-find.el b/org-ql-find.el index 080bb12..69994f2 100644 --- a/org-ql-find.el +++ b/org-ql-find.el @@ -160,6 +160,8 @@ single predicate)." (all (string table pred _point) (all-completions string table pred)) (collection (str _pred flag) + (when query-prefix + (setf str (concat query-prefix str))) (pcase flag ('metadata (list 'metadata (cons 'group-function #'group) @@ -184,7 +186,7 @@ single predicate)." (rx-to-string `(seq (optional (repeat 1 3 (repeat 1 15 (not space)) (0+ space))) bow (or ,@query-tokens) (0+ (not space)) (optional (repeat 1 3 (0+ space) (repeat 1 15 (not space)))))))))) - (org-ql-select buffers-files (org-ql--query-string-to-sexp (concat query-prefix str)) + (org-ql-select buffers-files (org-ql--query-string-to-sexp str) :action #'action)))))) ;; NOTE: It seems that the `completing-read' machinery can call, ;; abort, and re-call the collection function while the user is From a986e360c336f076e4051668ecb120ddf0e06e62 Mon Sep 17 00:00:00 2001 From: Adam Porter Date: Thu, 14 Jul 2022 13:05:42 -0500 Subject: [PATCH 31/40] Fix: (org-ql-find) Select buffer Thanks again to Thomas Bergheim (@bergheim). --- org-ql-find.el | 1 + 1 file changed, 1 insertion(+) diff --git a/org-ql-find.el b/org-ql-find.el index 69994f2..04b06ad 100644 --- a/org-ql-find.el +++ b/org-ql-find.el @@ -211,6 +211,7 @@ single predicate)." (with-current-buffer (marker-buffer marker) (goto-char marker) (display-buffer (current-buffer)) + (select-window (get-buffer-window (current-buffer))) (run-hook-with-args 'org-ql-find-goto-hook)))))) ;;;###autoload From 98b6049eccc4f3b19700d27a86f22d4428013501 Mon Sep 17 00:00:00 2001 From: Adam Porter Date: Fri, 15 Jul 2022 13:49:52 -0500 Subject: [PATCH 32/40] Fix: (org-ql-find) Check major mode See https://github.com/alphapapa/org-ql/issues/284#issuecomment-1185367899. --- org-ql-find.el | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/org-ql-find.el b/org-ql-find.el index 04b06ad..4f8e981 100644 --- a/org-ql-find.el +++ b/org-ql-find.el @@ -110,7 +110,10 @@ single predicate)." (cl-remove-if-not (lambda (buffer) (eq 'org-mode (buffer-local-value 'major-mode buffer))) (buffer-list))) nil t)) - (current-buffer)))) + (progn + (unless (eq major-mode 'org-mode) + (user-error "This is not an Org buffer: %S" (current-buffer))) + (current-buffer))))) ;; Emacs's completion API is not always easy to understand, ;; especially when using "programmed completion." This code was ;; made possible by the example Clemens Radermacher shared at From a02ed48ac81583300747e91c0a964abbb590ef43 Mon Sep 17 00:00:00 2001 From: Adam Porter Date: Mon, 18 Jul 2022 10:57:50 -0500 Subject: [PATCH 33/40] Change: (org-ql-find) Add workaround for Ivy support See . --- org-ql-find.el | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/org-ql-find.el b/org-ql-find.el index 4f8e981..8630f72 100644 --- a/org-ql-find.el +++ b/org-ql-find.el @@ -83,6 +83,12 @@ For an experience like `org-rifle', use a newline." (defface org-ql-find-snippet '((t (:inherit font-lock-comment-face))) "Snippets.") +;;;; Variables + +;; TODO: When Ivy no longer requires this workaround, remove it. See +;; . +(defvar ivy-completing-read-dynamic-collection) + ;;;; Functions ;;;###autoload @@ -209,6 +215,10 @@ single predicate)." (mapc #'org-ql--ensure-buffer buffers-files) (let* ((completion-styles '(org-ql-find)) (completion-styles-alist (list (list 'org-ql-find #'try #'all "Org QL Find"))) + ;; TODO: When Ivy no longer requires this workaround, + ;; remove it and the corresponding defvar. See + ;; . + (ivy-completing-read-dynamic-collection t) (selected (completing-read prompt #'collection nil)) (marker (gethash selected table))) (with-current-buffer (marker-buffer marker) From 06f1e1be6ff5ef7e2c8c05dc1954bcedcbb6eb0b Mon Sep 17 00:00:00 2001 From: Adam Porter Date: Mon, 18 Jul 2022 13:44:25 -0500 Subject: [PATCH 34/40] Revert "Change: (org-ql-find) Add workaround for Ivy support" This reverts commit a02ed48ac81583300747e91c0a964abbb590ef43. See . --- org-ql-find.el | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/org-ql-find.el b/org-ql-find.el index 8630f72..4f8e981 100644 --- a/org-ql-find.el +++ b/org-ql-find.el @@ -83,12 +83,6 @@ For an experience like `org-rifle', use a newline." (defface org-ql-find-snippet '((t (:inherit font-lock-comment-face))) "Snippets.") -;;;; Variables - -;; TODO: When Ivy no longer requires this workaround, remove it. See -;; . -(defvar ivy-completing-read-dynamic-collection) - ;;;; Functions ;;;###autoload @@ -215,10 +209,6 @@ single predicate)." (mapc #'org-ql--ensure-buffer buffers-files) (let* ((completion-styles '(org-ql-find)) (completion-styles-alist (list (list 'org-ql-find #'try #'all "Org QL Find"))) - ;; TODO: When Ivy no longer requires this workaround, - ;; remove it and the corresponding defvar. See - ;; . - (ivy-completing-read-dynamic-collection t) (selected (completing-read prompt #'collection nil)) (marker (gethash selected table))) (with-current-buffer (marker-buffer marker) From 6257ae83044aa9fcd5c2ddffbd2f73f2b235f905 Mon Sep 17 00:00:00 2001 From: Adam Porter Date: Mon, 15 Aug 2022 16:16:55 -0500 Subject: [PATCH 35/40] Fix: (org-ql-find--snippet-*) Use save-excursion --- org-ql-find.el | 38 ++++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/org-ql-find.el b/org-ql-find.el index 4f8e981..143a861 100644 --- a/org-ql-find.el +++ b/org-ql-find.el @@ -232,29 +232,31 @@ single predicate)." (defun org-ql-find--snippet-simple (&optional _regexp) "Return a snippet of the current entry. Returns up to `org-ql-find-snippet-length' characters." - (org-end-of-meta-data t) - (unless (org-at-heading-p) - (let ((end (min (+ (point) org-ql-find-snippet-length) - (org-entry-end-position)))) - (concat org-ql-find-snippet-prefix - (truncate-string-to-width - (replace-regexp-in-string "\n" " " (buffer-substring (point) end) - t t) - 50 nil nil t))))) + (save-excursion + (org-end-of-meta-data t) + (unless (org-at-heading-p) + (let ((end (min (+ (point) org-ql-find-snippet-length) + (org-entry-end-position)))) + (concat org-ql-find-snippet-prefix + (truncate-string-to-width + (replace-regexp-in-string "\n" " " (buffer-substring (point) end) + t t) + 50 nil nil t)))))) (defun org-ql-find--snippet-regexp (regexp) "Return a snippet of the current entry's matches for REGEXP." ;; REGEXP may be nil if there are no qualifying tokens in the query. (when regexp - (org-end-of-meta-data t) - (unless (org-at-heading-p) - (let* ((end (org-entry-end-position)) - (snippets (cl-loop while (re-search-forward regexp end t) - concat (match-string 0) concat "…" - do (goto-char (match-end 0))))) - (unless (string-empty-p snippets) - (concat org-ql-find-snippet-prefix - (replace-regexp-in-string (rx (1+ "\n")) " " snippets t t))))))) + (save-excursion + (org-end-of-meta-data t) + (unless (org-at-heading-p) + (let* ((end (org-entry-end-position)) + (snippets (cl-loop while (re-search-forward regexp end t) + concat (match-string 0) concat "…" + do (goto-char (match-end 0))))) + (unless (string-empty-p snippets) + (concat org-ql-find-snippet-prefix + (replace-regexp-in-string (rx (1+ "\n")) " " snippets t t)))))))) (provide 'org-ql-find) From 18869c47fffe196621fd4c0fea7c56de1f3511c8 Mon Sep 17 00:00:00 2001 From: Adam Porter Date: Fri, 19 Aug 2022 03:14:46 -0500 Subject: [PATCH 36/40] Tidy: Headers --- org-ql.el | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/org-ql.el b/org-ql.el index 0cf6c8a..d4dbff9 100644 --- a/org-ql.el +++ b/org-ql.el @@ -1,19 +1,13 @@ ;;; org-ql.el --- Org Query Language, search command, and agenda-like view -*- lexical-binding: t; -*- +;; Copyright (C) 2017-2022 Adam Porter + ;; Author: Adam Porter ;; Url: https://github.com/alphapapa/org-ql ;; Version: 0.7-pre ;; Package-Requires: ((emacs "26.1") (dash "2.18.1") (f "0.17.2") (map "2.1") (org "9.0") (org-super-agenda "1.2") (ov "1.0.6") (peg "1.0") (s "1.12.0") (transient "0.1") (ts "0.2-pre")) ;; Keywords: hypermedia, outlines, Org, agenda -;;; Commentary: - -;; `org-ql' is a lispy query language for Org files. It allows you to -;; find Org entries matching certain criteria and return a list of -;; them or perform actions on them. Commands are also provided which -;; display a buffer with matching results, similar to an Org Agenda -;; buffer. - ;;; License: ;; This program is free software; you can redistribute it and/or modify @@ -29,6 +23,14 @@ ;; You should have received a copy of the GNU General Public License ;; along with this program. If not, see . +;;; Commentary: + +;; `org-ql' is a Lispy query language for Org files. It allows you to +;; find Org entries matching certain criteria and return a list of +;; them or perform actions on them. Commands are also provided which +;; display a buffer with matching results, similar to an Org Agenda +;; buffer. + ;;; Code: ;;;; Requirements From cfa3b307ecb9d461745882ac756ca05c3de9519e Mon Sep 17 00:00:00 2001 From: Adam Porter Date: Fri, 19 Aug 2022 03:17:18 -0500 Subject: [PATCH 37/40] Add/Change: org-ql-completing-read, etc. --- org-ql-completing-read.el | 227 ++++++++++++++++++++++++++++++++++++++ org-ql-find.el | 190 +++---------------------------- 2 files changed, 241 insertions(+), 176 deletions(-) create mode 100644 org-ql-completing-read.el diff --git a/org-ql-completing-read.el b/org-ql-completing-read.el new file mode 100644 index 0000000..81d8557 --- /dev/null +++ b/org-ql-completing-read.el @@ -0,0 +1,227 @@ +;;; org-ql-completing-read.el --- Completing read of Org entries using org-ql -*- lexical-binding: t; -*- + +;; Copyright (C) 2022 Adam Porter + +;; Author: Adam Porter + +;; This program is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see . + +;;; Commentary: + +;; This library provides completing-read of Org entries using `org-ql' +;; search. + +;;; Code: + +(require 'org-ql) + +;;;; Customization + +(defgroup org-ql-completing-read nil + "Completing-read of Org entries using `org-ql' search." + :group 'org-ql) + +(defcustom org-ql-completing-read-reverse-paths t + "Whether to reverse Org outline paths in `org-ql-completing-read' results." + :type 'boolean) + +(defcustom org-ql-completing-read-snippet-function #'org-ql-completing-read--snippet-simple + ;; TODO: I'd like to make the -regexp one the default, but with + ;; default Emacs completion affixation, it can sometimes be a bit + ;; slow, and I don't want that to be a user's first impression. It + ;; may be possible to further optimize the -regexp one so that it + ;; can be used by default. In the meantime, the -simple one seems + ;; fast enough for general use. + "Function used to annotate results in `org-ql-completing-read'. +Function is called at entry beginning. (When set to +`org-ql-completing-read--snippet-regexp', it is called with a +regexp matching plain query tokens.)" + :type '(choice (function-item :tag "Show context around search terms" org-ql-completing-read--snippet-regexp) + (function-item :tag "Show first N characters" org-ql-completing-read--snippet-simple) + (function :tag "Custom function"))) + +(defcustom org-ql-completing-read-snippet-length 51 + "Size of snippets of entry content to include in completion annotations. +Only used when `org-ql-completing-read-snippet-function' is set +to `org-ql-completing-read--snippet-regexp'." + :type 'integer) + +(defcustom org-ql-completing-read-snippet-minimum-token-length 3 + "Query tokens shorter than this many characters are ignored. +That is, they are not included when gathering entry snippets. +This avoids too-small tokens causing performance problems." + :type 'integer) + +(defcustom org-ql-completing-read-snippet-prefix nil + "String prepended to snippets. +For an experience like `org-rifle', use a newline." + :type '(choice (const :tag "None (shown on same line)" nil) + (const :tag "New line (shown under heading)" "\n") + string)) + +(defface org-ql-completing-read-snippet '((t (:inherit font-lock-comment-face))) + "Snippets.") + +;;;; Functions + +;;;;; Completing read + +;;;###autoload +(cl-defun org-ql-completing-read (buffers-files &key query-prefix query-filter + (prompt "Find entry: ")) + "Return marker at Org entry in BUFFERS-FILES selected with `org-ql'. + +QUERY-PREFIX may be a string to prepend to the query entered by +the user (e.g. use \"heading:\" to only search headings, easily +creating a custom command that saves the user from having to type +it). + +QUERY-FILTER may be a function through which the query the user +types is filtered before execution (e.g. it could replace spaces +with commas to turn multiple tokens, which would normally be +treated as multiple predicates, into multiple arguments to a +single predicate)." + (declare (indent defun)) + ;; Emacs's completion API is not always easy to understand, + ;; especially when using "programmed completion." This code was + ;; made possible by the example Clemens Radermacher shared at + ;; . + (let ((table (make-hash-table :test #'equal)) + (window-width (window-width)) + query-tokens snippet-regexp) + (cl-labels ((action + () (font-lock-ensure (point-at-bol) (point-at-eol)) + (let* ((path (thread-first (org-get-outline-path t t) + (org-format-outline-path window-width nil "") + (org-split-string ""))) + (path (if org-ql-completing-read-reverse-paths + (string-join (nreverse path) "\\") + (string-join path "/")))) + (puthash path (point-marker) table) + path)) + (affix (completions) + (cl-loop for completion in completions + for marker = (gethash completion table) + for todo-state = (if-let (it (org-entry-get marker "TODO")) + (concat (propertize it + 'face (org-get-todo-face it)) + " ") + "") + for snippet = (if-let (it (snippet marker)) + (propertize (concat " " it) + 'face 'org-ql-completing-read-snippet) + "") + collect (list completion todo-state snippet))) + (annotate (candidate) + (while-no-input + ;; Using `while-no-input' here doesn't make it as + ;; responsive as, e.g. Helm while typing, but it seems to + ;; help a little when using the org-rifle-style snippets. + (or (snippet (gethash candidate table)) ""))) + (snippet (marker) + (org-with-point-at marker + (or (funcall org-ql-completing-read-snippet-function snippet-regexp) + (org-ql-completing-read--snippet-simple)))) + (group (candidate transform) + (pcase transform + (`nil (buffer-name (marker-buffer (gethash candidate table)))) + (_ candidate))) + (try (string _table _pred point &optional _metadata) + (cons string point)) + (all (string table pred _point) + (all-completions string table pred)) + (collection (str _pred flag) + (when query-prefix + (setf str (concat query-prefix str))) + (pcase flag + ('metadata (list 'metadata + (cons 'group-function #'group) + (cons 'affixation-function #'affix) + (cons 'annotation-function #'annotate))) + (`t (unless (string-empty-p str) + (when query-filter + (setf str (funcall query-filter str))) + (pcase org-ql-completing-read-snippet-function + ('org-ql-completing-read--snippet-regexp + (setf query-tokens + ;; Remove any tokens that specify predicates or are too short. + (--select (not (or (string-match-p (rx bos (1+ (not (any ":"))) ":") it) + (< (length it) org-ql-completing-read-snippet-minimum-token-length))) + (split-string str nil t (rx space))) + snippet-regexp + (when query-tokens + ;; Limiting each context word to 15 characters + ;; prevents excessively long, non-word strings + ;; from ending up in snippets, which can + ;; adversely affect performance. + (rx-to-string `(seq (optional (repeat 1 3 (repeat 1 15 (not space)) (0+ space))) + bow (or ,@query-tokens) (0+ (not space)) + (optional (repeat 1 3 (0+ space) (repeat 1 15 (not space)))))))))) + (org-ql-select buffers-files (org-ql--query-string-to-sexp str) + :action #'action)))))) + ;; NOTE: It seems that the `completing-read' machinery can call, + ;; abort, and re-call the collection function while the user is + ;; typing, which can interrupt the machinery Org uses to prepare + ;; an Org buffer when an Org file is loaded. This results in, + ;; e.g. the buffer being left in fundamental-mode, unprepared to + ;; be used as an Org buffer, which breaks many things and is + ;; very confusing for the user. Ideally, of course, we would + ;; solve this in `org-ql-select', and we already attempt to, but + ;; that function is called by the `completing-read' machinery, + ;; which interrupts it, so we must work around this problem by + ;; ensuring all of the BUFFERS-FILES are loaded and initialized + ;; before calling `completing-read'. + (unless (listp buffers-files) + ;; Since we map across this argument, we ensure it's a list. + (setf buffers-files (list buffers-files))) + (mapc #'org-ql--ensure-buffer buffers-files) + (let* ((completion-styles '(org-ql-completing-read)) + (completion-styles-alist (list (list 'org-ql-completing-read #'try #'all "Org QL Find"))) + (selected (completing-read prompt #'collection nil))) + (gethash selected table))))) + +(defun org-ql-completing-read--snippet-simple (&optional _regexp) + "Return a snippet of the current entry. +Returns up to `org-ql-completing-read-snippet-length' characters." + (save-excursion + (org-end-of-meta-data t) + (unless (org-at-heading-p) + (let ((end (min (+ (point) org-ql-completing-read-snippet-length) + (org-entry-end-position)))) + (concat org-ql-completing-read-snippet-prefix + (truncate-string-to-width + (replace-regexp-in-string "\n" " " (buffer-substring (point) end) + t t) + 50 nil nil t)))))) + +(defun org-ql-completing-read--snippet-regexp (regexp) + "Return a snippet of the current entry's matches for REGEXP." + ;; REGEXP may be nil if there are no qualifying tokens in the query. + (when regexp + (save-excursion + (org-end-of-meta-data t) + (unless (org-at-heading-p) + (let* ((end (org-entry-end-position)) + (snippets (cl-loop while (re-search-forward regexp end t) + concat (match-string 0) concat "…" + do (goto-char (match-end 0))))) + (unless (string-empty-p snippets) + (concat org-ql-completing-read-snippet-prefix + (replace-regexp-in-string (rx (1+ "\n")) " " snippets t t)))))))) + +;;;; Footer + +(provide 'org-ql-completing-read) + +;;; org-ql-completing-read.el ends here diff --git a/org-ql-find.el b/org-ql-find.el index 143a861..a85b937 100644 --- a/org-ql-find.el +++ b/org-ql-find.el @@ -31,6 +31,7 @@ (require 'org) (require 'org-ql) (require 'org-ql-search) +(require 'org-ql-completing-read) ;;;; Customization @@ -38,51 +39,10 @@ "Options for `org-ql-find'." :group 'org-ql) -(defcustom org-ql-find-reverse-paths t - "Whether to reverse Org outline paths in `org-ql-find' results." - :type 'boolean) - (defcustom org-ql-find-goto-hook '(org-show-entry) "Functions called when selecting an entry." :type 'hook) -(defcustom org-ql-find-snippet-function #'org-ql-find--snippet-simple - ;; TODO: I'd like to make the -regexp one the default, but with - ;; default Emacs completion affixation, it can sometimes be a bit - ;; slow, and I don't want that to be a user's first impression. It - ;; may be possible to further optimize the -regexp one so that it - ;; can be used by default. In the meantime, the -simple one seems - ;; fast enough for general use. - "Function used to annotate results in `org-ql-find'. -Function is called at entry beginning. (When set to -`org-ql-find--snippet-regexp', it is called with a regexp -matching plain query tokens.)" - :type '(choice (function-item :tag "Show context around search terms" org-ql-find--snippet-regexp) - (function-item :tag "Show first N characters" org-ql-find--snippet-simple) - (function :tag "Custom function"))) - -(defcustom org-ql-find-snippet-length 51 - "Size of snippets of entry content to include in `org-ql-find' annotations. -Only used when `org-ql-find-snippet-function' is set to -`org-ql-find--snippet-regexp'." - :type 'integer) - -(defcustom org-ql-find-snippet-minimum-token-length 3 - "Query tokens shorter than this many characters are ignored. -That is, they are not included when gathering entry snippets. -This avoids too-small tokens causing performance problems." - :type 'integer) - -(defcustom org-ql-find-snippet-prefix nil - "String prepended to snippets. -For an experience like `org-rifle', use a newline." - :type '(choice (const :tag "None (shown on same line)" nil) - (const :tag "New line (shown under heading)" "\n") - string)) - -(defface org-ql-find-snippet '((t (:inherit font-lock-comment-face))) - "Snippets.") - ;;;; Functions ;;;###autoload @@ -106,116 +66,23 @@ single predicate)." (mapcar #'get-buffer (completing-read-multiple "Buffers: " - (mapcar #'buffer-name - (cl-remove-if-not (lambda (buffer) - (eq 'org-mode (buffer-local-value 'major-mode buffer))) - (buffer-list))) nil t)) + (cl-loop for buffer in (buffer-list) + when (eq 'org-mode (buffer-local-value 'major-mode buffer)) + collect (buffer-name buffer)) + nil t)) (progn (unless (eq major-mode 'org-mode) (user-error "This is not an Org buffer: %S" (current-buffer))) (current-buffer))))) - ;; Emacs's completion API is not always easy to understand, - ;; especially when using "programmed completion." This code was - ;; made possible by the example Clemens Radermacher shared at - ;; . - (let ((table (make-hash-table :test #'equal)) - (window-width (window-width)) - query-tokens snippet-regexp) - (cl-labels ((action - () (font-lock-ensure (point-at-bol) (point-at-eol)) - (let* ((path (thread-first (org-get-outline-path t t) - (org-format-outline-path window-width nil "") - (org-split-string ""))) - (path (if org-ql-find-reverse-paths - (string-join (nreverse path) "\\") - (string-join path "/")))) - (puthash path (point-marker) table) - path)) - (affix (completions) - (cl-loop for completion in completions - for marker = (gethash completion table) - for todo-state = (if-let (it (org-entry-get marker "TODO")) - (concat (propertize it - 'face (org-get-todo-face it)) - " ") - "") - for snippet = (if-let (it (snippet marker)) - (propertize (concat " " it) - 'face 'org-ql-find-snippet) - "") - collect (list completion todo-state snippet))) - (annotate (candidate) - (while-no-input - ;; Using `while-no-input' here doesn't make it as - ;; responsive as, e.g. Helm while typing, but it seems to - ;; help a little when using the org-rifle-style snippets. - (or (snippet (gethash candidate table)) ""))) - (snippet (marker) - (org-with-point-at marker - (or (funcall org-ql-find-snippet-function snippet-regexp) - (org-ql-find--snippet-simple)))) - (group (candidate transform) - (pcase transform - (`nil (buffer-name (marker-buffer (gethash candidate table)))) - (_ candidate))) - (try (string _table _pred point &optional _metadata) - (cons string point)) - (all (string table pred _point) - (all-completions string table pred)) - (collection (str _pred flag) - (when query-prefix - (setf str (concat query-prefix str))) - (pcase flag - ('metadata (list 'metadata - (cons 'group-function #'group) - (cons 'affixation-function #'affix) - (cons 'annotation-function #'annotate))) - (`t (unless (string-empty-p str) - (when query-filter - (setf str (funcall query-filter str))) - (pcase org-ql-find-snippet-function - ('org-ql-find--snippet-regexp - (setf query-tokens - ;; Remove any tokens that specify predicates or are too short. - (--select (not (or (string-match-p (rx bos (1+ (not (any ":"))) ":") it) - (< (length it) org-ql-find-snippet-minimum-token-length))) - (split-string str nil t (rx space))) - snippet-regexp - (when query-tokens - ;; Limiting each context word to 15 characters - ;; prevents excessively long, non-word strings - ;; from ending up in snippets, which can - ;; adversely affect performance. - (rx-to-string `(seq (optional (repeat 1 3 (repeat 1 15 (not space)) (0+ space))) - bow (or ,@query-tokens) (0+ (not space)) - (optional (repeat 1 3 (0+ space) (repeat 1 15 (not space)))))))))) - (org-ql-select buffers-files (org-ql--query-string-to-sexp str) - :action #'action)))))) - ;; NOTE: It seems that the `completing-read' machinery can call, - ;; abort, and re-call the collection function while the user is - ;; typing, which can interrupt the machinery Org uses to prepare - ;; an Org buffer when an Org file is loaded. This results in, - ;; e.g. the buffer being left in fundamental-mode, unprepared to - ;; be used as an Org buffer, which breaks many things and is - ;; very confusing for the user. Ideally, of course, we would - ;; solve this in `org-ql-select', and we already attempt to, but - ;; that function is called by the `completing-read' machinery, - ;; which interrupts it, so we must work around this problem by - ;; ensuring all of the BUFFERS-FILES are loaded and initialized - ;; before calling `completing-read'. - (unless (listp buffers-files) - ;; Since we map across this argument, we ensure it's a list. - (setf buffers-files (list buffers-files))) - (mapc #'org-ql--ensure-buffer buffers-files) - (let* ((completion-styles '(org-ql-find)) - (completion-styles-alist (list (list 'org-ql-find #'try #'all "Org QL Find"))) - (selected (completing-read prompt #'collection nil)) - (marker (gethash selected table))) - (with-current-buffer (marker-buffer marker) - (goto-char marker) - (display-buffer (current-buffer)) - (select-window (get-buffer-window (current-buffer))) - (run-hook-with-args 'org-ql-find-goto-hook)))))) + (let ((marker (org-ql-completing-read buffers-files + :query-prefix query-prefix + :query-filter query-filter + :prompt prompt))) + (with-current-buffer (marker-buffer marker) + (goto-char marker) + (display-buffer (current-buffer)) + (select-window (get-buffer-window (current-buffer))) + (run-hook-with-args 'org-ql-find-goto-hook)))) ;;;###autoload (defun org-ql-find-in-agenda () @@ -229,35 +96,6 @@ single predicate)." (interactive) (org-ql-find (org-ql-search-directories-files))) -(defun org-ql-find--snippet-simple (&optional _regexp) - "Return a snippet of the current entry. -Returns up to `org-ql-find-snippet-length' characters." - (save-excursion - (org-end-of-meta-data t) - (unless (org-at-heading-p) - (let ((end (min (+ (point) org-ql-find-snippet-length) - (org-entry-end-position)))) - (concat org-ql-find-snippet-prefix - (truncate-string-to-width - (replace-regexp-in-string "\n" " " (buffer-substring (point) end) - t t) - 50 nil nil t)))))) - -(defun org-ql-find--snippet-regexp (regexp) - "Return a snippet of the current entry's matches for REGEXP." - ;; REGEXP may be nil if there are no qualifying tokens in the query. - (when regexp - (save-excursion - (org-end-of-meta-data t) - (unless (org-at-heading-p) - (let* ((end (org-entry-end-position)) - (snippets (cl-loop while (re-search-forward regexp end t) - concat (match-string 0) concat "…" - do (goto-char (match-end 0))))) - (unless (string-empty-p snippets) - (concat org-ql-find-snippet-prefix - (replace-regexp-in-string (rx (1+ "\n")) " " snippets t t)))))))) - (provide 'org-ql-find) ;;; org-ql-find.el ends here From 7f4faff27bf68d05262a92607cca58f27ebb258d Mon Sep 17 00:00:00 2001 From: Adam Porter Date: Fri, 19 Aug 2022 03:18:14 -0500 Subject: [PATCH 38/40] Add: (org-ql-refile) --- README.org | 5 ++ org-ql-find.el | 29 +++++++++ org-ql.info | 155 +++++++++++++++++++++++++++---------------------- 3 files changed, 119 insertions(+), 70 deletions(-) diff --git a/README.org b/README.org index 10d6e07..9c9d734 100644 --- a/README.org +++ b/README.org @@ -117,6 +117,10 @@ These commands jump to a heading selected using Emacs's built-in completion faci [[images/org-ql-find.png]] +*** org-ql-refile + +This command refiles the current Org entry to one selected by searching with Org QL completion. It searches files listed in ~org-refile-targets~ as well as the current buffer. + *** org-ql-search /Note: This command supports both sexp queries and [[#non-sexp-query-syntax][non-sexp queries]]./ @@ -542,6 +546,7 @@ Simple links may also be written manually in either sexp or non-sexp form, like: *Added* + Commands ~org-ql-find~, ~org-ql-find-heading~, and ~org-ql-find-path~, which jump to entries selected using Emacs's built-in completion facilities and Org QL queries (like ~helm-org-ql~, but doesn't require Helm.). ++ Command ~org-ql-refile~, which refiles the entry at point to one selected using Org QL completion. + Predicate ~rifle~, which matches an entry if each of the given arguments is found in either the entry's contents or its outline path. This provides very intuitive results, mimicing the behavior of [[https://github.com/alphapapa/org-rifle][=org-rifle=]]. In fact, the results are so useful that it's now the default predicate for plain-string query tokens. (It is also aliased to ~smart~, since it's so "smart," and not all users have used =org-rifle=.) + Option ~org-ql-default-predicate~, applied to plain-string query tokens (before, the ~regexp~ predicate was always used, but now it may be customized). diff --git a/org-ql-find.el b/org-ql-find.el index a85b937..5a482f7 100644 --- a/org-ql-find.el +++ b/org-ql-find.el @@ -84,6 +84,35 @@ single predicate)." (select-window (get-buffer-window (current-buffer))) (run-hook-with-args 'org-ql-find-goto-hook)))) +;;;###autoload +(defun org-ql-refile (marker) + "Refile current entry to MARKER (interactively, one selected with `org-ql'). +Interactive completion uses files listed in `org-refile-targets', +which see (but only the files are used)." + (interactive (let ((buffers-files (delete-dups + ;; Always include the current buffer. + (cons (current-buffer) + (cl-loop for (files-spec . _candidate-spec) in org-refile-targets + append (cl-typecase files-spec + (null (list (current-buffer))) + (symbol (pcase (funcall files-spec) + ((and (pred stringp) file) (list file)) + ((and (pred listp) files) files))) + (list files-spec))))))) + (list (org-ql-completing-read buffers-files :prompt "Refile to: ")))) + (org-refile nil nil + ;; The RFLOC argument: + (list + ;; Name + (org-with-point-at marker + (nth 4 (org-heading-components))) + ;; File + (buffer-file-name (marker-buffer marker)) + ;; nil + nil + ;; Position + marker))) + ;;;###autoload (defun org-ql-find-in-agenda () "Call `org-ql-find' on `org-agenda-files'." diff --git a/org-ql.info b/org-ql.info index cfb9896..96fb8e4 100644 --- a/org-ql.info +++ b/org-ql.info @@ -48,6 +48,7 @@ Usage Commands * org-ql-find:: +* org-ql-refile:: * org-ql-search:: * helm-org-ql:: * org-ql-view:: @@ -201,6 +202,7 @@ File: README.info, Node: Commands, Next: Queries, Up: Usage * Menu: * org-ql-find:: +* org-ql-refile:: * org-ql-search:: * helm-org-ql:: * org-ql-view:: @@ -209,7 +211,7 @@ File: README.info, Node: Commands, Next: Queries, Up: Usage * org-ql-sparse-tree::  -File: README.info, Node: org-ql-find, Next: org-ql-search, Up: Commands +File: README.info, Node: org-ql-find, Next: org-ql-refile, Up: Commands 4.1.1 org-ql-find ----------------- @@ -224,9 +226,19 @@ completion facilities with an Org QL query: • ‘org-ql-find-in-org-directory’ searches in ‘org-directory’.  -File: README.info, Node: org-ql-search, Next: helm-org-ql, Prev: org-ql-find, Up: Commands +File: README.info, Node: org-ql-refile, Next: org-ql-search, Prev: org-ql-find, Up: Commands -4.1.2 org-ql-search +4.1.2 org-ql-refile +------------------- + +This command refiles the current Org entry to one selected by searching +with Org QL completion. It searches files listed in +‘org-refile-targets’ as well as the current buffer. + + +File: README.info, Node: org-ql-search, Next: helm-org-ql, Prev: org-ql-refile, Up: Commands + +4.1.3 org-ql-search ------------------- _Note: This command supports both sexp queries and ._ @@ -269,7 +281,7 @@ text properties are placed on each item, imitating an Agenda buffer.)  File: README.info, Node: helm-org-ql, Next: org-ql-view, Prev: org-ql-search, Up: Commands -4.1.3 helm-org-ql +4.1.4 helm-org-ql ----------------- _Note: This command uses . It is available separately in the package @@ -283,7 +295,7 @@ _Note: This command uses . It is available separately in the package  File: README.info, Node: org-ql-view, Next: org-ql-view-sidebar, Prev: helm-org-ql, Up: Commands -4.1.4 org-ql-view +4.1.5 org-ql-view ----------------- Choose and display a view stored in ‘org-ql-views’. @@ -298,7 +310,7 @@ Choose and display a view stored in ‘org-ql-views’.  File: README.info, Node: org-ql-view-sidebar, Next: org-ql-view-recent-items, Prev: org-ql-view, Up: Commands -4.1.5 org-ql-view-sidebar +4.1.6 org-ql-view-sidebar ------------------------- Show a sidebar window listing views stored in ‘org-ql-views’ for easy @@ -308,7 +320,7 @@ point, and press ‘c’ to customize the view at point.  File: README.info, Node: org-ql-view-recent-items, Next: org-ql-sparse-tree, Prev: org-ql-view-sidebar, Up: Commands -4.1.6 org-ql-view-recent-items +4.1.7 org-ql-view-recent-items ------------------------------ Show items in ‘FILES’ from last ‘DAYS’ days with timestamps of ‘TYPE’. @@ -319,7 +331,7 @@ returned by the function ‘org-agenda-files’.  File: README.info, Node: org-ql-sparse-tree, Prev: org-ql-view-recent-items, Up: Commands -4.1.7 org-ql-sparse-tree +4.1.8 org-ql-sparse-tree ------------------------ Arguments: ‘(query &key keep-previous (buffer (current-buffer)))’ @@ -1009,6 +1021,8 @@ File: README.info, Node: 07-pre, Next: 062, Up: Changelog ‘org-ql-find-path’, which jump to entries selected using Emacs’s built-in completion facilities and Org QL queries (like ‘helm-org-ql’, but doesn’t require Helm.). + • Command ‘org-ql-refile’, which refiles the entry at point to one + selected using Org QL completion. • Predicate ‘rifle’, which matches an entry if each of the given arguments is found in either the entry’s contents or its outline path. This provides very intuitive results, mimicing the behavior @@ -1639,68 +1653,69 @@ GPLv3  Tag Table: Node: Top225 -Node: Contents1764 -Node: Screenshots1887 -Node: Installation2005 -Node: Quelpa2519 -Node: Helm support3047 -Node: Usage3450 -Node: Commands3848 -Node: org-ql-find4274 -Node: org-ql-search4740 -Node: helm-org-ql6434 -Node: org-ql-view6812 -Node: org-ql-view-sidebar7342 -Node: org-ql-view-recent-items7722 -Node: org-ql-sparse-tree8218 -Node: Queries9018 -Node: Non-sexp query syntax10135 -Node: General predicates11894 -Node: Ancestor/descendant predicates18636 -Node: Date/time predicates19764 -Node: Functions / Macros22888 -Node: Agenda-like views23186 -Ref: Function ‘org-ql-block’23348 -Node: Listing / acting-on results24609 -Ref: Caching24817 -Ref: Function ‘org-ql-select’25730 -Ref: Function ‘org-ql-query’28156 -Ref: Macro ‘org-ql’ (deprecated)29930 -Node: Custom predicates30245 -Ref: Macro ‘org-ql-defpred’30469 -Node: Dynamic block33910 -Node: Links36634 -Node: Tips37321 -Node: Changelog37645 -Node: 07-pre38413 -Node: 06239820 -Node: 06140128 -Node: 0640696 -Node: 05243750 -Node: 05144050 -Node: 0544473 -Node: 04946002 -Node: 04846282 -Node: 04746629 -Node: 04647038 -Node: 04547446 -Node: 04447807 -Node: 04348166 -Node: 04248369 -Node: 04148530 -Node: 0448777 -Node: 03252878 -Node: 03153281 -Node: 0353478 -Node: 02356778 -Node: 02257012 -Node: 02157292 -Node: 0257497 -Node: 0161575 -Node: Notes61676 -Node: Comparison with Org Agenda searches61838 -Node: org-sidebar62727 -Node: License63006 +Node: Contents1782 +Node: Screenshots1905 +Node: Installation2023 +Node: Quelpa2537 +Node: Helm support3065 +Node: Usage3468 +Node: Commands3866 +Node: org-ql-find4310 +Node: org-ql-refile4776 +Node: org-ql-search5099 +Node: helm-org-ql6795 +Node: org-ql-view7173 +Node: org-ql-view-sidebar7703 +Node: org-ql-view-recent-items8083 +Node: org-ql-sparse-tree8579 +Node: Queries9379 +Node: Non-sexp query syntax10496 +Node: General predicates12255 +Node: Ancestor/descendant predicates18997 +Node: Date/time predicates20125 +Node: Functions / Macros23249 +Node: Agenda-like views23547 +Ref: Function ‘org-ql-block’23709 +Node: Listing / acting-on results24970 +Ref: Caching25178 +Ref: Function ‘org-ql-select’26091 +Ref: Function ‘org-ql-query’28517 +Ref: Macro ‘org-ql’ (deprecated)30291 +Node: Custom predicates30606 +Ref: Macro ‘org-ql-defpred’30830 +Node: Dynamic block34271 +Node: Links36995 +Node: Tips37682 +Node: Changelog38006 +Node: 07-pre38774 +Node: 06240296 +Node: 06140604 +Node: 0641172 +Node: 05244226 +Node: 05144526 +Node: 0544949 +Node: 04946478 +Node: 04846758 +Node: 04747105 +Node: 04647514 +Node: 04547922 +Node: 04448283 +Node: 04348642 +Node: 04248845 +Node: 04149006 +Node: 0449253 +Node: 03253354 +Node: 03153757 +Node: 0353954 +Node: 02357254 +Node: 02257488 +Node: 02157768 +Node: 0257973 +Node: 0162051 +Node: Notes62152 +Node: Comparison with Org Agenda searches62314 +Node: org-sidebar63203 +Node: License63482  End Tag Table From d7ada532c7d06e91d6e07800ca22d5fbdb970e3e Mon Sep 17 00:00:00 2001 From: Adam Porter Date: Fri, 19 Aug 2022 03:38:43 -0500 Subject: [PATCH 39/40] Add: Alias "c" to (category) --- README.org | 1 + org-ql.el | 2 +- org-ql.info | 57 +++++++++++++++++++++++++++-------------------------- 3 files changed, 31 insertions(+), 29 deletions(-) diff --git a/README.org b/README.org index 9c9d734..3ee3c48 100644 --- a/README.org +++ b/README.org @@ -549,6 +549,7 @@ Simple links may also be written manually in either sexp or non-sexp form, like: + Command ~org-ql-refile~, which refiles the entry at point to one selected using Org QL completion. + Predicate ~rifle~, which matches an entry if each of the given arguments is found in either the entry's contents or its outline path. This provides very intuitive results, mimicing the behavior of [[https://github.com/alphapapa/org-rifle][=org-rifle=]]. In fact, the results are so useful that it's now the default predicate for plain-string query tokens. (It is also aliased to ~smart~, since it's so "smart," and not all users have used =org-rifle=.) + Option ~org-ql-default-predicate~, applied to plain-string query tokens (before, the ~regexp~ predicate was always used, but now it may be customized). ++ Alias ~c~ for predicate ~category~. *Changed* + Give more useful error message for invalid queries. diff --git a/org-ql.el b/org-ql.el index d4dbff9..5b804ba 100644 --- a/org-ql.el +++ b/org-ql.el @@ -1318,7 +1318,7 @@ result form." ;; redefinitions until all of the predicates have been defined. (setf org-ql-defpred-defer t) -(org-ql-defpred category (&rest categories) +(org-ql-defpred (category c) (&rest categories) "Return non-nil if current heading is in one or more of CATEGORIES (a list of strings)." :body (when-let ((category (org-get-category (point)))) (cl-typecase categories diff --git a/org-ql.info b/org-ql.info index 96fb8e4..e191c28 100644 --- a/org-ql.info +++ b/org-ql.info @@ -1033,6 +1033,7 @@ File: README.info, Node: 07-pre, Next: 062, Up: Changelog • Option ‘org-ql-default-predicate’, applied to plain-string query tokens (before, the ‘regexp’ predicate was always used, but now it may be customized). + • Alias ‘c’ for predicate ‘category’. *Changed* • Give more useful error message for invalid queries. @@ -1688,34 +1689,34 @@ Node: Links36995 Node: Tips37682 Node: Changelog38006 Node: 07-pre38774 -Node: 06240296 -Node: 06140604 -Node: 0641172 -Node: 05244226 -Node: 05144526 -Node: 0544949 -Node: 04946478 -Node: 04846758 -Node: 04747105 -Node: 04647514 -Node: 04547922 -Node: 04448283 -Node: 04348642 -Node: 04248845 -Node: 04149006 -Node: 0449253 -Node: 03253354 -Node: 03153757 -Node: 0353954 -Node: 02357254 -Node: 02257488 -Node: 02157768 -Node: 0257973 -Node: 0162051 -Node: Notes62152 -Node: Comparison with Org Agenda searches62314 -Node: org-sidebar63203 -Node: License63482 +Node: 06240347 +Node: 06140655 +Node: 0641223 +Node: 05244277 +Node: 05144577 +Node: 0545000 +Node: 04946529 +Node: 04846809 +Node: 04747156 +Node: 04647565 +Node: 04547973 +Node: 04448334 +Node: 04348693 +Node: 04248896 +Node: 04149057 +Node: 0449304 +Node: 03253405 +Node: 03153808 +Node: 0354005 +Node: 02357305 +Node: 02257539 +Node: 02157819 +Node: 0258024 +Node: 0162102 +Node: Notes62203 +Node: Comparison with Org Agenda searches62365 +Node: org-sidebar63254 +Node: License63533  End Tag Table From e41cdb45912f1731b5b060f9e05d011096663655 Mon Sep 17 00:00:00 2001 From: Adam Porter Date: Thu, 25 Aug 2022 16:55:58 -0500 Subject: [PATCH 40/40] Change: (org-ql-find-goto-hook) Add org-reveal Showing the context is much better for the user. --- org-ql-find.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org-ql-find.el b/org-ql-find.el index 5a482f7..0208a45 100644 --- a/org-ql-find.el +++ b/org-ql-find.el @@ -39,7 +39,7 @@ "Options for `org-ql-find'." :group 'org-ql) -(defcustom org-ql-find-goto-hook '(org-show-entry) +(defcustom org-ql-find-goto-hook '(org-show-entry org-reveal) "Functions called when selecting an entry." :type 'hook)