Browse Source

Improve `org-insert-drawer' and related documentation.

* org.el (org-insert-property-drawer): Not an interactive
command anymore.
(org-insert-drawer): With a prefix argument, insert a property
drawer.  Check for headline within the region before inserting
the drawer.  Don't include special drawers in the completion
table.
(org-mode-map): New keybinding `C-c C-x d' for
`org-insert-drawer'.

* org.texi (Drawers): How to insert/complete drawers.

Thanks to Nicolas Goaziou for the discussion and the patch.
Bastien Guerry 7 years ago
parent
commit
471ddbd14e
2 changed files with 113 additions and 58 deletions
  1. 13 3
      doc/org.texi
  2. 100 55
      lisp/org.el

+ 13 - 3
doc/org.texi

@@ -1768,6 +1768,8 @@ numerically, alphabetically, by time, or by custom function.
 @cindex visibility cycling, drawers
 
 @vindex org-drawers
+@cindex org-insert-drawer
+@kindex C-c C-x d
 Sometimes you want to keep information associated with an entry, but you
 normally don't want to see it.  For this, Org mode has @emph{drawers}.
 Drawers need to be configured with the variable
@@ -1784,6 +1786,13 @@ look like this:
    After the drawer.
 @end example
 
+You can interactively insert drawers at point by calling
+@code{org-insert-drawer}, which is bound to @key{C-c C-x d}.  With an active
+region, this command will put the region inside the drawer.  With a prefix
+argument, this command calls @code{org-insert-property-drawer} and add a
+property drawer right below the current headline.  Completion over drawer
+keywords is also possible using @key{M-TAB}.
+
 Visibility cycling (@pxref{Visibility cycling}) on the headline will hide and
 show the entry, but keep the drawer collapsed to a single line.  In order to
 look inside the drawer, you need to move the cursor to the drawer line and
@@ -4894,8 +4903,8 @@ in the current file will be offered as possible completions.
 @orgcmd{C-c C-x p,org-set-property}
 Set a property.  This prompts for a property name and a value.  If
 necessary, the property drawer is created as well.
-@item M-x org-insert-property-drawer
-@findex org-insert-property-drawer
+@item C-u M-x org-insert-drawer
+@cindex org-insert-drawer
 Insert a property drawer into the current entry.  The drawer will be
 inserted early in the entry, but after the lines with planning
 information like deadlines.
@@ -15912,6 +15921,7 @@ If WHICH is nil or `all', get all properties.  If WHICH is
 `special' or `standard', only get that subclass.
 @end defun
 @vindex org-use-property-inheritance
+@findex org-insert-property-drawer
 @defun org-entry-get pom property &optional inherit
 Get value of PROPERTY for entry at point-or-marker POM.  By default,
 this only looks at properties defined locally in the entry.  If INHERIT
@@ -15934,7 +15944,7 @@ Get all property keys in the current buffer.
 @end defun
 
 @defun org-insert-property-drawer
-Insert a property drawer at point.
+Insert a property drawer for the current entry.  Also
 @end defun
 
 @defun org-entry-put-multivalued-property pom property &rest values

+ 100 - 55
lisp/org.el

@@ -14398,62 +14398,106 @@ formats in the current buffer."
 
 (defun org-insert-property-drawer ()
   "Insert a property drawer into the current entry."
-  (interactive)
-  (org-insert-drawer "PROPERTIES"))
+  (org-back-to-heading t)
+  (looking-at org-outline-regexp)
+  (let ((indent (if org-adapt-indentation
+		    (- (match-end 0) (match-beginning 0))
+		  0))
+	(beg (point))
+	(re (concat "^[ \t]*" org-keyword-time-regexp))
+	end hiddenp)
+    (outline-next-heading)
+    (setq end (point))
+    (goto-char beg)
+    (while (re-search-forward re end t))
+    (setq hiddenp (outline-invisible-p))
+    (end-of-line 1)
+    (and (equal (char-after) ?\n) (forward-char 1))
+    (while (looking-at "^[ \t]*\\(:CLOCK:\\|:LOGBOOK:\\|CLOCK:\\|:END:\\)")
+      (if (member (match-string 1) '("CLOCK:" ":END:"))
+	  ;; just skip this line
+	  (beginning-of-line 2)
+	;; Drawer start, find the end
+	(re-search-forward "^\\*+ \\|^[ \t]*:END:" nil t)
+	(beginning-of-line 1)))
+    (org-skip-over-state-notes)
+    (skip-chars-backward " \t\n\r")
+    (if (eq (char-before) ?*) (forward-char 1))
+    (let ((inhibit-read-only t)) (insert "\n:PROPERTIES:\n:END:"))
+    (beginning-of-line 0)
+    (org-indent-to-column indent)
+    (beginning-of-line 2)
+    (org-indent-to-column indent)
+    (beginning-of-line 0)
+    (if hiddenp
+	(save-excursion
+	  (org-back-to-heading t)
+	  (hide-entry))
+      (org-flag-drawer t))))
 
