Browse Source

Make M-left and M-right affect only the item, not its children

This behavior is now parallel to the treatment of outline nodes.

This commit also introduces another change.  When an outline node or a
plain list item is folded by outline and contains hidden children,
M-left/right will refuse to act on this item.  You must either open
the tree, or use the subtree commands M-S-left and M-S-right.

Based on a patch by Matti De Craene, but significantly modified after
a discussion involving Bernt Hansen and others.
Carsten Dominik 15 years ago
parent
commit
11baa7cf77
5 changed files with 112 additions and 16 deletions
  1. 5 0
      doc/ChangeLog
  2. 5 0
      doc/org.texi
  3. 17 0
      lisp/ChangeLog
  4. 47 14
      lisp/org-list.el
  5. 38 2
      lisp/org.el

+ 5 - 0
doc/ChangeLog

@@ -1,3 +1,8 @@
+2010-04-23  Carsten Dominik  <carsten.dominik@gmail.com>
+
+	* org.texi (Plain lists): Document the commands to promote/demote
+	an item without affecting children.
+
 2010-04-22  Carsten Dominik  <carsten.dominik@gmail.com>
 
 	* org.texi (Sitemap): Document sitemap sorting.

+ 5 - 0
doc/org.texi

@@ -1321,6 +1321,11 @@ similar effect.
 Move the item including subitems up/down (swap with previous/next item
 of same indentation).  If the list is ordered, renumbering is
 automatic.
+@kindex M-@key{left}
+@kindex M-@key{right}
+@item M-@key{left}
+@itemx M-@key{right}
+Decrease/increase the indentation of an item, leaving children alone.
 @kindex M-S-@key{left}
 @kindex M-S-@key{right}
 @item M-S-@key{left}

+ 17 - 0
lisp/ChangeLog

@@ -1,5 +1,22 @@
 2010-04-23  Carsten Dominik  <carsten.dominik@gmail.com>
 
+	* org.el (org-shiftmetaleft, org-shiftmetaright): Call the subtree
+	indentation commands.
+	(org-hidden-tree-error): New defsubst.
+	(org-metaleft, org-metaright): Check for hidden stuff and throw an
+	error.
+	(org-check-for-hidden): New function.
+
+	* org-list.el (org-item-re): New function.
+	(org-at-item-p): Use `org-item-re'.
+	(org-end-of-item-text-before-children): New function.
+	(org-outdent-item, org-indent-item): Arrange for leaving the
+	subtree alone.
+	(org-outdent-item-tree, org-indent-item-tree): New argument
+	NO-SUBTREE.
+	(org-indent-item-tree): Use `org-end-of-item-text-before-children'
+	to find the end for processing while ignoring the subtree.
+
 	* org-publish.el (org-publish-sitemap-sort-alphabetically)
 	(org-publish-sitemap-sort-folders)
 	(org-publish-sitemap-sort-ignore-case): New options.

+ 47 - 14
lisp/org-list.el

@@ -197,17 +197,25 @@ list, obtained by prompting the user."
 
 ;;; Plain list items
 
