From 949a06c3ab50482b749fd2d4350837a197660d96 Mon Sep 17 00:00:00 2001 From: Adam Porter Date: Wed, 9 Oct 2019 19:05:11 -0500 Subject: [PATCH] Add: Negation in plain queries --- README.org | 2 ++ org-ql.el | 12 +++++++++--- org-ql.info | 39 +++++++++++++++++++++------------------ tests/test-org-ql.el | 11 +++++++++++ 4 files changed, 43 insertions(+), 21 deletions(-) diff --git a/README.org b/README.org index c57321b..7f4022d 100644 --- a/README.org +++ b/README.org @@ -164,6 +164,7 @@ The command =org-ql-search= also accepts, and the command =helm-org-ql= only acc | ~(heading "quoted phrase" "word")~ | ~heading:"quoted phrase",word~ | | ~(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~ | 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. @@ -375,6 +376,7 @@ Expands into a call to ~org-ql-select~ with the same arguments. For convenience ** 0.4-pre *Added* ++ 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=). + Info manual. + Command ~org-ql-search~ can search files in ~org-directory~; customization options are available in the ~org-ql-search~ group. diff --git a/org-ql.el b/org-ql.el index b5d898b..7c63954 100644 --- a/org-ql.el +++ b/org-ql.el @@ -1487,10 +1487,15 @@ Builds the PEG expression using predicates defined in Multiple predicates are combined with BOOLEAN." (unless (s-blank-str? input) (let* ((query (peg-parse-string - ((query (+ (or (and predicate-with-args `(pred args -- (cons (intern pred) args))) - (and predicate-without-args `(pred -- (list (intern pred)))) - (and plain-string `(s -- (list 'regexp s)))) + ((query (+ term (opt (+ (syntax-class whitespace) (any))))) + (term (or (and negation (list positive-term) + ;; This is a bit confusing, but it seems to work. There's probably a better way. + `(pred -- (list 'not (car pred)))) + positive-term)) + (positive-term (or (and predicate-with-args `(pred args -- (cons (intern pred) args))) + (and predicate-without-args `(pred -- (list (intern pred)))) + (and plain-string `(s -- (list 'regexp s))))) (plain-string (substring (+ (not (syntax-class whitespace)) (any)))) (predicate-with-args (substring predicate) ":" args) (predicate-without-args (substring predicate) ":") @@ -1500,6 +1505,7 @@ Multiple predicates are combined with BOOLEAN." (keyword (substring (+ (not (or separator "=" "\"" (syntax-class whitespace))) (any)))) (quoted-arg "\"" (substring (+ (not (or separator "\"")) (any))) "\"") (unquoted-arg (substring (+ (not (or separator "\"" (syntax-class whitespace))) (any)))) + (negation "!") (separator "," )) input 'noerror))) ;; Discard the t that `peg-parse-string' always returns as the first diff --git a/org-ql.info b/org-ql.info index 4c5c984..7c707b2 100644 --- a/org-ql.info +++ b/org-ql.info @@ -339,6 +339,7 @@ Sexp syntax Non-sexp syntax ‘(heading "quoted phrase" "word")’ ‘heading:"quoted phrase",word’ ‘(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’ Note that the priority predicate does not support comparators in the @@ -695,6 +696,8 @@ File: README.info, Node: 04-pre, Next: 031, Up: Changelog =========== *Added* + • 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). • Info manual. @@ -980,24 +983,24 @@ Node: org-ql-view-recent-items6452 Node: org-ql-sparse-tree6936 Node: Queries7736 Node: Non-sexp query syntax8578 -Node: Predicates10198 -Node: Date/time predicates15312 -Node: Functions / Macros17947 -Node: Agenda-like views18134 -Node: Listing / acting-on results19539 -Node: Changelog24141 -Node: 04-pre24663 -Node: 03125607 -Node: 0325805 -Node: 02328778 -Node: 02229004 -Node: 02129270 -Node: 0229467 -Node: 0133500 -Node: Notes33599 -Node: Comparison with Org Agenda searches33761 -Node: org-sidebar34632 -Node: License34911 +Node: Predicates10277 +Node: Date/time predicates15391 +Node: Functions / Macros18026 +Node: Agenda-like views18213 +Node: Listing / acting-on results19618 +Node: Changelog24220 +Node: 04-pre24742 +Node: 03125830 +Node: 0326028 +Node: 02329001 +Node: 02229227 +Node: 02129493 +Node: 0229690 +Node: 0133723 +Node: Notes33822 +Node: Comparison with Org Agenda searches33984 +Node: org-sidebar34855 +Node: License35134  End Tag Table diff --git a/tests/test-org-ql.el b/tests/test-org-ql.el index 84ebadc..24a08be 100644 --- a/tests/test-org-ql.el +++ b/tests/test-org-ql.el @@ -231,6 +231,17 @@ RESULTS should be a list of strings as returned by ;; TODO: Other predicates. + (it "Negated terms" + (expect (org-ql--plain-query "todo: !todo:CHECK,SOMEDAY") + :to-equal '(and (todo) (not (todo "CHECK" "SOMEDAY")))) + (expect (org-ql--plain-query "!todo:CHECK,SOMEDAY todo:") + :to-equal '(and (not (todo "CHECK" "SOMEDAY")) (todo))) + (expect (org-ql--plain-query "tags:universe !moon") + :to-equal '(and (tags "universe") (not (regexp "moon")))) + (expect (org-ql--plain-query "!moon tags:universe") + :to-equal '(and (not (regexp "moon")) (tags "universe"))) + (expect (org-ql--plain-query "mars !ts:on=today") + :to-equal '(and (regexp "mars") (not (ts :on "today"))))) (it "Regexp predicates" (expect (org-ql--plain-query "scheduled") ;; No colon after keyword, so not a predicate query.