Browse Source

Add level/indentation cycling for empty entries/items

Carsten Dominik 15 years ago
parent
commit
e1d0f342a1
5 changed files with 127 additions and 15 deletions
  1. 5 0
      doc/ChangeLog
  2. 12 0
      doc/org.texi
  3. 6 0
      lisp/ChangeLog
  4. 40 14
      lisp/org-list.el
  5. 64 1
      lisp/org.el

+ 5 - 0
doc/ChangeLog

@@ -1,3 +1,8 @@
+2009-11-03  Carsten Dominik  <carsten.dominik@gmail.com>
+
+	* org.texi (Structure editing, Plain lists): Document indentation
+	cycling in empty entries with TAB.
+
 2009-10-31  Carsten Dominik  <carsten.dominik@gmail.com>
 
 	* orgcard.tex: Document the new archiving keys.

+ 12 - 0
doc/org.texi

@@ -1017,6 +1017,12 @@ variable @code{org-treat-insert-todo-heading-as-state-change}.
 Insert new TODO entry with same level as current heading.  Like
 @kbd{C-@key{RET}}, the new headline will be inserted after the current
 subtree.
+@kindex @key{TAB}
+@item @key{TAB} @r{in new, empty entry}
+In a new entry with no text yet, the first @key{TAB} demotes the entry to
+become a child of the previous one.  The next @key{TAB} makes it a parent,
+and so on, all the way to top level.  Yet another @key{TAB}, and you are back
+to the initial level.
 @kindex M-@key{left}
 @item M-@key{left}
 Promote current heading by one level.
@@ -1286,6 +1292,12 @@ bullet, a bullet is added to the current line.
 @kindex M-S-@key{RET}
 @item M-S-@key{RET}
 Insert a new item with a checkbox (@pxref{Checkboxes}).
+@kindex @key{TAB}
+@item @key{TAB} @r{in new, empty item}
+In a new item with no text yet, the first @key{TAB} demotes the item to
+become a child of the previous one.  The next @key{TAB} makes it a parent,
+and so on, all the way to the left margin.  Yet another @key{TAB}, and you
+are back to the initial level.
 @kindex S-@key{up}
 @kindex S-@key{down}
 @item S-@key{up}

+ 6 - 0
lisp/ChangeLog

@@ -1,5 +1,11 @@
 2009-11-03  Carsten Dominik  <carsten.dominik@gmail.com>
 
+	* org.el (org-tab-ind-state): New variable.
+	(org-cycle-level): New function.
+	(org-cycle-level-after-item/entry-creation): New option.
+
+	* org-list.el (org-cycle-item-indentation): New function.
+
 	* org.el (org-refile): Make prefix argument 2 refile to current
 	clock.
 	(org-priority): Interpret action `remove' as call to remove the

+ 40 - 14
lisp/org-list.el

@@ -840,6 +840,7 @@ with something like \"1.\" or \"2)\"."
     (org-goto-line line)
     (org-move-to-column col)))
 
+(defvar org-suppress-item-indentation) ; dynamically scoped parameter
 (defun org-fix-bullet-type (&optional force-bullet)
   "Make sure all items in this list have the same bullet as the first item.
 Also, fix the indentation."
@@ -874,7 +875,8 @@ Also, fix the indentation."
 	  (looking-at "\\S-+ *")
 	  (setq oldbullet (match-string 0))
 	  (unless (equal bullet oldbullet) (replace-match bullet))
-	  (org-shift-item-indentation (- (length bullet) (length oldbullet))))))
+	  (org-shift-item-indentation (- (length bullet)
+					 (length oldbullet))))))
     (org-goto-line line)
     (org-move-to-column col)
     (if (string-match "[0-9]" bullet)
@@ -882,19 +884,20 @@ Also, fix the indentation."
 
 (defun org-shift-item-indentation (delta)
   "Shift the indentation in current item by DELTA."
-  (save-excursion
-    (let ((beg (point-at-bol))
-	  (end (progn (org-end-of-item) (point)))
-	  i)
-      (goto-char end)
-      (beginning-of-line 0)
-      (while (> (point) beg)
-	(when (looking-at "[ \t]*\\S-")
-	  ;; this is not an empty line
-	  (setq i (org-get-indentation))
-	  (if (and (> i 0) (> (setq i (+ i delta)) 0))
-	      (indent-line-to i)))
-	(beginning-of-line 0)))))
+  (unless (org-bound-and-true-p org-suppress-item-indentation)
+    (save-excursion
+      (let ((beg (point-at-bol))
+	    (end (progn (org-end-of-item) (point)))
+	    i)
+	(goto-char end)
+	(beginning-of-line 0)
+	(while (> (point) beg)
+	  (when (looking-at "[ \t]*\\S-")
+	    ;; this is not an empty line
+	    (setq i (org-get-indentation))
+	    (if (and (> i 0) (> (setq i (+ i delta)) 0))
+		(indent-line-to i)))
+	  (beginning-of-line 0))))))
 
 (defun org-beginning-of-item-list ()
   "Go to the beginning of the current item list.
@@ -1040,6 +1043,29 @@ Assumes cursor in item line."
 	  (cons ind-up bullet-up)
 	  (cons ind-down bullet-down))))
 
+(defvar org-tab-ind-state) ; defined in org.el
+(defun org-cycle-item-indentation ()
+  (let ((org-suppress-item-indentation t)
+	(org-adapt-indentation nil))
+    (cond
+     ((and (looking-at "[ \t]*$")
+	   (looking-back "^\\([ \t]*\\)\\([-+*]\\|[0-9]+[).]\\)[ \t]+"))
+      (setq this-command 'org-cycle-item-indentation)
+      (if (eq last-command 'org-cycle-item-indentation)
+	  (condition-case nil
+	      (progn (org-outdent-item 1)
+		     (if (equal org-tab-ind-state (org-get-indentation))
+			 (org-outdent-item 1))
+		     (end-of-line 1))
+	    (error
+	     (progn
+	       (while (< (org-get-indentation) org-tab-ind-state)
+		 (progn (org-indent-item 1) (end-of-line 1)))
+	       (setq this-command 'org-cycle))))
+	(setq org-tab-ind-state (org-get-indentation))
+	(org-indent-item 1))
+      t))))
+
 (defun org-get-bullet ()
   (save-excursion
     (goto-char (point-at-bol))

+ 64 - 1
lisp/org.el

@@ -680,6 +680,21 @@ of the buffer."
   :group 'org-cycle
   :type 'boolean)
 
+(defcustom org-cycle-level-after-item/entry-creation t
+  "Non-nil means, cycle entry level or item indentation in new empty entries.
+
+When the cursor is at the end of an empty headline, i.e with only stars
+and maybe a TODO keyword, TAB will then switch the entry to become a child,
+and then all possible anchestor states, before returning to the original state.
+This makes data entry extremely fast:  M-RET to create a new hedline,
+on TAB to make it a child, two or more tabs to make it a (grand-)uncle.
+
+When the cursor is at the end of an empty plain list item, one TAB will
+make it a subitem, two or more tabs will back up to make this an item
+higher up in the item hierarchy."
+  :group 'org-cycle
+  :type 'boolean)
+
 (defcustom org-cycle-emulate-tab t
   "Where should `org-cycle' emulate TAB.
 nil         Never
