Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add: (property) :multi argument #379

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.org
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,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 &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.
- 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.
Expand Down
35 changes: 25 additions & 10 deletions org-ql.el
Original file line number Diff line number Diff line change
Expand Up @@ -1784,13 +1784,14 @@ 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 &key inherit)
(org-ql-defpred property (property &optional value &key inherit multi)
"Return non-nil if current entry has PROPERTY, and optionally VALUE.
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)."
interpreted as nil or non-nil). If MULTI is non-nil, also check for
multi-value properties."
:normalizers ((`(,predicate-names)
;; HACK: This clause protects against the case in
;; which the arguments are nil, which would cause an
Expand All @@ -1811,26 +1812,31 @@ interpreted as nil or non-nil)."
(list 'property property value
:inherit (if (plist-member 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: Support (property) without args.

;; NOTE: When inheritance is enabled, the preamble can't be used,
;; 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,
;; because the regexp could match a string not inside a property drawer.
(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)))
: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,
;; 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 (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)))
:query query)))
:body
Expand All @@ -1848,9 +1854,18 @@ interpreted as nil or non-nil)."
;; 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)))))))))
(if multi
(when-let (values (org-ql--value-at
(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 tests for property inheritance.
Expand Down