Add heading-regexp predicate. Fix #64

This commit is contained in:
Feng Shu 2019-12-08 13:25:58 +08:00
parent b9dfd7d93b
commit 486778e9fa
3 changed files with 48 additions and 12 deletions

View file

@ -153,19 +153,20 @@ An =org-ql= query is a lisp form which may contain arbitrary lisp forms, as well
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 |
|-------------------------------------------------+----------------------------------------------|
| ~(todo)~ | ~todo:~ |
| ~(todo "SOMEDAY")~ | ~todo:SOMEDAY~ |
| ~(todo "SOMEDAY" "WAITING")~ | ~todo:SOMEDAY,WAITING~ |
| ~(ts :on today)~ | ~ts:on=today~ |
| ~(ts-active :from "2017-01-01" :to "2018-01-01")~ | ~ts-active:from=2017-01-01,to=2018-01-01~ |
| ~(clocked :on -1)~ | ~clocked:on=-1~ |
| ~(heading "quoted phrase" "word")~ | ~heading:"quoted phrase",word~ |
| ~(and (tags "book" "books") (priority "A"))~ | ~tags:book,books priority:A~ |
| Sexp syntax | Non-sexp syntax |
|---------------------------------------------------+--------------------------------------------------|
| ~(todo)~ | ~todo:~ |
| ~(todo "SOMEDAY")~ | ~todo:SOMEDAY~ |
| ~(todo "SOMEDAY" "WAITING")~ | ~todo:SOMEDAY,WAITING~ |
| ~(ts :on today)~ | ~ts:on=today~ |
| ~(ts-active :from "2017-01-01" :to "2018-01-01")~ | ~ts-active:from=2017-01-01,to=2018-01-01~ |
| ~(clocked :on -1)~ | ~clocked:on=-1~ |
| ~(heading "quoted phrase" "word")~ | ~heading:"quoted phrase",word~ |
| ~(heading-regexp "quoted phrase" "[Ww]ord")~ | ~heading-regexp:"quoted phrase",[Ww]ord~ |
| ~(and (tags "book" "books") (priority "A"))~ | ~tags:book,books priority:A~ |
| ~(src :lang "elisp" :regexps ("defun"))~ | ~src:defun,lang=elisp~ or ~src:lang=elisp,defun~ |
| ~(and (tags "space") (not (regexp "moon")))~ | ~tags:space !moon~ |
| ~(priority >= B)~ | ~priority:A,B~ |
| ~(and (tags "space") (not (regexp "moon")))~ | ~tags:space !moon~ |
| ~(priority >= B)~ | ~priority:A,B~ |
Note that the =priority= predicate does not support comparators in the non-sexp syntax, so multiple priorities should be passed instead, as seen in the last example.
@ -183,6 +184,7 @@ Arguments are listed next to predicate names, where applicable.
+ =habit= :: Return non-nil if entry is a habit.
+ =heading (&rest regexps)= :: Return non-nil if current entry's heading matches all ~REGEXPS~ (regexp strings).
- Aliases: =h=.
+ =heading-regexp (&rest regexps)= :: Return non-nil if current entry's heading matches all ~REGEXPS~ (regexp strings).
+ =level (level-or-comparator &optional level)= :: Return non-nil if current heading's outline level matches arguments. The following forms are accepted: ~(level NUMBER)~: Matches if heading level is ~NUMBER~. ~(level NUMBER NUMBER)~: Matches if heading level is equal to or between NUMBERs. ~(level COMPARATOR NUMBER)~: Matches if heading level compares to ~NUMBER~ with ~COMPARATOR~. ~COMPARATOR~ may be ~<~, ~<=~, ~>~, or ~>=~.
+ =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")~.
- Aliases: ~olp~.
@ -385,6 +387,7 @@ Expands into a call to ~org-ql-select~ with the same arguments. For convenience
+ *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=).
- Predicate ~heading-regexp~, which matchs heading with regexps.
- Predicate ~src~, which matches Org Babel source blocks.
- Alias =h= for =heading= predicate.
- Alias =r= for =regexp= predicate. (Thanks to [[https://github.com/tumashu][Feng Shu]].)

View file

@ -696,6 +696,23 @@ replace the clause with a preamble."
'no-group))
element)
;; Heading regexp.
;; MAYBE: Adjust regexp to avoid matching in tag list.
(`(heading-regexp ,regexp)
;; Only one regexp: match with preamble, then let predicate confirm (because
;; the match could be in e.g. the tags rather than the heading text).
(setq org-ql-preamble (rx-to-string `(seq bol (1+ "*") (1+ blank) (0+ nonl)
(regexp ,regexp))
'no-group))
element)
(`(heading-regexp . ,regexps)
;; Multiple regexps: use preamble to match against first
;; regexp, then let the predicate match the rest.
(setq org-ql-preamble (rx-to-string `(seq bol (1+ "*") (1+ blank) (0+ nonl)
(regexp ,(car regexps)))
'no-group))
element)
;; Heading levels.
(`(level ,comparator-or-num ,num)
(let ((repeat (pcase comparator-or-num
@ -1149,6 +1166,12 @@ priority B)."
(let ((heading (org-get-heading 'no-tags 'no-todo)))
(--all? (string-match it heading) regexps)))
(org-ql--defpred heading-regexp (&rest regexps)
"Return non-nil if current entry's heading matches all REGEXPS (regexp strings)."
;; TODO: In Org 9.2+, `org-get-heading' takes 2 more arguments.
(let ((heading (org-get-heading 'no-tags 'no-todo)))
(--all? (string-match it heading) regexps)))
(org-ql--defpred property (property &optional value)
"Return non-nil if current entry has PROPERTY (a string), and optionally VALUE (a string)."
(pcase property

View file

@ -513,6 +513,16 @@ RESULTS should be a list of strings as returned by
(org-ql-expect ((heading "Take over" "world"))
'("Take over the world"))))
(describe "(heading-regexp)"
(org-ql-it "with one argument"
(org-ql-expect ((heading-regexp "w[ol]r[lm]d"))
;; NOTE: This--correctly--does not match the "Skype with president of
;; Antarctica" heading, which has the tag ":world:" in its heading line.
'("Take over the world")))
(org-ql-it "with two arguments"
(org-ql-expect ((heading-regexp "Take over" "w[ol]r[lm]d"))
'("Take over the world"))))
(describe "(outline-path)"
(org-ql-it "with one argument"
(org-ql-expect ((outline-path "symphony"))