Skip to content

Commit

Permalink
Fix: (link) Allow brackets in description
Browse files Browse the repository at this point in the history
Org 9.3 stopped replacing brackets in link descriptions with braces
and started escaping them instead.

Fixes alphapapa#283.  Thanks to Daniel Borchmann (@exot) for reporting.
  • Loading branch information
alphapapa committed Mar 10, 2023
1 parent 3c73888 commit 9e14f9e
Show file tree
Hide file tree
Showing 5 changed files with 115 additions and 68 deletions.
1 change: 1 addition & 0 deletions README.org
Original file line number Diff line number Diff line change
Expand Up @@ -560,6 +560,7 @@ Simple links may also be written manually in either sexp or non-sexp form, like:
+ Command ~org-ql-sparse-tree~ accepts both string and sexp queries. (Thanks to [[https://github.com/akirak][Akira Komamura]].)

*Fixed*
+ Predicate ~link~ matches links whose descriptions contain escaped brackets (changed in Org 9.3). (Thanks to [[https://github.com/exot][Daniel Borchmann]] for reporting.)
+ Predicate ~src~'s matching of begin/end block lines, normalization of arguments, and handling in non-sexp queries. (Thanks to [[https://github.com/akirak][Akira Komamura]] for reporting.)
+ Predicate ~src~'s behavior with various arguments.
+ Various compilation warnings.
Expand Down
77 changes: 39 additions & 38 deletions org-ql.el
Original file line number Diff line number Diff line change
Expand Up @@ -794,38 +794,43 @@ DESCRIPTION-OR-TARGET, match it in either description or target.
If DESCRIPTION, match it in the description. If TARGET, match it
in the target. If both DESCRIPTION and TARGET, match both,
respectively."
(cl-labels
((no-desc
(match) (rx-to-string `(seq (or bol (1+ blank))
"[[" (0+ (not (any "]"))) (regexp ,match) (0+ (not (any "]")))
"]]")))
(match-both
(description target)
(rx-to-string `(seq (or bol (1+ blank))
"[[" (0+ (not (any "]"))) (regexp ,target) (0+ (not (any "]")))
"][" (0+ (not (any "]"))) (regexp ,description) (0+ (not (any "]")))
"]]")))
;; Note that these actually allow empty descriptions
;; or targets, depending on what they are matching.
(match-desc
(match) (rx-to-string `(seq (or bol (1+ blank))
"[[" (0+ (not (any "]")))
"][" (0+ (not (any "]"))) (regexp ,match) (0+ (not (any "]")))
"]]")))
(match-target
(match) (rx-to-string `(seq (or bol (1+ blank))
"[[" (0+ (not (any "]"))) (regexp ,match) (0+ (not (any "]")))
"][" (0+ (not (any "]")))
"]]"))))
(cond (description-or-target
(rx-to-string `(or (regexp ,(no-desc description-or-target))
(regexp ,(match-desc description-or-target))
(regexp ,(match-target description-or-target)))))
((and description target)
(match-both description target))
(description (match-desc description))
(target (rx-to-string `(or (regexp ,(no-desc target))
(regexp ,(match-target target))))))))
;; This `rx' part is borrowed from `org-make-link-regexps'. It matches the interior of an
;; Org link target (i.e. the parts between the brackets, including any escaped brackets).
(let ((link-target-part '(0+ (or (not (any "[]\\"))
(and "\\" (0+ "\\\\") (any "[]"))
(and (1+ "\\") (not (any "[]")))))))
(cl-labels
((no-desc
(match) (rx-to-string `(seq (or bol (1+ blank))
"[[" ,link-target-part (regexp ,match) ,link-target-part
"]]")))
(match-both
(description target)
(rx-to-string `(seq (or bol (1+ blank))
"[[" ,link-target-part (regexp ,target) ,link-target-part
"][" (*? anything) (regexp ,description) (*? anything)
"]]")))
;; Note that these actually allow empty descriptions
;; or targets, depending on what they are matching.
(match-desc
(match) (rx-to-string `(seq (or bol (1+ blank))
"[[" ,link-target-part
"][" (*? anything) (regexp ,match) (*? anything)
"]]")))
(match-target
(match) (rx-to-string `(seq (or bol (1+ blank))
"[[" ,link-target-part (regexp ,match) ,link-target-part
"][" (*? anything)
"]]"))))
(cond (description-or-target
(rx-to-string `(or (regexp ,(no-desc description-or-target))
(regexp ,(match-desc description-or-target))
(regexp ,(match-target description-or-target)))))
((and description target)
(match-both description target))
(description (match-desc description))
(target (rx-to-string `(or (regexp ,(no-desc target))
(regexp ,(match-target target)))))))))

(defun org-ql--format-src-block-regexp (&optional lang)
"Return regexp equivalent to `org-babel-src-block-regexp' with LANG filled in."
Expand Down Expand Up @@ -1563,12 +1568,8 @@ any link is found."
;; enabled nearly all of the time, in which case this function won't be called anyway, it's
;; probably not worth rewriting code all over the place to fix this.
:preambles ((`(,predicate-names)
(list :regexp ;; Match a link with a target and optionally a description.
(rx (or bol (1+ blank))
"[[" (1+ (not (any "]"))) "]"
(optional (seq "[" (0+ (not (any "]"))) "]"))
"]"
(or eol blank))))
;; Match a link with a target and optionally a description.
(list :regexp (org-ql--link-regexp :target ".*")))
(`(,predicate-names ,(and description-or-target
(guard (not (keywordp description-or-target))))
. ,plist)
Expand Down
61 changes: 32 additions & 29 deletions org-ql.info
Original file line number Diff line number Diff line change
Expand Up @@ -1055,6 +1055,9 @@ File: README.info, Node: 07-pre, Next: 063, Up: Changelog
(Thanks to Akira Komamura (https://github.com/akirak).)

*Fixed*
• Predicate ‘link’ matches links whose descriptions contain escaped
brackets (changed in Org 9.3). (Thanks to Daniel Borchmann
(https://github.com/exot) for reporting.)
• Predicate ‘src’’s matching of begin/end block lines, normalization
of arguments, and handling in non-sexp queries. (Thanks to Akira
Komamura (https://github.com/akirak) for reporting.)
Expand Down Expand Up @@ -1735,35 +1738,35 @@ Node: Links37255
Node: Tips37942
Node: Changelog38266
Node: 07-pre39049
Node: 06341784
Node: 06242319
Node: 06142624
Node: 0643192
Node: 05246246
Node: 05146546
Node: 0546969
Node: 04948498
Node: 04848778
Node: 04749127
Node: 04649536
Node: 04549944
Node: 04450305
Node: 04350664
Node: 04250867
Node: 04151028
Node: 0451275
Node: 03255376
Node: 03155779
Node: 0355976
Node: 02359276
Node: 02259510
Node: 02159790
Node: 0259995
Node: 0164073
Node: Notes64174
Node: Comparison with Org Agenda searches64336
Node: org-sidebar65225
Node: License65504
Node: 06341973
Node: 06242508
Node: 06142813
Node: 0643381
Node: 05246435
Node: 05146735
Node: 0547158
Node: 04948687
Node: 04848967
Node: 04749316
Node: 04649725
Node: 04550133
Node: 04450494
Node: 04350853
Node: 04251056
Node: 04151217
Node: 0451464
Node: 03255565
Node: 03155968
Node: 0356165
Node: 02359465
Node: 02259699
Node: 02159979
Node: 0260184
Node: 0164262
Node: Notes64363
Node: Comparison with Org Agenda searches64525
Node: org-sidebar65414
Node: License65693

End Tag Table

Expand Down
10 changes: 10 additions & 0 deletions tests/data-links.org
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
* Alpha

Let us link to: [[id:74d357ac-fb9c-40d1-a63f-eca8a227321d][Bravo [a phrase in brackets]​]].

* Bravo [a phrase in brackets]
:PROPERTIES:
:ID: 74d357ac-fb9c-40d1-a63f-eca8a227321d
:END:

* Charlie
34 changes: 33 additions & 1 deletion tests/test-org-ql.el
Original file line number Diff line number Diff line change
Expand Up @@ -1141,7 +1141,39 @@ with keyword arg NOW in PLIST."
'("/r/emacs")))
(org-ql-it "with :description and :target regexp"
(org-ql-expect ('(link :description "em.cs" :target "em.cs" :regexp-p t))
'("/r/emacs"))))
'("/r/emacs")))

(describe "matches links whose descriptions contain brackets"
(before-each
(setq org-ql-test-buffer (org-ql-test-data-buffer "data-links.org")))

(org-ql-it "without arguments"
(org-ql-expect ('(link))
'("Alpha")))
(org-ql-it "with description-or-target"
(org-ql-expect ('(link "phrase"))
'("Alpha")))
(org-ql-it "with :description"
(org-ql-expect ('(link :description "phrase"))
'("Alpha")))
(org-ql-it "with :target"
(org-ql-expect ('(link :target "id:"))
'("Alpha")))
(org-ql-it "with :description and :target"
(org-ql-expect ('(link :description "phrase" :target "id"))
'("Alpha")))
(org-ql-it "with description-or-target regexp"
(org-ql-expect ('(link "id:.*" :regexp-p t))
'("Alpha")))
(org-ql-it "with :description regexp"
(org-ql-expect ('(link :description "phr.se" :regexp-p t))
'("Alpha")))
(org-ql-it "with :target regexp"
(org-ql-expect ('(link :target "id:.*" :regexp-p t))
'("Alpha")))
(org-ql-it "with :description and :target regexp"
(org-ql-expect ('(link :description "phr.se" :target "id:.*" :regexp-p t))
'("Alpha")))))

(describe "(outline-path)"
(org-ql-it "with one argument"
Expand Down

0 comments on commit 9e14f9e

Please sign in to comment.