+(defun org-item-re (&optional general)
+  "Return the correct regular expression for plain lists.
+If GENERAL is non-nil, return the general regexp independent of the value
+of `org-plain-list-ordered-item-terminator'."
+  (cond
+   ((or general (eq org-plain-list-ordered-item-terminator t))
+    "\\([ \t]*\\([-+]\\|\\([0-9]+[.)]\\)\\)\\|[ \t]+\\*\\)\\( \\|$\\)")
+   ((= org-plain-list-ordered-item-terminator ?.)
+    "\\([ \t]*\\([-+]\\|\\([0-9]+\\.\\)\\)\\|[ \t]+\\*\\)\\( \\|$\\)")
+   ((= org-plain-list-ordered-item-terminator ?\))
+    "\\([ \t]*\\([-+]\\|\\([0-9]+)\\)\\)\\|[ \t]+\\*\\)\\( \\|$\\)")
+   (t (error "Invalid value of `org-plain-list-ordered-item-terminator'"))))
+
 (defun org-at-item-p ()
   "Is point in a line starting a hand-formatted item?"
-  (let ((llt org-plain-list-ordered-item-terminator))
-    (save-excursion
-      (goto-char (point-at-bol))
-      (looking-at
-       (cond
-	((eq llt t)  "\\([ \t]*\\([-+]\\|\\([0-9]+[.)]\\)\\)\\|[ \t]+\\*\\)\\( \\|$\\)")
-	((= llt ?.)  "\\([ \t]*\\([-+]\\|\\([0-9]+\\.\\)\\)\\|[ \t]+\\*\\)\\( \\|$\\)")
-	((= llt ?\)) "\\([ \t]*\\([-+]\\|\\([0-9]+)\\)\\)\\|[ \t]+\\*\\)\\( \\|$\\)")
-	(t (error "Invalid value of `org-plain-list-ordered-item-terminator'")))))))
+
+  (save-excursion
+    (goto-char (point-at-bol))
+    (looking-at (org-item-re))))
 
 (defun org-at-item-bullet-p ()
   "Is point at the bullet of a plain list item?"
@@ -590,6 +598,16 @@ If the cursor is not in an item, throw an error."
       (goto-char pos)
       (error "Not in an item"))))
 
