Browse Source

org-capture: Fix sexp handling

* lisp/org-capture.el (org-capture-expand-embedded-elisp): Do not mark
  invalid sexp.  Renamed from `org-capture--expand-embedded-elisp'.
(org-capture-fill-template): Escape " characters for placeholders
located within sexp.  Small refactoring.
(org-capture-inside-embedded-elisp-p): Rewrite function.
Nicolas Goaziou 9 years ago
parent
commit
aa52550e4b
1 changed files with 95 additions and 101 deletions
  1. 95 101
      lisp/org-capture.el

+ 95 - 101
lisp/org-capture.el

@@ -1,6 +1,6 @@
 ;;; org-capture.el --- Fast note taking in Org-mode  -*- lexical-binding: t; -*-
 
-;; Copyright (C) 2010-2015 Free Software Foundation, Inc.
+;; Copyright (C) 2010-2016 Free Software Foundation, Inc.
 
 ;; Author: Carsten Dominik <carsten at orgmode dot org>
 ;; Keywords: outlines, hypermedia, calendar, wp
@@ -1543,42 +1543,35 @@ Lisp programs can force the template by setting KEYS to a string."
 (defun org-capture-fill-template (&optional template initial annotation)
   "Fill a template and return the filled template as a string.
 The template may still contain \"%?\" for cursor positioning."
-  (setq template (or template (org-capture-get :template)))
-  (when (stringp initial)
-    (setq initial (org-no-properties initial)))
-  (let* ((buffer (org-capture-get :buffer))
+  (let* ((template (or template (org-capture-get :template)))
+	 (buffer (org-capture-get :buffer))
 	 (file (buffer-file-name (or (buffer-base-buffer buffer) buffer)))
-	 (ct (org-capture-get :default-time))
-	 (dct (decode-time ct))
-	 (ct1
-	  (if (< (nth 2 dct) org-extend-today-until)
-	      (encode-time 0 59 23 (1- (nth 3 dct)) (nth 4 dct) (nth 5 dct))
-	    ct))
-	 (v-c (and (> (length kill-ring) 0) (current-kill 0)))
+	 (time (let* ((c (or (org-capture-get :default-time) (current-time)))
+		      (d (decode-time c)))
+		 (if (< (nth 2 d) org-extend-today-until)
+		     (encode-time 0 59 23 (1- (nth 3 d)) (nth 4 d) (nth 5 d))
+		   c)))
+	 (v-t (format-time-string (org-time-stamp-format nil) time))
+	 (v-T (format-time-string (org-time-stamp-format t) time))
+	 (v-u (format-time-string (org-time-stamp-format nil t) time))
+	 (v-U (format-time-string (org-time-stamp-format t t) time))
+	 (v-c (and kill-ring (current-kill 0)))
 	 (v-x (or (org-get-x-clipboard 'PRIMARY)
 		  (org-get-x-clipboard 'CLIPBOARD)
 		  (org-get-x-clipboard 'SECONDARY)))
-	 (v-t (format-time-string (car org-time-stamp-formats) ct1))
-	 (v-T (format-time-string (cdr org-time-stamp-formats) ct1))
-	 (v-u (concat "[" (substring v-t 1 -1) "]"))
-	 (v-U (concat "[" (substring v-T 1 -1) "]"))
 	 ;; `initial' and `annotation' might have been passed.  But if
 	 ;; the property list has them, we prefer those values.
 	 (v-i (or (plist-get org-store-link-plist :initial)
-		  initial
+		  (and (stringp initial) (org-no-properties initial))
 		  (org-capture-get :initial)
 		  ""))