-(defun org-insert-drawer (&optional drawer)
-  "Insert a drawer into the current entry."
-  (interactive)
-  (if (org-region-active-p)
-    (let ((rbeg (region-beginning))
-	  (rend (region-end))
-	  (drawer (or drawer (completing-read "Drawer: " org-drawers))))
-      (goto-char rbeg)
-      (insert ":" drawer ":\n")
-      (move-beginning-of-line 1)
-      (indent-for-tab-command)
-      (goto-char rend)
-      (move-end-of-line 1)
-      (insert "\n:END:")
-      (move-beginning-of-line 1)
-      (indent-for-tab-command))
-    (org-back-to-heading t)
-    (looking-at org-outline-regexp)
-    (let ((indent (if org-adapt-indentation
-		      (- (match-end 0) (match-beginning 0))
-		    0))
-	  (beg (point))
-	  (re (concat "^[ \t]*" org-keyword-time-regexp))
-	  (drawer (or drawer (completing-read "Drawer: " org-drawers)))
-	  end hiddenp)
-      (outline-next-heading)
-      (setq end (point))
-      (goto-char beg)
-      (while (re-search-forward re end t))
-      (setq hiddenp (outline-invisible-p))
-      (end-of-line 1)
-      (and (equal (char-after) ?\n) (forward-char 1))
-      (while (looking-at "^[ \t]*\\(:CLOCK:\\|:LOGBOOK:\\|CLOCK:\\|:END:\\)")
-	(if (member (match-string 1) '("CLOCK:" ":END:"))
-	    ;; just skip this line
-	    (beginning-of-line 2)
-	  ;; Drawer start, find the end
-	  (re-search-forward "^\\*+ \\|^[ \t]*:END:" nil t)
-	  (beginning-of-line 1)))
-      (org-skip-over-state-notes)
-      (skip-chars-backward " \t\n\r")
-      (if (eq (char-before) ?*) (forward-char 1))
-      (let ((inhibit-read-only t)) (insert "\n:" drawer ":\n:END:"))
-      (beginning-of-line 0)
-      (org-indent-to-column indent)
-      (beginning-of-line 2)
-      (org-indent-to-column indent)
-      (beginning-of-line 0)
-      (if hiddenp
-	  (save-excursion
-	    (org-back-to-heading t)
-	    (hide-entry))
-	(org-flag-drawer t)))))
+(defun org-insert-drawer (&optional arg drawer)
+  "Insert a drawer at point.
+
+Optional argument DRAWER, when non-nil, is a string representing
+drawer's name.  Otherwise, the user is prompted for a name.
+
+If a region is active, insert the drawer around that region
+instead.
+
+Point is left between drawer's boundaries."
+  (interactive "P")
+  (let* ((logbook (if (stringp org-log-into-drawer) org-log-into-drawer
+		    "LOGBOOK"))
+	 ;; SYSTEM-DRAWERS is a list of drawer names that are used
+	 ;; internally by Org.  They are meant to be inserted
+	 ;; automatically.
+	 (system-drawers `("CLOCK" ,logbook "PROPERTIES"))
+	 ;; Remove system drawers from list.  Note: For some reason,
+	 ;; `org-completing-read' ignores the predicate while
+	 ;; `completing-read' handles it fine.
+	 (drawer (if arg "PROPERTIES"
+		   (or drawer
+		       (completing-read
+			"Drawer: " org-drawers
+			(lambda (d) (not (member d system-drawers))))))))
+    (cond
+     ;; With C-u, fall back on `org-insert-property-drawer'
+     (arg (org-insert-property-drawer))
+     ;; With an active region, insert a drawer at point.
+     ((not (org-region-active-p))
+      (progn
+	(unless (bolp) (insert "\n"))
+	(insert (format ":%s:\n\n:END:\n" drawer))
+	(forward-line -2)))
+     ;; Otherwise, insert the drawer at point
+     (t
+      (let ((rbeg (region-beginning))
+	    (rend (copy-marker (region-end))))
+	(unwind-protect
+	    (progn
+	      (goto-char rbeg)
+	      (beginning-of-line)
+	      (when (save-excursion
+		      (re-search-forward org-outline-regexp-bol rend t))
+		(error "Drawers cannot contain headlines"))
+	      ;; Position point at the beginning of the first
+	      ;; non-blank line in region. Insert drawer's opening
+	      ;; there, then indent it.
+	      (org-skip-whitespace)
+	      (beginning-of-line)
+	      (insert ":" drawer ":\n")
+	      (forward-line -1)
+	      (indent-for-tab-command)
+	      ;; Move point to the beginning of the first blank line
+	      ;; after the last non-blank line in region.  Insert
+	      ;; drawer's closing, then indent it.
+	      (goto-char rend)
+	      (skip-chars-backward " \r\t\n")
+	      (insert "\n:END:")
+	      (indent-for-tab-command)
+	      (unless (eolp) (insert "\n")))
+	  ;; Clear marker, whatever the outcome of insertion is.
+	  (set-marker rend nil)))))))
 
 (defvar org-property-set-functions-alist nil
   "Property set function alist.
@@ -17320,6 +17364,7 @@ BEG and END default to the buffer boundaries."
 (org-defkey org-mode-map "\C-c$"    'org-archive-subtree)
 (org-defkey org-mode-map "\C-c\C-x\C-s" 'org-advertized-archive-subtree)
 (org-defkey org-mode-map "\C-c\C-x\C-a" 'org-archive-subtree-default)
+(org-defkey org-mode-map "\C-c\C-xd" 'org-insert-drawer)
 (org-defkey org-mode-map "\C-c\C-xa" 'org-toggle-archive-tag)
 (org-defkey org-mode-map "\C-c\C-xA" 'org-archive-to-archive-sibling)
 (org-defkey org-mode-map "\C-c\C-xb" 'org-tree-to-indirect-buffer)