+(defun org-end-of-item-text-before-children ()
+  "Move to the end of the item text, stops before the first child if any.
+Assumes that the cursor is in the first ine of an item."
+  (goto-char
+   (min (save-excursion (org-end-of-item) (point))
+	(save-excursion
+	  (goto-char (point-at-eol))
+	  (re-search-forward (concat "^" (org-item-re t)) nil t)
+	  (match-beginning 0)))))
+
 (defun org-next-item ()
   "Move to the beginning of the next item in the current plain list.
 Error if not at a plain list, or if this is the last item in the list."
@@ -961,12 +979,24 @@ I.e. to the text after the last item."
 (defvar org-last-indent-end-marker (make-marker))
 
 (defun org-outdent-item (arg)
-  "Outdent a local list item."
+  "Outdent a local list item, but not its children."
   (interactive "p")
-  (org-indent-item (- arg)))
+  (org-indent-item-tree (- arg) 'no-subtree))
 
 (defun org-indent-item (arg)
-  "Indent a local list item."
+  "Indent a local list item, but not its children."
+  (interactive "p")
+  (org-indent-item-tree arg 'no-subtree))
+
+(defun org-outdent-item-tree (arg &optional no-subtree)
+  "Outdent a local list item including its children.
+If NO-SUBTREE is set, only outdend the item itself, not its children."
+  (interactive "p")
+  (org-indent-item-tree (- arg) no-subtree))
+
+(defun org-indent-item-tree (arg &optional no-subtree)
+  "Indent a local list item including its children.
+If NO-SUBTREE is set, only indent the item itself, not its children."
   (interactive "p")
   (and (org-region-active-p) (org-cursor-to-region-beginning))
   (unless (org-at-item-p)
@@ -975,12 +1005,15 @@ I.e. to the text after the last item."
     (setq firstp (org-first-list-item-p))
     (save-excursion
       (setq end (and (org-region-active-p) (region-end)))
-      (if (memq last-command '(org-shiftmetaright org-shiftmetaleft))
+      (if (and (memq last-command '(org-shiftmetaright org-shiftmetaleft))
+	       (memq this-command '(org-shiftmetaright org-shiftmetaleft)))
 	  (setq beg org-last-indent-begin-marker
 		end org-last-indent-end-marker)
 	(org-beginning-of-item)
 	(setq beg (move-marker org-last-indent-begin-marker (point)))
-	(org-end-of-item)
+	(if no-subtree
+	    (org-end-of-item-text-before-children)
+	  (org-end-of-item))
 	(setq end (move-marker org-last-indent-end-marker (or end (point)))))
       (goto-char beg)
       (setq ind-bul (org-item-indent-positions)

+ 38 - 2
lisp/org.el

@@ -15970,7 +15970,7 @@ See the individual commands for more information."
    ((run-hook-with-args-until-success 'org-shiftmetaleft-hook))
    ((org-at-table-p) (call-interactively 'org-table-delete-column))
    ((org-on-heading-p) (call-interactively 'org-promote-subtree))
-   ((org-at-item-p) (call-interactively 'org-outdent-item))
+   ((org-at-item-p) (call-interactively 'org-outdent-item-tree))
    (t (org-modifier-cursor-error))))
 
 (defun org-shiftmetaright ()
@@ -15983,7 +15983,7 @@ See the individual commands for more information."
    ((run-hook-with-args-until-success 'org-shiftmetaright-hook))
    ((org-at-table-p) (call-interactively 'org-table-insert-column))
    ((org-on-heading-p) (call-interactively 'org-demote-subtree))
-   ((org-at-item-p) (call-interactively 'org-indent-item))
+   ((org-at-item-p) (call-interactively 'org-indent-item-tree))
    (t (org-modifier-cursor-error))))
 
 (defun org-shiftmetaup (&optional arg)
@@ -16012,6 +16012,10 @@ commands for more information."
    ((org-at-item-p) (call-interactively 'org-move-item-down))
    (t (org-modifier-cursor-error))))
 
+(defsubst org-hidden-tree-error ()
+  (error
+   "Hidden subtree, open with TAB or use subtree command M-S-<left>/<right>"))
+
 (defun org-metaleft (&optional arg)
   "Promote heading or move table column to left.
 Calls `org-do-promote' or `org-table-move-column', depending on context.
@@ -16026,12 +16030,14 @@ See the individual commands for more information."
 	     (save-excursion
 	       (goto-char (region-beginning))
 	       (org-on-heading-p))))
+    (when (org-check-for-hidden 'headlines) (org-hidden-tree-error))
     (call-interactively 'org-do-promote))
    ((or (org-at-item-p)
 	(and (org-region-active-p)
 	     (save-excursion
 	       (goto-char (region-beginning))
 	       (org-at-item-p))))
+    (when (org-check-for-hidden 'items) (org-hidden-tree-error))
     (call-interactively 'org-outdent-item))
    (t (call-interactively 'backward-word))))
 
@@ -16049,15 +16055,45 @@ See the individual commands for more information."
 	     (save-excursion
 	       (goto-char (region-beginning))
 	       (org-on-heading-p))))
+    (when (org-check-for-hidden 'headlines) (org-hidden-tree-error))
     (call-interactively 'org-do-demote))
    ((or (org-at-item-p)
 	(and (org-region-active-p)
 	     (save-excursion
 	       (goto-char (region-beginning))
 	       (org-at-item-p))))
+    (when (org-check-for-hidden 'items) (org-hidden-tree-error))
     (call-interactively 'org-indent-item))
    (t (call-interactively 'forward-word))))
 
+(defun org-check-for-hidden (what)
+  "Check if there are hidden headlines/items in the current visual line.
+WHAT can be either `headlines' or `items'.  If the current line is
+an outline or item heading and it has a folded subtree below it,
+this fucntion returns t, nil otherwise."
+  (let ((re (cond
+	     ((eq what 'headlines) (concat "^" org-outline-regexp))
+	     ((eq what 'items) (concat "^" (org-item-re t)))
+	     (t (error "This should not happen"))))
+	beg end)
+    (save-excursion
+      (catch 'exit
+	(if (org-region-active-p)
+	    (setq beg (region-beginning) end (region-end))
+	  (setq beg (point-at-bol))
+	  (beginning-of-line 2)
+	  (while (and (not (eobp)) ;; this is like `next-line'
+		      (get-char-property (1- (point)) 'invisible))
+	    (beginning-of-line 2))
+	  (setq end (point)))
+	(goto-char beg)
+	(goto-char (point-at-eol))
+	(setq end (max end (point)))
+	(while (re-search-forward re end t)
+	  (if (get-char-property (match-beginning 0) 'invisible)
+	      (throw 'exit t)))
+	nil))))
+
 (defun org-metaup (&optional arg)
   "Move subtree up or move table row up.
 Calls `org-move-subtree-up' or `org-table-move-row' or