瀏覽代碼

Add level/indentation cycling for empty entries/items

Carsten Dominik 15 年之前
父節點
當前提交
e1d0f342a1
共有 5 個文件被更改,包括 127 次插入15 次删除
  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>
 2009-10-31  Carsten Dominik  <carsten.dominik@gmail.com>
 
 
 	* orgcard.tex: Document the new archiving keys.
 	* 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
 Insert new TODO entry with same level as current heading.  Like
 @kbd{C-@key{RET}}, the new headline will be inserted after the current
 @kbd{C-@key{RET}}, the new headline will be inserted after the current
 subtree.
 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}
 @kindex M-@key{left}
 @item M-@key{left}
 @item M-@key{left}
 Promote current heading by one level.
 Promote current heading by one level.
@@ -1286,6 +1292,12 @@ bullet, a bullet is added to the current line.
 @kindex M-S-@key{RET}
 @kindex M-S-@key{RET}
 @item M-S-@key{RET}
 @item M-S-@key{RET}
 Insert a new item with a checkbox (@pxref{Checkboxes}).
 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{up}
 @kindex S-@key{down}
 @kindex S-@key{down}
 @item S-@key{up}
 @item S-@key{up}

+ 6 - 0
lisp/ChangeLog

@@ -1,5 +1,11 @@
 2009-11-03  Carsten Dominik  <carsten.dominik@gmail.com>
 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
 	* org.el (org-refile): Make prefix argument 2 refile to current
 	clock.
 	clock.
 	(org-priority): Interpret action `remove' as call to remove the
 	(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-goto-line line)
     (org-move-to-column col)))
     (org-move-to-column col)))
 
 
+(defvar org-suppress-item-indentation) ; dynamically scoped parameter
 (defun org-fix-bullet-type (&optional force-bullet)
 (defun org-fix-bullet-type (&optional force-bullet)
   "Make sure all items in this list have the same bullet as the first item.
   "Make sure all items in this list have the same bullet as the first item.
 Also, fix the indentation."
 Also, fix the indentation."
@@ -874,7 +875,8 @@ Also, fix the indentation."
 	  (looking-at "\\S-+ *")
 	  (looking-at "\\S-+ *")
 	  (setq oldbullet (match-string 0))
 	  (setq oldbullet (match-string 0))
 	  (unless (equal bullet oldbullet) (replace-match bullet))
 	  (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-goto-line line)
     (org-move-to-column col)
     (org-move-to-column col)
     (if (string-match "[0-9]" bullet)
     (if (string-match "[0-9]" bullet)
@@ -882,19 +884,20 @@ Also, fix the indentation."
 
 
 (defun org-shift-item-indentation (delta)
 (defun org-shift-item-indentation (delta)
   "Shift the indentation in current item by 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 ()
 (defun org-beginning-of-item-list ()
   "Go to the beginning of the current 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-up bullet-up)
 	  (cons ind-down bullet-down))))
 	  (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 ()
 (defun org-get-bullet ()
   (save-excursion
   (save-excursion
     (goto-char (point-at-bol))
     (goto-char (point-at-bol))

+ 64 - 1
lisp/org.el

@@ -680,6 +680,21 @@ of the buffer."
   :group 'org-cycle
   :group 'org-cycle
   :type 'boolean)
   :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
 (defcustom org-cycle-emulate-tab t
   "Where should `org-cycle' emulate TAB.
   "Where should `org-cycle' emulate TAB.
 nil         Never
 nil         Never
@@ -4953,7 +4968,10 @@ in special contexts.
   But only if also the variable `org-cycle-global-at-bob' is t."
   But only if also the variable `org-cycle-global-at-bob' is t."
   (interactive "P")
   (interactive "P")
   (org-load-modules-maybe)
   (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
     (let* ((limit-level
 	    (or org-cycle-max-level
 	    (or org-cycle-max-level
 		(and (boundp 'org-inlinetask-min-level)
 		(and (boundp 'org-inlinetask-min-level)
@@ -5040,6 +5058,9 @@ in special contexts.
 
 
        ((org-try-cdlatex-tab))
        ((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)
        ((and (eq org-cycle-emulate-tab 'exc-hl-bol)
 	     (or (not (bolp))
 	     (or (not (bolp))
 		 (not (looking-at outline-regexp))))
 		 (not (looking-at outline-regexp))))
@@ -6077,6 +6098,16 @@ in the region."
 	    ((eolp) (insert " "))
 	    ((eolp) (insert " "))
 	    ((equal (char-after) ?\ ) (forward-char 1))))))
 	    ((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)
 (defun org-reduced-level (l)
   "Compute the effective level of a heading.
   "Compute the effective level of a heading.
 This takes into account the setting of `org-odd-levels-only'."
 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))
     (if org-adapt-indentation (org-fixup-indentation diff))
     (run-hooks 'org-after-demote-entry-hook)))
     (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)
 (defun org-map-tree (fun)
   "Call FUN for every heading underneath the current one."
   "Call FUN for every heading underneath the current one."
   (org-back-to-heading)
   (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
 not visibility should be done because of current context.  This is probably
 the place where a package like yasnippets can hook in.")
 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
 (defvar org-metaleft-hook nil
   "Hook for functions attaching themselves to `M-left'.
   "Hook for functions attaching themselves to `M-left'.
 See `org-ctrl-c-ctrl-c-hook' for more information.")
 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
 ;; arch-tag: e77da1a7-acc7-4336-b19e-efa25af3f9fd
 
 
 ;;; org.el ends here
 ;;; org.el ends here
+