forked from mirrors/org-mode
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.
This commit is contained in:
parent
a902c830e7
commit
aa52550e4b
|
@ -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 ()
|
||||
|
|
Loading…
Reference in a new issue