diff --git a/contrib/lisp/org-link-edit.el b/contrib/lisp/org-link-edit.el index 9059df06a..b046a77ea 100644 --- a/contrib/lisp/org-link-edit.el +++ b/contrib/lisp/org-link-edit.el @@ -95,9 +95,7 @@ The list includes (match-end 0) (save-match-data (org-link-unescape (match-string-no-properties 1))) - (or (and (match-end 3) - (match-string-no-properties 3)) - ""))) + (or (match-string-no-properties 2) ""))) ((looking-at org-plain-link-re) (list (match-beginning 0) (match-end 0) diff --git a/lisp/ob-tangle.el b/lisp/ob-tangle.el index dcdbc681b..f9f785910 100644 --- a/lisp/ob-tangle.el +++ b/lisp/ob-tangle.el @@ -30,6 +30,7 @@ (require 'cl-lib) (require 'org-src) (require 'org-macs) +(require 'ol) (declare-function make-directory "files" (dir &optional parents)) (declare-function org-at-heading-p "org" (&optional ignored)) @@ -40,14 +41,8 @@ (declare-function org-element-type "org-element" (element)) (declare-function org-heading-components "org" ()) (declare-function org-in-commented-heading-p "org" (&optional no-inheritance)) -(declare-function org-link-escape "org" (text &optional table merge)) -(declare-function org-link-open-from-string "ol" (s &optional arg)) -(declare-function org-link-trim-scheme "ol" (uri)) -(declare-function org-store-link "org" (arg &optional interactive?)) (declare-function outline-previous-heading "outline" ()) -(defvar org-link-types-re) - (defcustom org-babel-tangle-lang-exts '(("emacs-lisp" . "el") ("elisp" . "el")) @@ -330,8 +325,6 @@ references." (delete-region (save-excursion (beginning-of-line 1) (point)) (save-excursion (end-of-line 1) (forward-char 1) (point))))) -(defvar org-stored-links) -(defvar org-link-bracket-re) (defun org-babel-spec-to-string (spec) "Insert SPEC into the current file. @@ -506,10 +499,7 @@ non-nil, return the full association list to be used by `(("start-line" . ,(number-to-string (org-babel-where-is-src-block-head))) ("file" . ,(buffer-file-name)) - ("link" . ,(org-link-escape - (progn - (call-interactively #'org-store-link) - (org-no-properties (car (pop org-stored-links)))))) + ("link" . ,(org-no-properties (org-store-link nil))) ("source-name" . ,(nth 4 (or info (org-babel-get-src-block-info 'light))))))) (list (org-fill-template org-babel-tangle-comment-format-beg link-data) @@ -527,7 +517,7 @@ which enable the original code blocks to be found." (let ((counter 0) new-body end) (while (re-search-forward org-link-bracket-re nil t) (when (re-search-forward - (concat " " (regexp-quote (match-string 3)) " ends here")) + (concat " " (regexp-quote (match-string 2)) " ends here")) (setq end (match-end 0)) (forward-line -1) (save-excursion @@ -549,7 +539,7 @@ which enable the original code blocks to be found." (and (setq start (line-beginning-position)) (setq body-start (line-beginning-position 2)) (setq link (match-string 0)) - (setq block-name (match-string 3)) + (setq block-name (match-string 2)) (save-excursion (save-match-data (re-search-forward diff --git a/lisp/ol.el b/lisp/ol.el index 46231530e..6f337718e 100644 --- a/lisp/ol.el +++ b/lisp/ol.el @@ -375,13 +375,6 @@ single keystroke rather than having to type \"yes\"." :tag "Org Store Link" :group 'org-link) -(defcustom org-link-url-hexify t - "When non-nil, hexify URL when creating a link." - :type 'boolean - :version "24.3" - :group 'org-link-store - :safe #'booleanp) - (defcustom org-link-context-for-files t "Non-nil means file links from `org-store-link' contain context. \\ @@ -451,12 +444,6 @@ links more efficient." ;;; Public variables -(defconst org-link-escape-chars - ;;%20 %5B %5D %25 - '(?\s ?\[ ?\] ?%) - "List of characters that should be escaped in a link when stored to Org. -This is the list that is used for internal purposes.") - (defconst org-target-regexp (let ((border "[^<>\n\r \t]")) (format "<<\\(%s\\|%s[^<>\n\r]*%s\\)>>" border border border)) @@ -597,7 +584,7 @@ either a link description or nil." (concat (format "%-45s" (substring desc 0 (min (length desc) 40))) "<" (car link) ">"))) -(defun org-link--unescape-compound (hex) +(defun org-link--decode-compound (hex) "Unhexify Unicode hex-chars HEX. E.g. \"%C3%B6\" is the German o-Umlaut. Note: this function also decodes single byte encodings like \"%E1\" (a-acute) if not @@ -628,14 +615,15 @@ followed by another \"%[A-F0-9]{2}\" group." (setq ret (concat ret (char-to-string sum))) (setq sum 0)) ((not bytes) ; single byte(s) - (setq ret (org-link--unescape-single-byte-sequence hex)))))) + (setq ret (org-link--decode-single-byte-sequence hex)))))) ret))) -(defun org-link--unescape-single-byte-sequence (hex) +(defun org-link--decode-single-byte-sequence (hex) "Unhexify hex-encoded single byte character sequence HEX." (mapconcat (lambda (byte) (char-to-string (string-to-number byte 16))) - (cdr (split-string hex "%")) "")) + (cdr (split-string hex "%")) + "")) (defun org-link--fontify-links-to-this-file () "Fontify links to the current file in `org-stored-links'." @@ -750,7 +738,18 @@ This should be called after the variable `org-link-parameters' has changed." "\\([^][ \t\n()<>]+\\(?:([[:word:]0-9_]+)\\|\\([^[:punct:] \t\n]\\|/\\)\\)\\)") ;; "\\([^]\t\n\r<>() ]+[^]\t\n\r<>,.;() ]\\)") org-link-bracket-re - "\\[\\[\\([^][]+\\)\\]\\(\\[\\([^][]+\\)\\]\\)?\\]" + (rx (seq "[[" + ;; URI part: match group 1. + (group + (*? anything) + ;; Allow an even number of backslashes right + ;; before the closing bracket. + (not (any "\\")) + (zero-or-more "\\\\")) + "]" + ;; Description (optional): match group 2. + (opt "[" (group (+? anything)) "]") + "]")) org-link-any-re (concat "\\(" org-link-bracket-re "\\)\\|\\(" org-link-angle-re "\\)\\|\\(" @@ -841,55 +840,71 @@ and dates." (setq org-store-link-plist (plist-put org-store-link-plist key value))))) -(defun org-link-escape (text &optional table merge) - "Return percent escaped representation of TEXT. -TEXT is a string with the text to escape. -Optional argument TABLE is a list with characters that should be -escaped. When nil, `org-link-escape-chars' is used. -If optional argument MERGE is set, merge TABLE into -`org-link-escape-chars'." - (let ((characters-to-encode - (cond ((null table) org-link-escape-chars) - (merge (append org-link-escape-chars table)) - (t table)))) - (mapconcat - (lambda (c) - (if (or (memq c characters-to-encode) - (and org-link-url-hexify (or (< c 32) (> c 126)))) - (mapconcat (lambda (e) (format "%%%.2X" e)) - (or (encode-coding-char c 'utf-8) - (error "Unable to percent escape character: %c" c)) - "") - (char-to-string c))) - text ""))) +(defun org-link-encode (text table) + "Return percent escaped representation of string TEXT. +TEXT is a string with the text to escape. TABLE is a list of +characters that should be escaped." + (mapconcat + (lambda (c) + (if (memq c table) + (mapconcat (lambda (e) (format "%%%.2X" e)) + (or (encode-coding-char c 'utf-8) + (error "Unable to percent escape character: %c" c)) + "") + (char-to-string c))) + text "")) -(defun org-link-unescape (str) - "Unhex hexified Unicode parts in string STR. -E.g. \"%C3%B6\" becomes the german o-Umlaut. This is the -reciprocal of `org-link-escape', which see." - (if (org-string-nw-p str) - (replace-regexp-in-string - "\\(%[0-9A-Za-z]\\{2\\}\\)+" #'org-link--unescape-compound str t t) - str)) +(defun org-link-decode (s) + "Decode percent-encoded parts in string S. +E.g. \"%C3%B6\" becomes the german o-Umlaut." + (replace-regexp-in-string "\\(%[0-9A-Za-z]\\{2\\}\\)+" + #'org-link--decode-compound s t t)) + +(defun org-link-escape (link) + "Backslash-escape sensitive characters in string LINK." + ;; Escape closing square brackets followed by another square bracket + ;; or at the end of the link. Also escape final backslashes so that + ;; we do not escape inadvertently URI's closing bracket. + (with-temp-buffer + (insert link) + (insert (make-string (- (skip-chars-backward "\\\\")) + ?\\)) + (while (search-backward "\]" nil t) + (when (looking-at-p "\\]\\(?:[][]\\|\\'\\)") + (insert (make-string (1+ (- (skip-chars-backward "\\\\"))) + ?\\)))) + (buffer-string))) + +(defun org-link-unescape (link) + "Remove escaping backslash characters from string LINK." + (with-temp-buffer + (save-excursion (insert link)) + (while (re-search-forward "\\(\\\\+\\)\\]\\(?:[][]\\|\\'\\)" nil t) + (replace-match (make-string (/ (- (match-end 1) (match-beginning 1)) 2) + ?\\) + nil t nil 1)) + (goto-char (point-max)) + (delete-char (/ (- (skip-chars-backward "\\\\")) 2)) + (buffer-string))) (defun org-link-make-string (link &optional description) - "Make a bracket link, consisting of LINK and DESCRIPTION." + "Make a bracket link, consisting of LINK and DESCRIPTION. +LINK is escaped with backslashes for inclusion in buffer." (unless (org-string-nw-p link) (error "Empty link")) - (let ((uri (cond ((string-match org-link-types-re link) - (concat (match-string 1 link) - (org-link-escape (substring link (match-end 1))))) - ((or (file-name-absolute-p link) - (string-match-p "\\`\\.\\.?/" link)) - (org-link-escape link)) - ;; For readability, do not encode space characters - ;; in fuzzy links. - (t (org-link-escape link (remq ?\s org-link-escape-chars))))) - (description - (and (org-string-nw-p description) - ;; Remove brackets from description, as they are fatal. - (replace-regexp-in-string - "[][]" (lambda (m) (if (equal "[" m) "{" "}")) - (org-trim description))))) + (let* ((uri (org-link-escape link)) + (zero-width-space (string ?\x200B)) + (description + (and (org-string-nw-p description) + ;; Description cannot contain two consecutive square + ;; brackets, or end with a square bracket. To prevent + ;; this, insert a zero width space character between + ;; the brackets, or at the end of the description. + (replace-regexp-in-string + "\\(]\\)\\(]\\)" + (concat "\\1" zero-width-space "\\2") + (replace-regexp-in-string "]\\'" + (concat "\\&" zero-width-space) + (org-trim description)))))) (format "[[%s]%s]" uri (if description (format "[%s]" description) "")))) @@ -1207,7 +1222,7 @@ If there is no description, use the link target." (save-match-data (replace-regexp-in-string org-link-bracket-re - (lambda (m) (or (match-string 3 m) (match-string 1 m))) + (lambda (m) (or (match-string 2 m) (match-string 1 m))) s nil t))) (defun org-link-add-angle-brackets (s) @@ -1662,7 +1677,7 @@ don't allow to edit the default description." ((org-in-regexp org-link-bracket-re 1) ;; We do have a link at point, and we are going to edit it. (setq remove (list (match-beginning 0) (match-end 0))) - (setq desc (when (match-end 3) (match-string-no-properties 3))) + (setq desc (when (match-end 2) (match-string-no-properties 2))) (setq link (read-string "Link: " (org-link-unescape (match-string-no-properties 1))))) diff --git a/lisp/org-agenda.el b/lisp/org-agenda.el index d362ae270..90b880204 100644 --- a/lisp/org-agenda.el +++ b/lisp/org-agenda.el @@ -5201,7 +5201,7 @@ function from a program - use `org-agenda-get-day-entries' instead." (when results (setq results (mapcar (lambda (i) (replace-regexp-in-string - org-link-bracket-re "\\3" i)) results)) + org-link-bracket-re "\\2" i)) results)) (concat (org-agenda-finalize-entries results) "\n")))) ;;; Agenda entry finders @@ -6571,9 +6571,7 @@ Any match of REMOVE-RE will be removed from TXT." level (or level "")) (if (string-match org-link-bracket-re category) (progn - (setq l (if (match-end 3) - (- (match-end 3) (match-beginning 3)) - (- (match-end 1) (match-beginning 1)))) + (setq l (string-width (or (match-string 2) (match-string 1)))) (when (< l (or org-prefix-category-length 0)) (setq category (copy-sequence category)) (org-add-props category nil @@ -10238,7 +10236,7 @@ to override `appt-message-warning-time'." (lambda (x) (let* ((evt (org-trim (replace-regexp-in-string - org-link-bracket-re "\\3" + org-link-bracket-re "\\2" (or (get-text-property 1 'txt x) "")))) (cat (get-text-property (1- (length x)) 'org-category x)) (tod (get-text-property 1 'time-of-day x)) diff --git a/lisp/org-clock.el b/lisp/org-clock.el index 71f512245..317256a05 100644 --- a/lisp/org-clock.el +++ b/lisp/org-clock.el @@ -2621,10 +2621,10 @@ from the dynamic block definition." (if (and (string-match (format "\\`%s\\'" org-link-bracket-re) headline) - (match-end 3)) + (match-end 2)) (format "[[%s][%s]]" (match-string 1 headline) - (org-shorten-string (match-string 3 headline) + (org-shorten-string (match-string 2 headline) narrow)) (org-shorten-string headline narrow)))) (cl-flet ((format-field (f) (format (cond ((not emph) "%s |") diff --git a/lisp/org-compat.el b/lisp/org-compat.el index 65980ad9f..44bdbb380 100644 --- a/lisp/org-compat.el +++ b/lisp/org-compat.el @@ -462,9 +462,6 @@ use of this function is for the stuck project list." (define-obsolete-variable-alias 'org-descriptive-links 'org-link-descriptive "Org 9.3") -(define-obsolete-variable-alias 'org-url-hexify-p - 'org-link-url-hexify "Org 9.3") - (define-obsolete-variable-alias 'org-context-in-file-links 'org-link-context-for-files "Org 9.3") diff --git a/lisp/org-element.el b/lisp/org-element.el index 8d4e49d78..d9ee8fdfa 100644 --- a/lisp/org-element.el +++ b/lisp/org-element.el @@ -3141,8 +3141,8 @@ Assume point is at the beginning of the link." ;; Type 2: Standard link, i.e. [[https://orgmode.org][homepage]] ((looking-at org-link-bracket-re) (setq format 'bracket) - (setq contents-begin (match-beginning 3)) - (setq contents-end (match-end 3)) + (setq contents-begin (match-beginning 2)) + (setq contents-end (match-end 2)) (setq link-end (match-end 0)) ;; RAW-LINK is the original link. Decode any encoding. ;; Expand any abbreviation in it. diff --git a/lisp/org-mobile.el b/lisp/org-mobile.el index 1e6d35740..854f7ce20 100644 --- a/lisp/org-mobile.el +++ b/lisp/org-mobile.el @@ -31,14 +31,10 @@ ;; iPhone and Android - any external viewer/flagging/editing ;; application that uses the same conventions could be used. +(require 'cl-lib) (require 'org) (require 'org-agenda) -(require 'cl-lib) - -(declare-function org-link-escape "ol" (text &optional table merge)) -(declare-function org-link-unescape "ol" (str)) - -(defvar org-agenda-keep-restricted-file-list) +(require 'ol) ;;; Code: @@ -673,8 +669,7 @@ The table of checksums is written to the file mobile-checksums." (org-mobile-escape-olp (nth 4 (org-heading-components)))))) (defun org-mobile-escape-olp (s) - (let ((table '(?: ?/))) - (org-link-escape s table))) + (org-link-encode s '(?: ?/))) (defun org-mobile-create-sumo-agenda () "Create a file that contains all custom agenda views." @@ -968,7 +963,7 @@ is currently a noop.") (if (not (string-match "\\`olp:\\(.*?\\)$" link)) nil (let ((file (match-string 1 link))) - (setq file (org-link-unescape file)) + (setq file (org-link-decode file)) (setq file (expand-file-name file org-directory)) (save-excursion (find-file file) @@ -978,9 +973,9 @@ is currently a noop.") (point-marker)))) (let ((file (match-string 1 link)) (path (match-string 2 link))) - (setq file (org-link-unescape file)) + (setq file (org-link-decode file)) (setq file (expand-file-name file org-directory)) - (setq path (mapcar 'org-link-unescape + (setq path (mapcar #'org-link-decode (org-split-string path "/"))) (org-find-olp (cons file path)))))) diff --git a/lisp/org-protocol.el b/lisp/org-protocol.el index ea2873e6d..3f48be187 100644 --- a/lisp/org-protocol.el +++ b/lisp/org-protocol.el @@ -301,7 +301,7 @@ results of that splitting are returned as a list." (split-parts (split-string data sep))) (cond ((not unhexify) split-parts) ((fboundp unhexify) (mapcar unhexify split-parts)) - (t (mapcar #'org-link-unescape split-parts))))) + (t (mapcar #'org-link-decode split-parts))))) (defun org-protocol-flatten-greedy (param-list &optional strip-path replacement) "Transform PARAM-LIST into a flat list for greedy handlers. @@ -382,7 +382,7 @@ If INFO is already a property list, return it unchanged." (while data (setq result (append result - (list (pop data) (org-link-unescape (pop data)))))) + (list (pop data) (org-link-decode (pop data)))))) result) (let ((data (org-protocol-split-data info t org-protocol-data-separator))) (if default-order diff --git a/lisp/org.el b/lisp/org.el index 28c0ce414..0f0ceceec 100644 --- a/lisp/org.el +++ b/lisp/org.el @@ -9389,7 +9389,7 @@ prefix argument (`C-u C-u C-u C-c C-w')." (setq heading-text (replace-regexp-in-string org-link-bracket-re - "\\3" + "\\2" (or (nth 4 (org-heading-components)) "")))) (org-refile-get-location diff --git a/testing/README b/testing/README index add3c4fb0..2469d1458 100644 --- a/testing/README +++ b/testing/README @@ -120,15 +120,15 @@ load and run the test suite with the following commands. To run one test: Use this as a demo example of a failing test #+BEGIN_SRC emacs-lisp - (ert-deftest test-org/org-link-escape-ascii-character-demo-of-fail () + (ert-deftest test-org/org-link-encode-ascii-character-demo-of-fail () (should (string= "%5B" ; Expecting %5B is correct. - (org-link-escape "["))) + (org-link-encode "["))) (should (string= "%5C" ; Expecting %5C is wrong, %5D correct. - (org-link-escape "]")))) + (org-link-encode "]")))) #+END_SRC or evaluate the ~ert-deftest form~ of the test you want to run. Then ~M-x ert RET - test-org/org-link-escape-ascii-character-demo-of-fail RET~. When + test-org/org-link-encode-ascii-character-demo-of-fail RET~. When not visible yet switch to the ERT results buffer named ~*ert*~. When a test failed the ERT results buffer shows the details of the first ~should~ that failed. See ~(info "(ert)Running Tests diff --git a/testing/lisp/test-ol.el b/testing/lisp/test-ol.el index e6c09e633..995da8543 100644 --- a/testing/lisp/test-ol.el +++ b/testing/lisp/test-ol.el @@ -20,80 +20,106 @@ ;;; Code: -;;; (Un)Escape links +;;; Decode and Encode Links -(ert-deftest test-ol/escape-ascii-character () - "Escape an ascii character." - (should - (string= - "%5B" - (org-link-escape "[")))) +(ert-deftest test-ol/encode () + "Test `org-link-encode' specifications." + ;; Regural test. + (should (string= "Foo%3A%42ar" (org-link-encode "Foo:Bar" '(?\: ?\B)))) + ;; Encode an ASCII character. + (should (string= "%5B" (org-link-encode "[" '(?\[)))) + ;; Encode an ASCII control character. + (should (string= "%09" (org-link-encode "\t" '(9)))) + ;; Encode a Unicode multibyte character. + (should (string= "%E2%82%AC" (org-link-encode "€" '(?\€))))) -(ert-deftest test-ol/escape-ascii-ctrl-character () - "Escape an ascii control character." - (should - (string= - "%09" - (org-link-escape "\t")))) +(ert-deftest test-ol/decode () + "Test `org-link-decode' specifications." + ;; Decode an ASCII character. + (should (string= "[" (org-link-decode "%5B"))) + ;; Decode an ASCII control character. + (should (string= "\n" (org-link-decode "%0A"))) + ;; Decode a Unicode multibyte character. + (should (string= "€" (org-link-decode "%E2%82%AC")))) -(ert-deftest test-ol/escape-multibyte-character () - "Escape an unicode multibyte character." +(ert-deftest test-ol/encode-url-with-escaped-char () + "Encode and decode a URL that includes an encoded char." (should - (string= - "%E2%82%AC" - (org-link-escape "€")))) + (string= "http://some.host.com/form?&id=blah%2Bblah25" + (org-link-decode + (org-link-encode "http://some.host.com/form?&id=blah%2Bblah25" + '(?\s ?\[ ?\] ?%)))))) -(ert-deftest test-ol/escape-custom-table () - "Escape string with custom character table." - (should - (string= - "Foo%3A%42ar%0A" - (org-link-escape "Foo:Bar\n" '(?\: ?\B))))) + +;;; Escape and Unescape Links -(ert-deftest test-ol/escape-custom-table-merge () - "Escape string with custom table merged with default table." - (should - (string= - "%5BF%6F%6F%3A%42ar%0A%5D" - (org-link-escape "[Foo:Bar\n]" '(?\: ?\B ?\o) t)))) +(ert-deftest test-ol/escape () + "Test `org-link-escape' specifications." + ;; No-op when there is no backslash or closing square bracket. + (should (string= "foo[" (org-link-escape "foo["))) + ;; Escape closing square bracket at the end of the link. + (should (string= "[foo\\]" (org-link-escape "[foo]"))) + ;; Escape closing square brackets followed by another square + ;; bracket. + (should (string= "foo\\][bar" (org-link-escape "foo][bar"))) + (should (string= "foo\\]]bar" (org-link-escape "foo]]bar"))) + ;; However, escaping closing square bracket at the end of the link + ;; has precedence over the previous rule. + (should (string= "foo]\\]" (org-link-escape "foo]]"))) + ;; Escape backslashes at the end of the link. + (should (string= "foo\\\\" (org-link-escape "foo\\"))) + ;; Escape backslashes that could be confused with escaping + ;; characters. + (should (string= "foo\\\\\\]" (org-link-escape "foo\\]"))) + (should (string= "foo\\\\\\][" (org-link-escape "foo\\]["))) + (should (string= "foo\\\\\\]]bar" (org-link-escape "foo\\]]bar"))) + ;; Do not escape backslash characters when unnecessary. + (should (string= "foo\\bar" (org-link-escape "foo\\bar"))) + (should (string= "foo\\]bar" (org-link-escape "foo\\]bar"))) + ;; Pathological cases: consecutive closing square brackets. + (should (string= "[[[foo\\]]\\]" (org-link-escape "[[[foo]]]"))) + (should (string= "[[[foo]\\]] bar" (org-link-escape "[[[foo]]] bar")))) -(ert-deftest test-ol/unescape-ascii-character () - "Unescape an ascii character." - (should - (string= - "[" - (org-link-unescape "%5B")))) +(ert-deftest test-ol/unescape () + "Test `org-link-unescape' specifications." + ;; No-op if there is no backslash. + (should (string= "foo[" (org-link-unescape "foo["))) + ;; No-op if backslashes are not escaping backslashes. + (should (string= "foo\\bar" (org-link-unescape "foo\\bar"))) + (should (string= "foo\\]bar" (org-link-unescape "foo\\]bar"))) + ;; + (should (string= "foo\\]" (org-link-unescape "foo\\\\\\]"))) + (should (string= "foo\\][" (org-link-unescape "foo\\\\\\]["))) + (should (string= "foo\\]]bar" (org-link-unescape "foo\\\\\\]]bar"))) + ;; Unescape backslashes at the end of the link. + (should (string= "foo\\" (org-link-unescape "foo\\\\"))) + ;; Unescape closing square bracket at the end of the link. + (should (string= "[foo]" (org-link-unescape "[foo\\]"))) + ;; Pathological cases: consecutive closing square brackets. + (should (string= "[[[foo]]]" (org-link-unescape "[[[foo\\]]\\]"))) + (should (string= "[[[foo]]] bar" (org-link-unescape "[[[foo]\\]] bar")))) -(ert-deftest test-ol/unescape-ascii-ctrl-character () - "Unescpae an ascii control character." +(ert-deftest test-ol/make-string () + "Test `org-link-make-string' specifications." + ;; Throw an error on empty URI. + (should-error (org-link-make-string "")) + ;; Empty description returns a [[URI]] construct. + (should (string= "[[uri]]"(org-link-make-string "uri"))) + ;; Non-empty description returns a [[URI][DESCRIPTION]] construct. (should - (string= - "\n" - (org-link-unescape "%0A")))) - -(ert-deftest test-ol/unescape-multibyte-character () - "Unescape unicode multibyte character." + (string= "[[uri][description]]" + (org-link-make-string "uri" "description"))) + ;; Escape "]]" strings in the description with zero-width spaces. (should - (string= - "€" - (org-link-unescape "%E2%82%AC")))) - -(ert-deftest test-ol/unescape-ascii-extended-char () - "Unescape old style percent escaped character." + (let ((zws (string ?\x200B))) + (string= (format "[[uri][foo]%s]bar]]" zws) + (org-link-make-string "uri" "foo]]bar")))) + ;; Prevent description from ending with a closing square bracket + ;; with a zero-width space. (should - (string= - "àâçèéêîôùû" - (decode-coding-string - (org-link-unescape "%E0%E2%E7%E8%E9%EA%EE%F4%F9%FB") 'latin-1)))) - -(ert-deftest test-ol/escape-url-with-escaped-char () - "Escape and unescape a URL that includes an escaped char. -http://article.gmane.org/gmane.emacs.orgmode/21459/" - (should - (string= - "http://some.host.com/form?&id=blah%2Bblah25" - (org-link-unescape - (org-link-escape "http://some.host.com/form?&id=blah%2Bblah25"))))) + (let ((zws (string ?\x200B))) + (string= (format "[[uri][foo]%s]]" zws) + (org-link-make-string "uri" "foo]"))))) ;;; Store links diff --git a/testing/lisp/test-org-clock.el b/testing/lisp/test-org-clock.el index fa336f680..1b32305fb 100644 --- a/testing/lisp/test-org-clock.el +++ b/testing/lisp/test-org-clock.el @@ -590,18 +590,18 @@ CLOCK: [2016-12-27 Wed 13:09]--[2016-12-28 Wed 15:09] => 26:00" ;; Otherwise, link to the headline in the current file. (should (equal - "| Headline | Time | -|--------------+---------| -| *Total time* | *26:00* | -|--------------+---------| -| [[file:filename::Foo][Foo]] | 26:00 |" + "| Headline | Time | +|-------------------------------+---------| +| *Total time* | *26:00* | +|-------------------------------+---------| +| [[file:filename::Foo][Foo]] | 26:00 |" (org-test-with-temp-text-in-file "* Foo CLOCK: [2016-12-27 Wed 13:09]--[2016-12-28 Wed 15:09] => 26:00" (let ((file (buffer-file-name))) (replace-regexp-in-string (regexp-quote file) "filename" - (test-org-clock-clocktable-contents ":link t")))))) + (test-org-clock-clocktable-contents ":link t :lang en")))))) ;; Ignore TODO keyword, priority cookie, COMMENT and tags in ;; headline. (should @@ -675,26 +675,26 @@ CLOCK: [2016-12-27 Wed 13:09]--[2016-12-28 Wed 15:09] => 26:00" ;; links if there is no description. (should (equal - "| Headline | Time | -|--------------+---------| -| *Total time* | *26:00* | -|--------------+---------| -| [[Foo %5B%5Bhttps://orgmode.org%5D%5BOrg mode%5D%5D][Foo Org mode]] | 26:00 |" + "| Headline | Time | +|-----------------------------------------+---------| +| *Total time* | *26:00* | +|-----------------------------------------+---------| +| [[Foo [[https://orgmode.org\\][Org mode]\\]][Foo Org mode]] | 26:00 |" (org-test-with-temp-text "* Foo [[https://orgmode.org][Org mode]] CLOCK: [2016-12-27 Wed 13:09]--[2016-12-28 Wed 15:09] => 26:00" - (test-org-clock-clocktable-contents ":link t")))) + (test-org-clock-clocktable-contents ":link t :lang en")))) (should (equal - "| Headline | Time | -|-------------------------+---------| -| *Total time* | *26:00* | -|-------------------------+---------| -| [[Foo %5B%5Bhttps://orgmode.org%5D%5D][Foo https://orgmode.org]] | 26:00 |" + "| Headline | Time | +|------------------------------+---------| +| *Total time* | *26:00* | +|------------------------------+---------| +| [[Foo [[https://orgmode.org]\\]][Foo https://orgmode.org]] | 26:00 |" (org-test-with-temp-text "* Foo [[https://orgmode.org]] CLOCK: [2016-12-27 Wed 13:09]--[2016-12-28 Wed 15:09] => 26:00" - (test-org-clock-clocktable-contents ":link t"))))) + (test-org-clock-clocktable-contents ":link t :lang en"))))) (ert-deftest test-org-clock/clocktable/compact () "Test \":compact\" parameter in Clock table." diff --git a/testing/lisp/test-org.el b/testing/lisp/test-org.el index a70c18f86..abc004689 100644 --- a/testing/lisp/test-org.el +++ b/testing/lisp/test-org.el @@ -2331,7 +2331,7 @@ SCHEDULED: <2014-03-04 tue.>" ;; Handle escape characters. (should (org-test-with-temp-text - "* H1\n:PROPERTIES:\n:CUSTOM_ID: [%]\n:END:\n* H2\n[[#%5B%25%5D]]" + "* H1\n:PROPERTIES:\n:CUSTOM_ID: [%]\n:END:\n* H2\n[[#[%\\]]]" (org-open-at-point) (looking-at-p "\\* H1"))) ;; Throw an error on false positives. @@ -2425,13 +2425,9 @@ Foo Bar (org-test-with-temp-text "[[*Test]]\n* TODO COMMENT Test" (org-open-at-point) (looking-at "\\* TODO COMMENT Test"))) - ;; Correctly un-hexify fuzzy links. + ;; Correctly un-escape fuzzy links. (should - (org-test-with-temp-text "* With space\n[[*With%20space][With space]]" - (org-open-at-point) - (bobp))) - (should - (org-test-with-temp-text "* [1]\n[[*%5B1%5D]]" + (org-test-with-temp-text "* [foo]\n[[*[foo\\]][With escaped characters]]" (org-open-at-point) (bobp))) ;; Match search strings containing newline characters, including diff --git a/testing/lisp/test-ox.el b/testing/lisp/test-ox.el index 3bd26224d..43637b926 100644 --- a/testing/lisp/test-ox.el +++ b/testing/lisp/test-ox.el @@ -3510,9 +3510,9 @@ Another text. (ref:text) (org-element-type (org-export-resolve-fuzzy-link (org-element-map tree 'link 'identity info t) info))))) - ;; Handle url-encoded fuzzy links. + ;; Handle escaped fuzzy links. (should - (org-test-with-parsed-data "* A B\n[[A%20B]]" + (org-test-with-parsed-data "* [foo]\n[[[foo\\]]]" (org-export-resolve-fuzzy-link (org-element-map tree 'link #'identity info t) info))))