Browse Source

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 <samologist@gmail.com>
<http://permalink.gmane.org/gmane.emacs.orgmode/112232>
Nicolas Goaziou 8 years ago
parent
commit
92ee4d06a2
2 changed files with 46 additions and 26 deletions
  1. 33 26
      lisp/org-capture.el
  2. 13 0
      testing/lisp/test-org-capture.el

+ 33 - 26
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.

+ 13 - 0
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"