Skip to content

Commit 37ba30c

Browse files
committed
Add: src predicate
1 parent a60c238 commit 37ba30c

File tree

4 files changed

+80
-16
lines changed

4 files changed

+80
-16
lines changed

README.org

+2
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,7 @@ Arguments are listed next to predicate names, where applicable.
189189
+ =priority (&optional comparator-or-priority priority)= :: Return non-nil if current heading has a certain priority. ~COMPARATOR-OR-PRIORITY~ should be either a comparator function, like ~<=~, or a priority string, like "A" (in which case (~=~ will be the comparator). If ~COMPARATOR-OR-PRIORITY~ is a comparator, ~PRIORITY~ should be a priority string. If both arguments are nil, return non-nil if heading has any defined priority.
190190
+ =property (property &optional value)= :: Return non-nil if current entry has ~PROPERTY~ (a string), and optionally ~VALUE~ (a string). Note that property inheritance is currently /not/ enabled for this predicate. If you need to test with inheritance, you could use a custom predicate form, like ~(org-entry-get (point) "PROPERTY" 'inherit)~.
191191
+ =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.
192+
+ ~src (&optional lang regexps)~ :: Return non-nil if current entry contains an Org Babel source block. If ~LANG~ is non-nil, match blocks of that language. If ~REGEXPS~ is non-nil, require that block's contents match all regexps.
192193
+ =tags (&optional tags)= :: Return non-nil if current heading has one or more of ~TAGS~ (a list of strings). Tests both inherited and local tags.
193194
+ =tags-inherited (&optional tags)= :: Return non-nil if current heading's inherited tags include one or more of ~TAGS~ (a list of strings). If ~TAGS~ is nil, return non-nil if heading has any inherited tags.
194195
- Aliases: ~inherited-tags~, ~tags-i~, ~itags~.
@@ -379,6 +380,7 @@ Expands into a call to ~org-ql-select~ with the same arguments. For convenience
379380
+ Command ~org-ql-view-refresh~ can be called with a prefix argument to adjust search parameters.
380381
+ Function ~helm-org-ql-source~, which returns a Helm source that searches given buffers/files with ~helm-org-ql~. It can be used for custom Helm commands that search certain files.
381382
+ Command ~helm-org-ql-views~, which shows one of ~org-ql-views~ selected with Helm.
383+
+ Predicate ~src~, which matches Org Babel source blocks.
382384

383385
*Internal*
384386
+ Added generic node data cache to speed up recursive, tree-based queries.

org-ql.el

+54
Original file line numberDiff line numberDiff line change
@@ -747,6 +747,13 @@ replace the clause with a preamble."
747747
;; (setq org-ql-preamble (rx-to-string `(seq bol (0+ space) ":" (1+ (not (or space ":"))) ":"
748748
;; (1+ space) (minimal-match (1+ not-newline)) eol)))
749749
;; element)
750+
751+
;; Src blocks.
752+
(`(src ,lang . ,_)
753+
(setq org-ql-preamble (org-ql--format-src-block-regexp lang))
754+
;; Always check contents with predicate.
755+
element)
756+
750757
(`(scheduled . ,_)
751758
(setq org-ql-preamble org-scheduled-time-regexp)
752759
;; Return element, because the predicate still needs testing.
@@ -781,6 +788,29 @@ replace the clause with a preamble."
781788
(query (-flatten-n 1 query))))
782789
(list :query query :preamble org-ql-preamble :preamble-case-fold preamble-case-fold))))))
783790

791+
(defun org-ql--format-src-block-regexp (&optional lang)
792+
"Return regexp equivalent to `org-babel-src-block-regexp' with LANG filled in."
793+
;; I couldn't find a way to match block contents without the regexp
794+
;; also matching past the end of the block and into later blocks. Even
795+
;; using `minimal-match' in several different combinations didn't work.
796+
;; So matching contents will have to be done with the predicate.
797+
(rx-to-string `(seq bol (group (zero-or-more (any " ")))
798+
"#+begin_src"
799+
(one-or-more (any " "))
800+
,lang
801+
(zero-or-more (any " "))
802+
(group (or (seq (zero-or-more (not (any "\n\":")))
803+
"\""
804+
(zero-or-more (not (any "\n\"*")))
805+
"\""
806+
(zero-or-more (not (any "\n\":"))))
807+
(zero-or-more (not (any "\n\":")))))
808+
(group (zero-or-more (not (any "\n")))) "\n"
809+
(63 (group (*\? (not (any ""))) "\n"))
810+
(zero-or-more (any " "))
811+
"#+end_src")
812+
t))
813+
784814
(defmacro org-ql--from-to-on ()
785815
"For internal use.
786816
Expands into a form that processes arguments to timestamp-related
@@ -1102,6 +1132,30 @@ priority B)."
11021132
;; Check that PROPERTY has VALUE
11031133
(string-equal value (org-entry-get (point) property 'selective)))))))
11041134

1135+
(org-ql--defpred src (&optional lang &rest regexps)
1136+
"Return non-nil if current entry contains an Org source block matching all of REGEXPS.
1137+
If LANG is non-nil, the block must be in that language."
1138+
(catch 'return
1139+
(save-excursion
1140+
(save-match-data
1141+
(when (re-search-forward org-babel-src-block-regexp (org-entry-end-position) t)
1142+
(when lang
1143+
(unless (string= lang (match-string 2))
1144+
(throw 'return nil)))
1145+
(if regexps
1146+
(let ((contents-beg (progn
1147+
(goto-char (match-beginning 0))
1148+
(forward-line 1)
1149+
(point)))
1150+
(contents-end (progn
1151+
(goto-char (match-end 0))
1152+
(point-at-bol))))
1153+
(cl-loop for re in regexps
1154+
do (goto-char contents-beg)
1155+
always (re-search-forward re contents-end t)))
1156+
;; No regexps to check: return non-nil.
1157+
t))))))
1158+
11051159
;;;;;; Timestamps
11061160

11071161
;; TODO: Remove the _on vars from these arg lists. I think they're not

org-ql.info

+21-16
Original file line numberDiff line numberDiff line change
@@ -410,6 +410,10 @@ Arguments are listed next to predicate names, where applicable.
410410
Return non-nil if current entry matches all of ‘REGEXPS’ (regexp
411411
strings). Matches against entire entry, from beginning of its
412412
heading to the next heading.
413+
‘‘src (&optional lang regexps)’’
414+
Return non-nil if current entry contains an Org Babel source block.
415+
If ‘LANG’ is non-nil, match blocks of that language. If ‘REGEXPS’
416+
is non-nil, require that block’s contents match all regexps.
413417
‘tags (&optional tags)’
414418
Return non-nil if current heading has one or more of ‘TAGS’ (a list
415419
of strings). Tests both inherited and local tags.
@@ -700,6 +704,7 @@ File: README.info, Node: 04-pre, Next: 03, Up: Changelog
700704
for custom Helm commands that search certain files.
701705
• Command ‘helm-org-ql-views’, which shows one of ‘org-ql-views’
702706
selected with Helm.
707+
• Predicate ‘src’, which matches Org Babel source blocks.
703708

704709
*Internal*
705710
• Added generic node data cache to speed up recursive, tree-based
@@ -963,22 +968,22 @@ Node: org-ql-sparse-tree6921
963968
Node: Queries7721
964969
Node: Non-sexp query syntax8563
965970
Node: Predicates10063
966-
Node: Date/time predicates14920
967-
Node: Functions / Macros17555
968-
Node: Agenda-like views17742
969-
Node: Listing / acting-on results19147
970-
Node: Changelog23749
971-
Node: 04-pre24256
972-
Node: 0325132
973-
Node: 02328108
974-
Node: 02228334
975-
Node: 02128600
976-
Node: 0228797
977-
Node: 0132830
978-
Node: Notes32929
979-
Node: Comparison with Org Agenda searches33091
980-
Node: org-sidebar33962
981-
Node: License34241
971+
Node: Date/time predicates15182
972+
Node: Functions / Macros17817
973+
Node: Agenda-like views18004
974+
Node: Listing / acting-on results19409
975+
Node: Changelog24011
976+
Node: 04-pre24518
977+
Node: 0325461
978+
Node: 02328437
979+
Node: 02228663
980+
Node: 02128929
981+
Node: 0229126
982+
Node: 0133159
983+
Node: Notes33258
984+
Node: Comparison with Org Agenda searches33420
985+
Node: org-sidebar34291
986+
Node: License34570
982987

983988
End Tag Table
984989

tests/test-org-ql.el

+3
Original file line numberDiff line numberDiff line change
@@ -656,6 +656,9 @@ RESULTS should be a list of strings as returned by
656656
(org-ql-expect ((scheduled :to today))
657657
'("Skype with president of Antarctica" "Practice leaping tall buildings in a single bound" "Order a pizza" "Get haircut" "Fix flux capacitor" "Shop for groceries" "Rewrite Emacs in Common Lisp")))))
658658

659+
;; TODO: Test (src) predicate. That will require modifying test data, which will be a
660+
;; significant hassle. Manual testing shows that the predicate appears to work properly.
661+
659662
(describe "(todo)"
660663

661664
(org-ql-it "without arguments"

0 commit comments

Comments
 (0)