-	 (v-a (or (plist-get org-store-link-plist :annotation)
-		  annotation
-		  (org-capture-get :annotation)
-		  ""))
-	 ;; Is the link empty?  Then we do not want it...
-	 (v-a (if (equal v-a "[[]]") "" v-a))
-	 (clipboards (remq nil (list v-i
-				     (org-get-x-clipboard 'PRIMARY)
-				     (org-get-x-clipboard 'CLIPBOARD)
-				     (org-get-x-clipboard 'SECONDARY)
-				     v-c)))
+	 (v-a
+	  (let ((a (or (plist-get org-store-link-plist :annotation)
+		       annotation
+		       (org-capture-get :annotation)
+		       "")))
+	    ;; Is the link empty?  Then we do not want it...
+	    (if (equal a "[[]]") "" a)))
 	 (l-re "\\[\\[\\(.*?\\)\\]\\(\\[.*?\\]\\)?\\]")
 	 (v-A (if (and v-a (string-match l-re v-a))
 		  (replace-match "[[\\1][%^{Link description}]]" nil nil v-a)
@@ -1595,12 +1588,15 @@ The template may still contain \"%?\" for cursor positioning."
 		   org-clock-heading)))
 	 (v-f (or (org-capture-get :original-file-nondirectory) ""))
 	 (v-F (or (org-capture-get :original-file) ""))
-	 (org-startup-folded nil)
-	 (org-inhibit-startup t))
+	 (clipboards (delq nil
+			   (list v-i
+				 (org-get-x-clipboard 'PRIMARY)
+				 (org-get-x-clipboard 'CLIPBOARD)
+				 (org-get-x-clipboard 'SECONDARY)
+				 v-c))))
 
     (setq org-store-link-plist (plist-put org-store-link-plist :annotation v-a))
     (setq org-store-link-plist (plist-put org-store-link-plist :initial v-i))
-    (setq initial v-i)
 
     (unless template
       (setq template "")
@@ -1609,13 +1605,10 @@ The template may still contain \"%?\" for cursor positioning."
     (save-window-excursion
       (org-switch-to-buffer-other-window (get-buffer-create "*Capture*"))
       (erase-buffer)
-      ;; Turn on org-mode in temp buffer, set local variables.  This
-      ;; is to support completion in interactive prompts
-      (insert template)
-      (goto-char (point-min))
-      (org-clone-local-variables buffer "\\`org-")
       (setq buffer-file-name nil)
       (setq mark-active nil)
+      (insert template)
+      (goto-char (point-min))
 
       ;; %[] insert contents of a file.
       (save-excursion
@@ -1633,7 +1626,7 @@ The template may still contain \"%?\" for cursor positioning."
 				       error))))))))
 
       ;; Mark %() embedded elisp for later evaluation.
-      (org-capture--expand-embedded-elisp 'mark)
+      (org-capture-expand-embedded-elisp 'mark)
 
       ;; Expand non-interactive templates.
       (let ((regexp "%\\(:[-a-za-z]+\\|<\\([^>\n]+\\)>\\|[aAcfFikKlntTuUx]\\)"))
@@ -1646,42 +1639,48 @@ The template may still contain \"%?\" for cursor positioning."
 	      (unless (org-capture-escaped-%)
 		(goto-char pos)
 		(delete-region pos end)
-		(pcase (string-to-char value)
-		  (?<
-		   ;; The current time.
-		   (insert (format-time-string time-string)))
-		  (?:
-		   ;; From the property list.
-		   (insert (or (plist-get org-store-link-plist (intern value))
-			       "")))
-		  (?i (let ((lead (buffer-substring-no-properties
-				   (line-beginning-position) pos)))
-			(insert (mapconcat #'identity
-					   (split-string initial "\n")
-					   (concat "\n" lead)))))
-		  (?a (insert v-a))
-		  (?A (insert v-A))
-		  (?c (insert v-c))
-		  (?f (insert v-f))
-		  (?F (insert v-F))
-		  (?k (insert v-k))
-		  (?K (insert v-K))
-		  (?l (insert v-l))
-		  (?n (insert v-n))
-		  (?t (insert v-t))
-		  (?T (insert v-T))
-		  (?u (insert v-u))
-		  (?U (insert v-U))
-		  (?x (insert v-x)))
+		(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
+					  (line-beginning-position) pos)))
+			       (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))))
+		  (insert
+		   (if (org-capture-inside-embedded-elisp-p)
+		       (replace-regexp-in-string
+			"\"" "\\\\\"" replacement nil t)
+		     replacement)))
 		(set-marker pos nil)
 		(set-marker end nil))))))
 
       ;; Expand %() embedded Elisp.  Limit to Sexp originally marked.
-      (org-capture--expand-embedded-elisp)
+      (org-capture-expand-embedded-elisp)
 
       ;; Expand interactive templates.  This is the last step so that
-      ;; template is mostly expanded when prompting happens.
+      ;; template is mostly expanded when prompting happens.  Turn on
+      ;; Org mode and set local variables.  This is to support
+      ;; completion in interactive prompts.
       (let ((org-inhibit-startup t)) (org-mode))
