I have about 21,000 headlines throughout my Org files, most of those tasks.
Nearly all of them have PROPERTIES drawers, and since those drawers appear at
the end of the entries, I've grown quite used to that arrangement. Anyway, it
didn't take much effort to adapt Org 9.x to allow for such positioning. This
patch is what I've been using, and it permits me to have the drawer either at
the beginning or the end.

What I haven't tested/modified yet is the code that drops items from the
cache, since that seems to also search for the properties re, yet I'm not sure
under what conditions that code is run...

--- a/lisp/org-element.el
+++ b/lisp/org-element.el
@@ -1018,6 +1018,20 @@ CONTENTS is the contents of the footnote-definition."
 
 ;;;; Headline
 
+(defcustom org-allow-properties-at-end t
+  "Allow the PROPERTIES drawer to appear at the end of an entry.
+Normally, for reasons of efficiency, the PROPERTIES drawer is
+expected to appear only at the beginning of an entry, after any
+scheduling lines. This avoids having to scan through a large
+entry in order to find its properties.
+
+When this option is non-nil, the PROPERIES drawer may also appear
+at the end of an entry. Note that this may adversely affect the
+speed of many operations, such as building the agenda."
+  :group 'org-todo
+  :type 'boolean
+  :package-version '(Org . "9.6.2"))
+
 (defun org-element--get-node-properties (&optional at-point-p?)
   "Return node properties for headline or property drawer at point.
 Upcase property names.  It avoids confusion between properties
@@ -1030,7 +1044,13 @@ parse properties for property drawer at point."
     (unless at-point-p?
       (forward-line)
       (when (looking-at-p org-element-planning-line-re) (forward-line)))
-    (when (looking-at org-property-drawer-re)
+    (when (if org-allow-properties-at-end
+              (and (re-search-forward org-property-drawer-re
+                                      (save-excursion
+                                        (outline-next-heading)
+                                        (point)) t)
+                   (goto-char (match-beginning 0)))
+            (looking-at org-property-drawer-re))
       (forward-line)
       (let ((end (match-end 0)) properties)
 	(while (< (line-end-position) end)
@@ -1158,8 +1178,14 @@ Assume point is at beginning of the headline."
                                             0)
                                           (point)))))
            (robust-end (and robust-begin
-                            (when (> (- contents-end 2) robust-begin)
-                              (- contents-end 2)))))
+                            (if (> (- contents-end 2) robust-begin)
+                                (min
+                                 (- contents-end 2)
+                                 (if (and org-allow-properties-at-end
+                                          (re-search-forward org-property-drawer-re
+                                                             contents-end t))
+                                     (- (match-beginning 0) 2)
+                                   (point-max)))))))
       (unless robust-end (setq robust-begin nil))
       (let ((headline
 	     (list 'headline
@@ -4246,13 +4272,14 @@ element it has to parse."
 	          (looking-at org-element-planning-line-re))
 	     (org-element-planning-parser limit))
             ;; Property drawer.
-            ((and (pcase mode
+            ((and (or org-allow-properties-at-end
+                      (pcase mode
 	                (`planning (eq ?* (char-after (line-beginning-position 0))))
 	                ((or `property-drawer `top-comment)
 		         (save-excursion
 		           (beginning-of-line 0)
 		           (not (looking-at "[[:blank:]]*$"))))
-	            (_ nil))
+	                (_ nil)))
 	          (looking-at org-property-drawer-re))
 	     (org-element-property-drawer-parser limit))
             ;; When not at bol, point is at the beginning of an item or
--- a/lisp/org.el
+++ b/lisp/org.el
@@ -12342,19 +12342,29 @@ the document if before the first headline.  If it is not given,
 it will be found.  If the drawer does not exist, create it if
 FORCE is non-nil, or return nil."
   (org-with-wide-buffer
-   (let ((beg (cond (beg (goto-char beg))
+   (let* ((beg (cond (beg (goto-char beg))
 		     ((or (not (featurep 'org-inlinetask))
 			  (org-inlinetask-in-task-p))
 		      (org-back-to-heading-or-point-min t) (point))
 		     (t (org-with-limited-levels
 			 (org-back-to-heading-or-point-min t))
-		       (point)))))
+		        (point))))
+          (end (save-excursion
+                 (outline-next-heading)
+                 (point))))
      ;; Move point to its position according to its positional rules.
      (cond ((org-before-first-heading-p)
 	    (while (and (org-at-comment-p) (bolp)) (forward-line)))
 	   (t (forward-line)
 	      (when (looking-at-p org-planning-line-re) (forward-line))))
-     (cond ((looking-at org-property-drawer-re)
+     (cond ((and (< (point) end)
+                 (if org-allow-properties-at-end
+                     (and (re-search-forward org-property-drawer-re
+                                             (save-excursion
+                                               (outline-next-heading)
+                                               (point)) t)
+                          (goto-char (match-beginning 0)))
+                   (looking-at org-property-drawer-re)))
             (forward-line)
             (cons (point) (progn (goto-char (match-end 0))
 			         (line-beginning-position))))
@@ -20600,7 +20610,8 @@ properties, clocking lines and logbook drawers."
   ;; Skip planning information.
   (when (looking-at-p org-planning-line-re) (forward-line))
   ;; Skip property drawer.
-  (when (looking-at org-property-drawer-re)
+  (when (and (not org-allow-properties-at-end)
+             (looking-at org-property-drawer-re))
     (goto-char (match-end 0))
     (forward-line))
   ;; When FULL is not nil, skip more.
--
John Wiegley                  GPG fingerprint = 4710 CF98 AF9B 327B B80F
http://newartisans.com                          60E1 46C4 BD1A 7AC1 4BA2

Reply via email to