@@ -4953,7 +4968,10 @@ in special contexts.
   But only if also the variable `org-cycle-global-at-bob' is t."
   (interactive "P")
   (org-load-modules-maybe)
-  (unless (run-hook-with-args-until-success 'org-tab-first-hook)
+  (unless (or (run-hook-with-args-until-success 'org-tab-first-hook)
+	      (and org-cycle-level-after-item/entry-creation
+		   (or (org-cycle-level)
+		       (org-cycle-item-indentation))))
     (let* ((limit-level
 	    (or org-cycle-max-level
 		(and (boundp 'org-inlinetask-min-level)
@@ -5040,6 +5058,9 @@ in special contexts.
 
        ((org-try-cdlatex-tab))
 
+       ((run-hook-with-args-until-success
+	 'org-tab-before-tab-emulation-hook))
+
        ((and (eq org-cycle-emulate-tab 'exc-hl-bol)
 	     (or (not (bolp))
 		 (not (looking-at outline-regexp))))
@@ -6077,6 +6098,16 @@ in the region."
 	    ((eolp) (insert " "))
 	    ((equal (char-after) ?\ ) (forward-char 1))))))
 
+(defun org-current-level ()
+  "Return the level of the current entry, or nil if before the first headline.
+The level is the number of stars at the beginning of the headline."
+  (save-excursion
+    (condition-case nil
+	(progn
+	  (org-back-to-heading t)
+	  (funcall outline-level))
+      (error nil))))
+
 (defun org-reduced-level (l)
   "Compute the effective level of a heading.
 This takes into account the setting of `org-odd-levels-only'."
@@ -6129,6 +6160,31 @@ in the region."
     (if org-adapt-indentation (org-fixup-indentation diff))
     (run-hooks 'org-after-demote-entry-hook)))
 
+(defvar org-tab-ind-state nil)
+
+(defun org-cycle-level ()
+  (let ((org-adapt-indentation nil))
+    (when (and (looking-at "[ \t]*$")
+	       (looking-back
+		(concat "^\\(\\*+\\)[ \t]+\\(" org-todo-regexp "\\)?[ \t]*")))
+      (setq this-command 'org-cycle-level)
+      (if (eq last-command 'org-cycle-level)
+	  (condition-case nil
+	      (progn (org-do-promote)
+		     (if (equal org-tab-ind-state (org-current-level))
+			 (org-do-promote)))
+	    (error
+	     (progn
+	       (save-excursion
+		 (beginning-of-line 1)
+		 (and (looking-at "\\*+")
+		      (replace-match
+		       (make-string org-tab-ind-state ?*))))
+	       (setq this-command 'org-cycle))))
+	(setq org-tab-ind-state (- (match-end 1) (match-beginning 1)))
+	(org-do-demote))
+      t)))
+
 (defun org-map-tree (fun)
   "Call FUN for every heading underneath the current one."
   (org-back-to-heading)
@@ -14893,6 +14949,12 @@ This hook runs after it has been established that not table field motion and
 not visibility should be done because of current context.  This is probably
 the place where a package like yasnippets can hook in.")
 
+(defvar org-tab-before-tab-emulation-hook nil
+  "Hook for functions to attach themselves to TAB.
+See `org-ctrl-c-ctrl-c-hook' for more information.
+This hook runs after every other options for TAB have been exhausted, but
+before indentation and \t insertion takes place.")
+
 (defvar org-metaleft-hook nil
   "Hook for functions attaching themselves to `M-left'.
 See `org-ctrl-c-ctrl-c-hook' for more information.")
@@ -17533,3 +17595,4 @@ Still experimental, may disappear in the future."
 ;; arch-tag: e77da1a7-acc7-4336-b19e-efa25af3f9fd
 
 ;;; org.el ends here
+