+      (org-clone-local-variables buffer "\\`org-")
       (let (strings)			; Stores interactive answers.
 	(save-excursion
 	  (let ((regexp "%\\^\\(?:{\\([^}]*\\)}\\)?\\([CgGLptTuU]\\)?"))
@@ -1781,7 +1780,7 @@ The template may still contain \"%?\" for cursor positioning."
       (delete-region (point) (point-max))
       (insert "\n")
 
-      ;; Return the expanded template and kill the temporary buffer.
+      ;; Return the expanded template and kill the capture buffer.
       (untabify (point-min) (point-max))
       (set-buffer-modified-p nil)
       (prog1 (buffer-substring-no-properties (point-min) (point-max))
@@ -1797,7 +1796,7 @@ placeholder to check."
       (delete-char (/ (1+ n) 2))
       (= (% n 2) 1))))
 
-(defun org-capture--expand-embedded-elisp (&optional mark)
+(defun org-capture-expand-embedded-elisp (&optional mark)
   "Evaluate embedded elisp %(sexp) and replace with the result.
 When optional MARK argument is non-nil, mark Sexp with a text
 property (`org-embedded-elisp') for later evaluation.  Only
@@ -1805,25 +1804,30 @@ marked Sexp are evaluated when this argument is nil."
   (save-excursion
     (goto-char (point-min))
     (while (re-search-forward "%(" nil t)
-      (unless (org-capture-escaped-%)
-	(if mark
-	    (put-text-property
-	     (match-beginning 0) (match-end 0) 'org-embedded-elisp t)
-	  (when (get-text-property (match-beginning 0) 'org-embedded-elisp)
-	    (goto-char (match-beginning 0))
-	    (let ((template-start (point)))
-	      (forward-char 1)
-	      (let* ((sexp (read (current-buffer)))
-		     (result (org-eval
-			      (org-capture--expand-keyword-in-embedded-elisp
-			       sexp))))
-		(delete-region template-start (point))
-		(when result
-		  (if (stringp result)
-		      (insert result)
-		    (error
-		     "Capture template sexp `%s' must evaluate to string or nil"
-		     sexp)))))))))))
+      (cond
+       ((get-text-property (match-beginning 0) 'org-embedded-elisp)
+	(goto-char (match-beginning 0))
+	(let ((template-start (point)))
+	  (forward-char 1)
+	  (let* ((sexp (read (current-buffer)))
+		 (result (org-eval
+			  (org-capture--expand-keyword-in-embedded-elisp
+			   sexp))))
+	    (delete-region template-start (point))
+	    (cond
+	     ((not result) nil)
+	     ((stringp result) (insert result))
+	     (t (error
+		 "Capture template sexp `%s' must evaluate to string or nil"
+		 sexp))))))
+       ((not mark) nil)
+       ;; Only mark valid and non-escaped sexp.
+       ((org-capture-escaped-%) nil)
+       (t
+	(let ((end (with-syntax-table emacs-lisp-mode-syntax-table
+		     (ignore-errors (scan-sexps (1- (point)) 1)))))
+	  (when end
+	    (put-text-property (- (point) 2) end 'org-embedded-elisp t))))))))
 
 (defun org-capture--expand-keyword-in-embedded-elisp (attr)
   "Recursively replace capture link keywords in ATTR sexp.
@@ -1840,20 +1844,10 @@ Such keywords are prefixed with \"%:\".  See
 	(t attr)))
 
 (defun org-capture-inside-embedded-elisp-p ()
-  "Return non-nil if point is inside of embedded elisp %(sexp)."
-  (let (beg end)
-    (with-syntax-table emacs-lisp-mode-syntax-table
-      (save-excursion
-	;; `looking-at' and `search-backward' below do not match the "%(" if
-	;; point is in its middle
-	(when (equal (char-before) ?%)
-	  (backward-char))
-	(save-match-data
-	  (when (or (looking-at "%(") (search-backward "%(" nil t))
-	    (setq beg (point))
-	    (setq end (progn (forward-char) (forward-sexp) (1- (point)))))))
-      (when (and beg end)
-	(and (<= (point) end) (>= (point) beg))))))
+  "Non-nil if point is inside of embedded elisp %(sexp).
+Assume sexps have been marked with
+`org-capture-expand-embedded-elisp' beforehand."
+  (get-text-property (point) 'org-embedded-elisp))
 
 ;;;###autoload
 (defun org-capture-import-remember-templates ()