From e54ba0102c69273183a5ffafafe878f820ab9e5f Mon Sep 17 00:00:00 2001 From: Adam Porter Date: Fri, 7 Oct 2022 18:33:35 -0500 Subject: [PATCH] Add: (property) :inherit argument --- README.org | 3 +- org-ql.el | 50 ++++++++++++++++++------- org-ql.info | 105 +++++++++++++++++++++++++++------------------------- 3 files changed, 94 insertions(+), 64 deletions(-) diff --git a/README.org b/README.org index 3ee3c48a..7ccf544f 100644 --- a/README.org +++ b/README.org @@ -237,7 +237,7 @@ Arguments are listed next to predicate names, where applicable. - Aliases: ~olps~. + =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. + =priority (&rest args)= :: Return non-nil if current heading has a certain priority. ~ARGS~ may be either a list of one or more priority letters as strings, or a comparator function symbol followed by a priority letter string. For example: ~(priority "A") (priority "A" "B") (priority '>= "B")~ Note that items without a priority cookie never match this predicate (while Org itself considers items without a cookie to have the default priority, which, by default, is equal to priority ~B~). -+ =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)~. ++ =property (property &optional value &key inherit)= :: Return non-nil if current entry has ~PROPERTY~ (a string), and optionally ~VALUE~ (a string). If ~INHERIT~ is nil, only match entries with ~PROPERTY~ set on the entry; if t, also match entries with inheritance. If ~INHERIT~ is not specified, use the Boolean value of ~org-use-property-inheritance~, which see (i.e. it is only interpreted as nil or non-nil). + =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. @@ -550,6 +550,7 @@ Simple links may also be written manually in either sexp or non-sexp form, like: + 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~. ++ Predicate ~property~ now accepts the argument ~:inherit~ to match entries with property inheritance, and when unspecified, the option ~org-use-property-inheritance~ controls whether inheritance is used. *Changed* + Give more useful error message for invalid queries. diff --git a/org-ql.el b/org-ql.el index 5b804bab..c4c1eaf6 100644 --- a/org-ql.el +++ b/org-ql.el @@ -1729,30 +1729,43 @@ priority B)." (cl-loop for priority-arg in args thereis (= item-priority (* 1000 (- org-lowest-priority (string-to-char priority-arg))))))))) -(org-ql-defpred property (property &optional value) - "Return non-nil if current entry has PROPERTY (a string), and optionally VALUE (a string)." - :normalizers ((`(,predicate-names ,property . ,value) +(org-ql-defpred property (property &optional value &key inherit) + "Return non-nil if current entry has PROPERTY (a string), and optionally VALUE (a string). +If INHERIT is nil, only match entries with PROPERTY set on the +entry; if t, also match entries with inheritance. If INHERIT is +not specified, use the Boolean value of +`org-use-property-inheritance', which see (i.e. it is only +interpreted as nil or non-nil)." + :normalizers ((`(,predicate-names ,property ,value . ,plist) ;; Convert keyword property arguments to strings. Non-sexp ;; queries result in keyword property arguments (because to do ;; otherwise would require ugly special-casing in the parsing). (when (keywordp property) (setf property (substring (symbol-name property) 1))) - (cons 'property (cons property value)))) + (list 'property property value + :inherit (if (plist-member plist :inherit) + (plist-get plist :inherit) + org-use-property-inheritance)))) ;; MAYBE: Should case folding be disabled for properties? What about values? ;; MAYBE: Support (property) without args. - :preambles ((`(,predicate-names ,property ,value) + + ;; NOTE: When inheritance is enabled, the preamble can't be used, + ;; which will make the search slower. + :preambles ((`(,predicate-names ,property ,value . ,(map :inherit)) ;; We do NOT return nil, because the predicate still needs to be tested, ;; because the regexp could match a string not inside a property drawer. - (list :regexp (rx-to-string `(seq bol (0+ space) ":" ,property ":" - (1+ space) ,value (0+ space) eol)) + (list :regexp (unless inherit + (rx-to-string `(seq bol (0+ space) ":" ,property ":" + (1+ space) ,value (0+ space) eol))) :query query)) - (`(,predicate-names ,property) + (`(,predicate-names ,property . ,(map :inherit)) ;; We do NOT return nil, because the predicate still needs to be tested, ;; because the regexp could match a string not inside a property drawer. ;; NOTE: The preamble only matches if there appears to be a value. ;; A line like ":ID: " without any other text does not match. - (list :regexp (rx-to-string `(seq bol (0+ space) ":" ,property ":" (1+ space) - (minimal-match (1+ not-newline)) eol)) + (list :regexp (unless inherit + (rx-to-string `(seq bol (0+ space) ":" ,property ":" (1+ space) + (minimal-match (1+ not-newline)) eol))) :query query))) :body (pcase property @@ -1760,10 +1773,21 @@ priority B)." (_ (pcase value ('nil ;; Check that PROPERTY exists - (org-entry-get (point) property)) + (org-ql--value-at + (point) (lambda () + (org-entry-get (point) property)))) (_ - ;; Check that PROPERTY has VALUE - (string-equal value (org-entry-get (point) property 'selective))))))) + ;; Check that PROPERTY has VALUE. + + ;; TODO: Since --value-at doesn't account for inheritance, + ;; we should generalize --tags-at to also work for property + ;; inheritance and use it here, which should be much faster. + (string-equal value (org-ql--value-at + (point) (lambda () + (org-entry-get (point) property inherit))))))))) + +;; TODO: Add property-local, property-inherit, etc. to match tags predicates. +;; TODO: Add tests for property inheritance. (org-ql-defpred (regexp r) (&rest regexps) "Return non-nil if current entry matches all of REGEXPS (regexp strings)." diff --git a/org-ql.info b/org-ql.info index e191c282..18064c03 100644 --- a/org-ql.info +++ b/org-ql.info @@ -476,12 +476,13 @@ Arguments are listed next to predicate names, where applicable. predicate (while Org itself considers items without a cookie to have the default priority, which, by default, is equal to priority ‘B’). -‘property (property &optional value)’ +‘property (property &optional value &key inherit)’ 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)’. + optionally ‘VALUE’ (a string). If ‘INHERIT’ is nil, only match + entries with ‘PROPERTY’ set on the entry; if t, also match entries + with inheritance. If ‘INHERIT’ is not specified, use the Boolean + value of ‘org-use-property-inheritance’, which see (i.e. it is + only interpreted as nil or non-nil). ‘regexp (&rest regexps)’ Return non-nil if current entry matches all of ‘REGEXPS’ (regexp strings). Matches against entire entry, from beginning of its @@ -1034,6 +1035,10 @@ File: README.info, Node: 07-pre, Next: 062, Up: Changelog tokens (before, the ‘regexp’ predicate was always used, but now it may be customized). • Alias ‘c’ for predicate ‘category’. + • Predicate ‘property’ now accepts the argument ‘:inherit’ to match + entries with property inheritance, and when unspecified, the option + ‘org-use-property-inheritance’ controls whether inheritance is + used. *Changed* • Give more useful error message for invalid queries. @@ -1672,51 +1677,51 @@ 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: 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 +Node: Ancestor/descendant predicates19085 +Node: Date/time predicates20213 +Node: Functions / Macros23337 +Node: Agenda-like views23635 +Ref: Function ‘org-ql-block’23797 +Node: Listing / acting-on results25058 +Ref: Caching25266 +Ref: Function ‘org-ql-select’26179 +Ref: Function ‘org-ql-query’28605 +Ref: Macro ‘org-ql’ (deprecated)30379 +Node: Custom predicates30694 +Ref: Macro ‘org-ql-defpred’30918 +Node: Dynamic block34359 +Node: Links37083 +Node: Tips37770 +Node: Changelog38094 +Node: 07-pre38862 +Node: 06240672 +Node: 06140980 +Node: 0641548 +Node: 05244602 +Node: 05144902 +Node: 0545325 +Node: 04946854 +Node: 04847134 +Node: 04747481 +Node: 04647890 +Node: 04548298 +Node: 04448659 +Node: 04349018 +Node: 04249221 +Node: 04149382 +Node: 0449629 +Node: 03253730 +Node: 03154133 +Node: 0354330 +Node: 02357630 +Node: 02257864 +Node: 02158144 +Node: 0258349 +Node: 0162427 +Node: Notes62528 +Node: Comparison with Org Agenda searches62690 +Node: org-sidebar63579 +Node: License63858  End Tag Table