Add: Selectors (children) and (descendants)
This commit is contained in:
parent
515b4960c4
commit
76debff1c5
3 changed files with 64 additions and 0 deletions
10
README.org
10
README.org
|
|
@ -73,6 +73,13 @@ More examples are available in [[examples.org]].
|
|||
(tags "Emacs")
|
||||
(priority "A")))
|
||||
(agenda)))))
|
||||
|
||||
;; Show a "stuck projects" view: tasks that are not done and have only
|
||||
;; non-task children.
|
||||
(org-ql-agenda (org-agenda-files)
|
||||
(and (todo)
|
||||
(children)
|
||||
(not (children (todo)))))
|
||||
#+END_SRC
|
||||
|
||||
* Usage
|
||||
|
|
@ -136,10 +143,12 @@ A query is a lisp form which may contain arbitrary lisp forms, as well as certai
|
|||
Arguments are listed next to predicate names, where applicable.
|
||||
|
||||
+ ~category (&optional categories)~ :: Return non-nil if current heading is in one or more of ~CATEGORIES~ (a list of strings).
|
||||
+ ~children (&optional query)~ :: Return non-nil if current heading has direct child headings. If ~QUERY~, test it against child headings. This selector may be nested, e.g. to match grandchild headings.
|
||||
+ ~clocked (&key from to on)~ :: Return non-nil if current entry was clocked in given period. If no arguments are specified, return non-nil if entry was clocked at any time. If ~FROM~, return non-nil if entry was clocked on or after ~FROM~. If ~TO~, return non-nil if entry was clocked on or before ~TO~. If ~ON~, return non-nil if entry was clocked on date ~ON~. ~FROM~, ~TO~, and ~ON~ should be strings parseable by ~parse-time-string~ but may omit the time value. Note: Clock entries are expected to be clocked out. Currently clocked entries (i.e. with unclosed timestamp ranges) are ignored.
|
||||
+ ~closed (&optional comparator target-date)~ :: Return non-nil if entry's closed date compares with ~TARGET-DATE~ using ~COMPARATOR~. ~TARGET-DATE~ should be a string parseable by ~date-to-day~. ~COMPARATOR~ should be a function (like ~<=~).
|
||||
+ ~date (&optional comparator target-date type)~ :: Return non-nil if Org entry at point has date of ~TYPE~ that compares with ~TARGET-DATE~ using ~COMPARATOR~. Checks all Org-formatted timestamp strings in entry. ~TYPE~ may be ~active~, ~inactive~, or ~all~, to control whether active, inactive, or all timestamps are checked. Ranges of each type are also checked. ~TARGET-DATE~ should be a string parseable by ~date-to-day~. ~COMPARATOR~ should be a function (like ~<=~).
|
||||
+ ~deadline (&optional comparator target-date)~ :: Return non-nil if entry's deadline compares with ~TARGET-DATE~ using ~COMPARATOR~. ~TARGET-DATE~ should be a string parseable by ~date-to-day~; or if omitted, it is determined automatically using ~org-deadline-warning-days~. ~COMPARATOR~ should be a function (like ~<=~).
|
||||
+ ~descendants (&optional query)~ :: Return non-nil if current heading has descendant headings. If ~QUERY~, test it against descendant headings. This selector may be nested (if you can grok the nesting!).
|
||||
+ ~done~ :: Return non-nil if entry's ~TODO~ keyword is in ~org-done-keywords~.
|
||||
+ ~habit~ :: Return non-nil if entry is a habit.
|
||||
+ ~heading (regexp)~ :: Return non-nil if current entry's heading matches ~REGEXP~ (a regexp string).
|
||||
|
|
@ -366,6 +375,7 @@ Expands into a call to ~org-ql-select~ with the same arguments. For convenience
|
|||
+ 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=.
|
||||
+ Selectors ~(children)~ and ~(descendants)~.
|
||||
|
||||
*Changed*
|
||||
+ Function ~org-ql-query~ renamed to ~org-ql-select~. ~org-ql-query~ now refers to a new function.
|
||||
|
|
|
|||
31
org-ql.el
31
org-ql.el
|
|
@ -253,6 +253,11 @@ Replaces bare strings with (regexp) selectors, and appropriate
|
|||
,@(mapcar #'rec clauses)))
|
||||
;; TODO: Combine (regexp) when appropriate (i.e. inside an OR, not an AND).
|
||||
((pred stringp) `(regexp ,element))
|
||||
;; Quote children queries so the user doesn't have to.
|
||||
(`(children ,query) `(children ',query))
|
||||
(`(children) '(children (lambda () t)))
|
||||
(`(descendants ,query) `(descendants ',query))
|
||||
(`(descendants) '(descendants (lambda () t)))
|
||||
(`(,(or 'ts-active 'ts-a) . ,rest) `(ts :type active ,@rest))
|
||||
(`(,(or 'ts-inactive 'ts-i) . ,rest) `(ts :type inactive ,@rest))
|
||||
(_ element))))
|
||||
|
|
@ -549,6 +554,32 @@ empty time values to 23:59:59; otherwise, to 00:00:00."
|
|||
|
||||
;;;;; Predicates
|
||||
|
||||
(org-ql--defpred children (query)
|
||||
"Return non-nil if current entry has children matching QUERY."
|
||||
(save-excursion
|
||||
(save-restriction
|
||||
(org-narrow-to-subtree)
|
||||
(when (org-goto-first-child)
|
||||
;; Lisp makes this easy and elegant: all we do is modify the query,
|
||||
;; nesting it inside an (and), and it doesn't descend into grandchildren.
|
||||
(let* ((level (org-current-level))
|
||||
(query (cl-typecase query
|
||||
(byte-code-function `(and (level ,level)
|
||||
(funcall ,query)))
|
||||
(t `(and (level ,level)
|
||||
,query)))))
|
||||
(org-ql-select (current-buffer)
|
||||
query :narrow t :action (lambda () t)))))))
|
||||
|
||||
(org-ql--defpred descendants (query)
|
||||
"Return non-nil if current entry has descendants matching QUERY."
|
||||
(save-excursion
|
||||
(save-restriction
|
||||
(org-narrow-to-subtree)
|
||||
(when (org-goto-first-child)
|
||||
(org-ql-select (current-buffer)
|
||||
query :narrow t :action (lambda () t))))))
|
||||
|
||||
(org-ql--defpred clocked (&key from to _on)
|
||||
;; The underscore before `on' prevents "unused lexical variable" warnings, because we
|
||||
;; pre-process that argument in a macro before this function is called.
|
||||
|
|
|
|||
|
|
@ -219,6 +219,29 @@ RESULTS should be a list of strings as returned by
|
|||
(org-ql-expect ((category "ambition"))
|
||||
'("Take over the universe" "Take over the world" "Skype with president of Antarctica" "Take over Mars" "Visit Mars" "Take over the moon" "Visit the moon" "Practice leaping tall buildings in a single bound" "Renew membership in supervillain club" "Learn universal sign language"))))
|
||||
|
||||
(describe "(children)"
|
||||
(org-ql-it "without arguments"
|
||||
(org-ql-expect ((children))
|
||||
'("Test data" "Take over the universe" "Take over the world" "Take over Mars" "Take over the moon" "Recurring" "Ideas" "Code" "Misc")))
|
||||
(org-ql-it "with sub-query"
|
||||
(org-ql-expect ((children (todo "CHECK")))
|
||||
'("Recurring")))
|
||||
(org-ql-it "with grandchildren query"
|
||||
;; It's really cool how this works. It's so simple.
|
||||
(org-ql-expect ((children (children "moon")))
|
||||
'("Test data" "Take over the universe"))))
|
||||
|
||||
(describe "(descendants)"
|
||||
(org-ql-it "without arguments"
|
||||
(org-ql-expect ((descendants))
|
||||
'("Test data" "Take over the universe" "Take over the world" "Take over Mars" "Take over the moon" "Recurring" "Ideas" "Code" "Misc")))
|
||||
(org-ql-it "with sub-query"
|
||||
(org-ql-expect ((descendants (todo "CHECK")))
|
||||
'("Test data" "Recurring")))
|
||||
(org-ql-it "with granddescendants query"
|
||||
(org-ql-expect ((descendants (descendants "moon")))
|
||||
'("Test data" "Take over the universe" "Take over the moon" "Code"))))
|
||||
|
||||
(describe "(clocked)"
|
||||
|
||||
(org-ql-it "without arguments"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue