Browse Source

org-capture: Fix plain capture at the end of the buffer.

* lisp/org-capture.el (org-capture-place-plain-text): Fix plain
  capture at the end of the buffer.
* testing/lisp/test-org-capture.el (test-org-capture/plain): New test.
Nicolas Goaziou 7 months ago
parent
commit
7a4a10dea0
2 changed files with 96 additions and 28 deletions
  1. 31 28
      lisp/org-capture.el
  2. 65 0
      testing/lisp/test-org-capture.el

+ 31 - 28
lisp/org-capture.el

@@ -1291,35 +1291,38 @@ If the target locator points at an Org node, place the template into
 the text of the entry, before the first child.  If not, place the
 template at the beginning or end of the file.
 Of course, if exact position has been required, just put it there."
-  (let* ((txt (org-capture-get :template))
-	 beg end)
-    (cond
-     ((org-capture-get :exact-position)
-      (goto-char (org-capture-get :exact-position)))
-     ((and (org-capture-get :target-entry-p)
-	   (bolp)
-	   (looking-at org-outline-regexp))
-      ;; we should place the text into this entry
-      (if (org-capture-get :prepend)
-	  ;; Skip meta data and drawers
-	  (org-end-of-meta-data t)
-	;; go to ent of the entry text, before the next headline
-	(outline-next-heading)))
-     (t
-      ;; beginning or end of file
-      (goto-char (if (org-capture-get :prepend) (point-min) (point-max)))))
-    (or (bolp) (newline))
+  (cond
+   ((org-capture-get :exact-position)
+    (goto-char (org-capture-get :exact-position)))
+   ((and (org-capture-get :target-entry-p)
+	 (bolp)
+	 (looking-at org-outline-regexp))
+    ;; Place the text into this entry.
+    (if (org-capture-get :prepend)
+	;; Skip meta data and drawers.
+	(org-end-of-meta-data t)
+      ;; Go to end of the entry text, before the next headline
+      (outline-next-heading)))
+   (t
+    ;; Beginning or end of file.
+    (goto-char (if (org-capture-get :prepend) (point-min) (point-max)))))
+  (let ((origin (point)))
+    (unless (bolp) (insert "\n"))
     (org-capture-empty-lines-before)
-    (setq beg (point))
-    (insert txt)
-    (org-capture-empty-lines-after)
-    (org-capture-position-for-last-stored beg)
-    (setq end (point))
-    (org-capture-mark-kill-region beg (1- end))
-    (org-capture-narrow beg (1- end))
-    (when (or (re-search-backward "%\\?" beg t)
-	      (re-search-forward "%\\?" end t))
-      (replace-match ""))))
+    (org-capture-position-for-last-stored (point))
+    (let ((beg (point)))
+      (insert (org-capture-get :template))
+      (unless (bolp) (insert "\n"))
+      ;; Ignore the final newline character so as to not alter data
+      ;; after inserted text.  Yet, if the template is empty, make
+      ;; sure END matches BEG instead of pointing before it.
+      (let ((end (max beg (1- (point)))))
+	(org-capture-empty-lines-after)
+	(org-capture-mark-kill-region origin (point))
+	(org-capture-narrow beg end)
+	(when (or (search-backward "%?" beg t)
+		  (search-forward "%?" end t))
+	  (replace-match ""))))))
 
 (defun org-capture-mark-kill-region (beg end)
   "Mark the region that will have to be killed when aborting capture."

+ 65 - 0
testing/lisp/test-org-capture.el

@@ -373,6 +373,71 @@
 	(org-capture nil "t")
 	(org-table-get-stored-formulas))))))
 
+(ert-deftest test-org-capture/plain ()
+  "Test `plain' type in capture template."
+  ;; Insert at end of the file, unless `:prepend' is non-nil.
+  (should
+   (equal "Some text.\nFoo\n"
+	  (org-test-with-temp-text-in-file "Some text."
+	    (let* ((file (buffer-file-name))
+		   (org-capture-templates
+		    `(("t" "Text" plain (file ,file) "Foo"
+		       :immediate-finish t))))
+	      (org-capture nil "t")
+	      (buffer-string)))))
+  (should
+   (equal "Foo\nSome text."
+	  (org-test-with-temp-text-in-file "Some text."
+	    (let* ((file (buffer-file-name))
+		   (org-capture-templates
+		    `(("t" "Text" plain (file ,file) "Foo"
+		       :immediate-finish t :prepend t))))
+	      (org-capture nil "t")
+	      (buffer-string)))))
+  ;; When a headline is specified, add it at the beginning of the
+  ;; entry, past any meta-data, or at its end, depending on
+  ;; `:prepend'.
+  (should
+   (equal "* A\nSCHEDULED: <2012-03-29 Thu>\nSome text.\nFoo\n* B"
+	  (org-test-with-temp-text-in-file
+	      "* A\nSCHEDULED: <2012-03-29 Thu>\nSome text.\n* B"
+	    (let* ((file (buffer-file-name))
+		   (org-capture-templates
+		    `(("t" "Text" plain (file+headline ,file "A") "Foo"
+		       :immediate-finish t))))
+	      (org-capture nil "t")
+	      (buffer-string)))))
+  (should
+   (equal "* A\nSCHEDULED: <2012-03-29 Thu>\nFoo\nSome text.\n* B"
+	  (org-test-with-temp-text-in-file
+	      "* A\nSCHEDULED: <2012-03-29 Thu>\nSome text.\n* B"
+	    (let* ((file (buffer-file-name))
+		   (org-capture-templates
+		    `(("t" "Text" plain (file+headline ,file "A") "Foo"
+		       :immediate-finish t :prepend t))))
+	      (org-capture nil "t")
+	      (buffer-string)))))
+  ;; At an exact position, in the middle of a line, make sure to
+  ;; insert text on a line on its own.
+  (should
+   (equal "A\nX\nB"
+	  (org-test-with-temp-text-in-file "AB"
+	    (let* ((file (buffer-file-name))
+		   (org-capture-templates
+		    `(("t" "Text" plain (file+function ,file forward-char) "X"
+		       :immediate-finish t))))
+	      (org-capture nil "t")
+	      (buffer-string)))))
+  ;; Pathological case: insert an empty template in an empty file.
+  (should
+   (equal ""
+	  (org-test-with-temp-text-in-file ""
+	    (let* ((file (buffer-file-name))
+		   (org-capture-templates
+		    `(("t" "Text" plain (file ,file) ""
+		       :immediate-finish t))))
+	      (org-capture nil "t")
+	      (buffer-string))))))
 
 (provide 'test-org-capture)
 ;;; test-org-capture.el ends here