From 92ee4d06a2a88b26a3fe33acbf6abb40f6944dbe Mon Sep 17 00:00:00 2001 From: Nicolas Goaziou Date: Mon, 13 Mar 2017 22:45:54 +0100 Subject: [PATCH] org-capture: Fix escaping characters in template embedded S-exps * lisp/org-capture.el (org-capture-fill-template): Escape backslash characters in %i contents when those are inserted within a S-exp. Also prevent adding any prefix to %i contents spanning over multiple lines when they are inserted within a S-exp. * testing/lisp/test-org-capture.el (test-org-capture/fill-template): Add tests. Reported-by: Samuel Wales --- lisp/org-capture.el | 59 ++++++++++++++++++-------------- testing/lisp/test-org-capture.el | 13 +++++++ 2 files changed, 46 insertions(+), 26 deletions(-) diff --git a/lisp/org-capture.el b/lisp/org-capture.el index 9003b2ff7..7a2007e2d 100644 --- a/lisp/org-capture.el +++ b/lisp/org-capture.el @@ -1654,34 +1654,41 @@ The template may still contain \"%?\" for cursor positioning." (delete-region pos end) (set-marker pos nil) (set-marker end nil) - (let ((replacement - (pcase (string-to-char value) - (?< (format-time-string time-string)) - (?: - (or (plist-get org-store-link-plist (intern value)) - "")) - (?i (let ((lead (buffer-substring-no-properties + (let* ((inside-sexp? (org-capture-inside-embedded-elisp-p)) + (replacement + (pcase (string-to-char value) + (?< (format-time-string time-string)) + (?: + (or (plist-get org-store-link-plist (intern value)) + "")) + (?i + (if inside-sexp? v-i + ;; Outside embedded Lisp, repeat leading + ;; characters before initial place holder + ;; every line. + (let ((lead (buffer-substring-no-properties (line-beginning-position) (point)))) - (mapconcat #'identity - (split-string v-i "\n") - (concat "\n" lead)))) - (?a v-a) - (?A v-A) - (?c v-c) - (?f v-f) - (?F v-F) - (?k v-k) - (?K v-K) - (?l v-l) - (?n v-n) - (?t v-t) - (?T v-T) - (?u v-u) - (?U v-U) - (?x v-x)))) + (replace-regexp-in-string "\n\\(.\\)" + (concat lead "\\1") + v-i nil nil 1)))) + (?a v-a) + (?A v-A) + (?c v-c) + (?f v-f) + (?F v-F) + (?k v-k) + (?K v-K) + (?l v-l) + (?n v-n) + (?t v-t) + (?T v-T) + (?u v-u) + (?U v-U) + (?x v-x)))) (insert - (if (org-capture-inside-embedded-elisp-p) - (replace-regexp-in-string "\"" "\\\\\"" replacement) + (if inside-sexp? + ;; Escape sensitive characters. + (replace-regexp-in-string "[\\\"]" "\\\\\\&" replacement) replacement)))))))) ;; Expand %() embedded Elisp. Limit to Sexp originally marked. diff --git a/testing/lisp/test-org-capture.el b/testing/lisp/test-org-capture.el index 3f477b2c9..c630142d3 100644 --- a/testing/lisp/test-org-capture.el +++ b/testing/lisp/test-org-capture.el @@ -35,6 +35,13 @@ (should (equal "success!\n" (org-capture-fill-template "%(concat \"success\" \"!\")"))) + ;; It is possible to include other place holders in %(sexp). In + ;; that case properly escape \ and " characters. + (should + (equal "Nested string \"\\\"\\\"\"\n" + (let ((org-store-link-plist nil)) + (org-capture-fill-template "%(concat \"%i\")" + "Nested string \"\\\"\\\"\"")))) ;; %<...> placeholder. (should (equal (concat (format-time-string "%Y") "\n") @@ -66,6 +73,12 @@ (let ((org-store-link-plist nil)) (org-capture-fill-template "%i" "%(concat \"no \" \"evaluation\")")))) + ;; When %i contents span over multiple line, repeat initial leading + ;; characters over each line. + (should + (equal "> line 1\n> line 2\n" + (let ((org-store-link-plist nil)) + (org-capture-fill-template "> %i" "line 1\nline 2")))) ;; Test %-escaping with \ character. (should (equal "%i\n"