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>
 2010-04-22  Carsten Dominik  <carsten.dominik@gmail.com>
 
 
 	* org.texi (Sitemap): Document sitemap sorting.
 	* 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
 Move the item including subitems up/down (swap with previous/next item
 of same indentation).  If the list is ordered, renumbering is
 of same indentation).  If the list is ordered, renumbering is
 automatic.
 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{left}
 @kindex M-S-@key{right}
 @kindex M-S-@key{right}
 @item M-S-@key{left}
 @item M-S-@key{left}

+ 17 - 0
lisp/ChangeLog

@@ -1,5 +1,22 @@
 2010-04-23  Carsten Dominik  <carsten.dominik@gmail.com>
 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.el (org-publish-sitemap-sort-alphabetically)
 	(org-publish-sitemap-sort-folders)
 	(org-publish-sitemap-sort-folders)
 	(org-publish-sitemap-sort-ignore-case): New options.
 	(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
 ;;; 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 ()
 (defun org-at-item-p ()
   "Is point in a line starting a hand-formatted item?"
   "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 ()
 (defun org-at-item-bullet-p ()
   "Is point at the bullet of a plain list item?"
   "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)
       (goto-char pos)
       (error "Not in an item"))))
       (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 ()
 (defun org-next-item ()
   "Move to the beginning of the next item in the current plain list.
   "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."
 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))
 (defvar org-last-indent-end-marker (make-marker))
 
 
 (defun org-outdent-item (arg)
 (defun org-outdent-item (arg)
-  "Outdent a local list item."
+  "Outdent a local list item, but not its children."
   (interactive "p")
   (interactive "p")
-  (org-indent-item (- arg)))
+  (org-indent-item-tree (- arg) 'no-subtree))
 
 
 (defun org-indent-item (arg)
 (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")
   (interactive "p")
   (and (org-region-active-p) (org-cursor-to-region-beginning))
   (and (org-region-active-p) (org-cursor-to-region-beginning))
   (unless (org-at-item-p)
   (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))
     (setq firstp (org-first-list-item-p))
     (save-excursion
     (save-excursion
       (setq end (and (org-region-active-p) (region-end)))
       (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
 	  (setq beg org-last-indent-begin-marker
 		end org-last-indent-end-marker)
 		end org-last-indent-end-marker)
 	(org-beginning-of-item)
 	(org-beginning-of-item)
 	(setq beg (move-marker org-last-indent-begin-marker (point)))
 	(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)))))
 	(setq end (move-marker org-last-indent-end-marker (or end (point)))))
       (goto-char beg)
       (goto-char beg)
       (setq ind-bul (org-item-indent-positions)
       (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))
    ((run-hook-with-args-until-success 'org-shiftmetaleft-hook))
    ((org-at-table-p) (call-interactively 'org-table-delete-column))
    ((org-at-table-p) (call-interactively 'org-table-delete-column))
    ((org-on-heading-p) (call-interactively 'org-promote-subtree))
    ((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))))
    (t (org-modifier-cursor-error))))
 
 
 (defun org-shiftmetaright ()
 (defun org-shiftmetaright ()
@@ -15983,7 +15983,7 @@ See the individual commands for more information."
    ((run-hook-with-args-until-success 'org-shiftmetaright-hook))
    ((run-hook-with-args-until-success 'org-shiftmetaright-hook))
    ((org-at-table-p) (call-interactively 'org-table-insert-column))
    ((org-at-table-p) (call-interactively 'org-table-insert-column))
    ((org-on-heading-p) (call-interactively 'org-demote-subtree))
    ((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))))
    (t (org-modifier-cursor-error))))
 
 
 (defun org-shiftmetaup (&optional arg)
 (defun org-shiftmetaup (&optional arg)
@@ -16012,6 +16012,10 @@ commands for more information."
    ((org-at-item-p) (call-interactively 'org-move-item-down))
    ((org-at-item-p) (call-interactively 'org-move-item-down))
    (t (org-modifier-cursor-error))))
    (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)
 (defun org-metaleft (&optional arg)
   "Promote heading or move table column to left.
   "Promote heading or move table column to left.
 Calls `org-do-promote' or `org-table-move-column', depending on context.
 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
 	     (save-excursion
 	       (goto-char (region-beginning))
 	       (goto-char (region-beginning))
 	       (org-on-heading-p))))
 	       (org-on-heading-p))))
+    (when (org-check-for-hidden 'headlines) (org-hidden-tree-error))
     (call-interactively 'org-do-promote))
     (call-interactively 'org-do-promote))
    ((or (org-at-item-p)
    ((or (org-at-item-p)
 	(and (org-region-active-p)
 	(and (org-region-active-p)
 	     (save-excursion
 	     (save-excursion
 	       (goto-char (region-beginning))
 	       (goto-char (region-beginning))
 	       (org-at-item-p))))
 	       (org-at-item-p))))
+    (when (org-check-for-hidden 'items) (org-hidden-tree-error))
     (call-interactively 'org-outdent-item))
     (call-interactively 'org-outdent-item))
    (t (call-interactively 'backward-word))))
    (t (call-interactively 'backward-word))))
 
 
@@ -16049,15 +16055,45 @@ See the individual commands for more information."
 	     (save-excursion
 	     (save-excursion
 	       (goto-char (region-beginning))
 	       (goto-char (region-beginning))
 	       (org-on-heading-p))))
 	       (org-on-heading-p))))
+    (when (org-check-for-hidden 'headlines) (org-hidden-tree-error))
     (call-interactively 'org-do-demote))
     (call-interactively 'org-do-demote))
    ((or (org-at-item-p)
    ((or (org-at-item-p)
 	(and (org-region-active-p)
 	(and (org-region-active-p)
 	     (save-excursion
 	     (save-excursion
 	       (goto-char (region-beginning))
 	       (goto-char (region-beginning))
 	       (org-at-item-p))))
 	       (org-at-item-p))))
+    (when (org-check-for-hidden 'items) (org-hidden-tree-error))
     (call-interactively 'org-indent-item))
     (call-interactively 'org-indent-item))
    (t (call-interactively 'forward-word))))
    (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)
 (defun org-metaup (&optional arg)
   "Move subtree up or move table row up.
   "Move subtree up or move table row up.
 Calls `org-move-subtree-up' or `org-table-move-row' or
 Calls `org-move-subtree-up' or `org-table-move-row' or