Add: (property) :multi argument

This commit is contained in:
SqrtMinusOne 2023-10-22 21:14:49 +03:00
parent ac2d43588a
commit 5f54a2c653
2 changed files with 26 additions and 11 deletions

View file

@ -242,7 +242,7 @@ Arguments are listed next to predicate names, where applicable.
- Aliases: ~olps~. - 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. + =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~). + =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 &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). + =property (property &optional value &key inherit multi)= :: 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). If ~MULTI~ is non-nil, also check for multi-value properties.
+ =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. + =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=. - 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. + =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.

View file

@ -1784,13 +1784,14 @@ priority B)."
(cl-loop for priority-arg in args (cl-loop for priority-arg in args
thereis (= item-priority (* 1000 (- org-lowest-priority (string-to-char priority-arg))))))))) thereis (= item-priority (* 1000 (- org-lowest-priority (string-to-char priority-arg)))))))))
(org-ql-defpred property (property &optional value &key inherit) (org-ql-defpred property (property &optional value &key inherit multi)
"Return non-nil if current entry has PROPERTY, and optionally VALUE. "Return non-nil if current entry has PROPERTY, and optionally VALUE.
If INHERIT is nil, only match entries with PROPERTY set on the If INHERIT is nil, only match entries with PROPERTY set on the
entry; if t, also match entries with inheritance. If INHERIT is entry; if t, also match entries with inheritance. If INHERIT is
not specified, use the Boolean value of not specified, use the Boolean value of
`org-use-property-inheritance', which see (i.e. it is only `org-use-property-inheritance', which see (i.e. it is only
interpreted as nil or non-nil)." interpreted as nil or non-nil). If MULTI is non-nil, also check for
multi-value properties."
:normalizers ((`(,predicate-names) :normalizers ((`(,predicate-names)
;; HACK: This clause protects against the case in ;; HACK: This clause protects against the case in
;; which the arguments are nil, which would cause an ;; which the arguments are nil, which would cause an
@ -1811,26 +1812,31 @@ interpreted as nil or non-nil)."
(list 'property property value (list 'property property value
:inherit (if (plist-member plist :inherit) :inherit (if (plist-member plist :inherit)
(plist-get plist :inherit) (plist-get plist :inherit)
org-use-property-inheritance)))) org-use-property-inheritance)
:multi (when (plist-member plist :multi)
(plist-get plist :multi)))))
;; MAYBE: Should case folding be disabled for properties? What about values? ;; MAYBE: Should case folding be disabled for properties? What about values?
;; MAYBE: Support (property) without args. ;; MAYBE: Support (property) without args.
;; NOTE: When inheritance is enabled, the preamble can't be used, ;; NOTE: When inheritance is enabled, the preamble can't be used,
;; which will make the search slower. ;; which will make the search slower.
:preambles ((`(,predicate-names ,property ,value . ,(map :inherit)) :preambles ((`(,predicate-names ,property ,value ,(map :multi) . ,(map :inherit))
;; We do NOT return nil, because the predicate still needs to be tested, ;; 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. ;; because the regexp could match a string not inside a property drawer.
(list :regexp (unless inherit (list :regexp (unless inherit
(rx-to-string `(seq bol (0+ space) ":" ,property ":" (rx-to-string `(seq bol (0+ space) ":" ,property
,@(when multi '((? "+"))) ":"
(1+ space) ,value (0+ space) eol))) (1+ space) ,value (0+ space) eol)))
:query query)) :query query))
(`(,predicate-names ,property . ,(map :inherit)) (`(,predicate-names ,property ,(map :multi) . ,(map :inherit))
;; We do NOT return nil, because the predicate still needs to be tested, ;; 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. ;; because the regexp could match a string not inside a property drawer.
;; NOTE: The preamble only matches if there appears to be a value. ;; NOTE: The preamble only matches if there appears to be a value.
;; A line like ":ID: " without any other text does not match. ;; A line like ":ID: " without any other text does not match.
(list :regexp (unless inherit (list :regexp (unless inherit
(rx-to-string `(seq bol (0+ space) ":" ,property ":" (1+ space) (rx-to-string `(seq bol (0+ space) ":" ,property
,@(when multi '((? "+")))
":" (1+ space)
(minimal-match (1+ not-newline)) eol))) (minimal-match (1+ not-newline)) eol)))
:query query))) :query query)))
:body :body
@ -1848,9 +1854,18 @@ interpreted as nil or non-nil)."
;; TODO: Since --value-at doesn't account for inheritance, ;; TODO: Since --value-at doesn't account for inheritance,
;; we should generalize --tags-at to also work for property ;; we should generalize --tags-at to also work for property
;; inheritance and use it here, which should be much faster. ;; inheritance and use it here, which should be much faster.
(string-equal value (org-ql--value-at (if multi
(point) (lambda () (when-let (values (org-ql--value-at
(org-entry-get (point) property inherit))))))))) (point) (lambda ()
;; The default separator is space
(let ((org-property-separators `((,property . "\n"))))
(org-entry-get (point) property inherit)))))
(seq-some (lambda (v)
(string-equal value v))
(split-string values "\n")))
(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 property-local, property-inherit, etc. to match tags predicates.
;; TODO: Add tests for property inheritance. ;; TODO: Add tests for property inheritance.