diff --git a/notes.org b/notes.org index 281f665..e9f1a14 100644 --- a/notes.org +++ b/notes.org @@ -436,6 +436,36 @@ Also, maybe instead of having a single =date= selector, I should have =scheduled | preamble: (level 1) | 1.34 | 0.562950 | 0 | 0 | | no preamble: (level 1) | slowest | 0.754050 | 0 | 0 | +*** =tags= + +If tag inheritance is enabled, we have to check tags on every heading. When it's disabled, we can search directly to headings with the given tags. + +#+BEGIN_SRC elisp + (let ((org-use-tag-inheritance t)) + (org-ql-preamble-bench :times 1 + :file "~/org/inbox.org" + :query (tags "Emacs"))) +#+END_SRC + +#+RESULTS: +| Form | x faster than next | Total runtime | # of GCs | Total GC runtime | +|-----------------------------+--------------------+---------------+----------+------------------| +| no preamble: (tags "Emacs") | 1.01 | 1.899647 | 0 | 0 | +| preamble: (tags "Emacs") | slowest | 1.921799 | 0 | 0 | + +#+BEGIN_SRC elisp + (let ((org-use-tag-inheritance nil)) + (org-ql-preamble-bench :times 1 + :file "~/org/inbox.org" + :query (tags "Emacs"))) +#+END_SRC + +#+RESULTS: +| Form | x faster than next | Total runtime | # of GCs | Total GC runtime | +|-----------------------------+--------------------+---------------+----------+------------------| +| preamble: (tags "Emacs") | 2.08 | 0.274555 | 0 | 0 | +| no preamble: (tags "Emacs") | slowest | 0.570116 | 0 | 0 | + ** Using =org-element-parse-buffer= This basically works, as a very basic kind of agenda view, but we can already see that it's much slower (at least, for single-day views) because =org-element-parse-buffer= is slow compared to the agenda code. diff --git a/org-ql.el b/org-ql.el index bb6e726..4c26b3c 100644 --- a/org-ql.el +++ b/org-ql.el @@ -338,6 +338,14 @@ replace the clause with a preamble." (`(level ,num) (setq org-ql-preamble (rx-to-string `(seq bol (repeat ,num "*") " ") t)) nil) + ((and `(tags . ,tags) (guard (not org-use-tag-inheritance))) + ;; When tag inheritance is disabled, we only consider direct tags, + ;; so we can search directly to headings containing one of the tags. + (setq org-ql-preamble (rx-to-string `(seq bol (1+ "*") (1+ space) (1+ not-newline) + ":" (or ,@tags) ":") + t)) + ;; Return nil, because we don't need to test the predicate. + nil) (`(and . ,rest) (let ((clauses (mapcar #'rec rest))) `(and ,@(-non-nil clauses)))) diff --git a/tests/test-org-ql.el b/tests/test-org-ql.el index 67d92be..9f7e5d8 100644 --- a/tests/test-org-ql.el +++ b/tests/test-org-ql.el @@ -393,6 +393,35 @@ Based on Buttercup macro `it'." :sort todo :action (org-ql-test-org-get-heading)) :to-equal '("Visit the moon" "Rewrite Emacs in Common Lisp" "Write a symphony")))) + (describe "(tags)" + (org-ql-it "without arguments" + (expect (org-ql test-buffer + (tags) + :action (org-ql-test-org-get-heading)) + :to-equal '("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" "Order a pizza" "Get haircut" "Internet" "Spaceship lease" "Fix flux capacitor" "/r/emacs" "Shop for groceries" "Rewrite Emacs in Common Lisp" "Write a symphony")) + (expect (org-ql test-buffer + (not (tags)) + :action (org-ql-test-org-get-heading)) + :to-equal '("Test data" "Recurring" "Sunrise/sunset" "Ideas" "Code" "Agenda examining" "Agenda censoring" "Auto grouping" "Auto categories" "Date" "Effort" "Misc" "let-plist" "Profiling"))) + (org-ql-it "with a tag" + (expect (org-ql test-buffer + (tags "Emacs") + :action (org-ql-test-org-get-heading)) + :to-equal '("/r/emacs" "Rewrite Emacs in Common Lisp")) + (expect (org-ql test-buffer + (not (tags "Emacs")) + :action (org-ql-test-org-get-heading)) + :to-equal '("Test data" "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" "Order a pizza" "Get haircut" "Internet" "Spaceship lease" "Fix flux capacitor" "Recurring" "Shop for groceries" "Sunrise/sunset" "Ideas" "Write a symphony" "Code" "Agenda examining" "Agenda censoring" "Auto grouping" "Auto categories" "Date" "Effort" "Misc" "let-plist" "Profiling"))) + (org-ql-it "with 2 tags" + (expect (org-ql test-buffer + (tags "Emacs" "space") + :action (org-ql-test-org-get-heading)) + :to-equal '("Visit Mars" "Visit the moon" "/r/emacs" "Rewrite Emacs in Common Lisp")) + (expect (org-ql test-buffer + (not (tags "Emacs" "space")) + :action (org-ql-test-org-get-heading)) + :to-equal '("Test data" "Take over the universe" "Take over the world" "Skype with president of Antarctica" "Take over Mars" "Take over the moon" "Practice leaping tall buildings in a single bound" "Renew membership in supervillain club" "Learn universal sign language" "Order a pizza" "Get haircut" "Internet" "Spaceship lease" "Fix flux capacitor" "Recurring" "Shop for groceries" "Sunrise/sunset" "Ideas" "Write a symphony" "Code" "Agenda examining" "Agenda censoring" "Auto grouping" "Auto categories" "Date" "Effort" "Misc" "let-plist" "Profiling")))) + (describe "(ts)" (org-ql-it "without arguments" (expect (org-ql test-buffer