summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNicolas Goaziou <n.goaziou@gmail.com>2011-02-12 16:42:42 +0100
committerNicolas Goaziou <n.goaziou@gmail.com>2011-02-18 13:43:49 +0100
commit713262edc1ba3703e60967873e202a1f09ba3789 (patch)
treeb0d1f4b529735a7dda9f10e17834ee41c3e8022d
parentde3d3652bb4ebea4d794db9ae9394af7c28fd9db (diff)
downloadorg-mode-713262edc1ba3703e60967873e202a1f09ba3789.tar.gz
Preserve hierarchy when converting items to headlines and the other way
* lisp/org.el (org-toggle-item, org-toggle-heading): make sure every sub-item in a list is changed into a sub-heading and sub-headings are translated into sub-items. Also ignore inline tasks in the process. org-toggle-item on headlines preserves hierarchy
-rw-r--r--lisp/org.el221
1 files changed, 129 insertions, 92 deletions
diff --git a/lisp/org.el b/lisp/org.el
index 41019c7..b92186a 100644
--- a/lisp/org.el
+++ b/lisp/org.el
@@ -17571,80 +17571,93 @@ Calls `org-table-insert-hline', `org-toggle-item', or
"Convert headings or normal lines to items, items to normal lines.
If there is no active region, only the current line is considered.
-If the first line in the region is a headline, convert all
-headlines to items.
+If the first non blank line in the region is an headline, convert
+all headlines to items.
-If the first line in the region is an item, convert all items to
-normal lines.
+If it is an item, convert all items to normal lines.
-If the first line is normal text, change region into an
-item. With a prefix argument ARG, change each line in region into
-an item."
+If it is normal text, change region into an item. With a prefix
+argument ARG, change each line in region into an item."
(interactive "P")
(let (l2 l beg end)
(if (org-region-active-p)
(setq beg (region-beginning) end (region-end))
(setq beg (point-at-bol)
end (min (1+ (point-at-eol)) (point-max))))
- (save-excursion
- (goto-char end)
- (setq l2 (org-current-line))
- (goto-char beg)
- (beginning-of-line 1)
- ;; Ignore blank lines at beginning of region
- (skip-chars-forward " \t\r\n")
- (beginning-of-line 1)
- (setq l (1- (org-current-line)))
- (if (org-at-item-p)
- ;; We already have items, de-itemize
- (while (< (setq l (1+ l)) l2)
- (when (org-at-item-p)
- (skip-chars-forward " \t")
- (delete-region (point) (match-end 0)))
- (beginning-of-line 2))
- (if (org-on-heading-p)
- ;; Headings, convert to items
- (while (< (setq l (1+ l)) l2)
- (if (looking-at org-outline-regexp)
- (replace-match (org-list-bullet-string "-") t t))
- (beginning-of-line 2))
- ;; normal lines, with ARG, turn all of them into items
- ;; unless they are already one.
- (if arg
- (while (< (setq l (1+ l)) l2)
- (unless (org-at-item-p)
- (if (looking-at "\\([ \t]*\\)\\(\\S-\\)")
- (replace-match
- (concat "\\1" (org-list-bullet-string "-") "\\2"))))
- (beginning-of-line 2))
- ;; Without ARG, make the first line of region an item, and
- ;; shift indentation of others lines to set them as item's
- ;; body.
- (let* ((bul (org-list-bullet-string "-"))
- (bul-len (length bul))
- (ref-ind (org-get-indentation)))
- (skip-chars-forward " \t")
- (insert bul)
- (beginning-of-line 2)
- (while (and (< (setq l (1+ l)) l2) (< (point) end))
- ;; Ensure that lines less indented than first one
- ;; still get included in item body.
- (org-indent-line-to (+ (max ref-ind (org-get-indentation))
- bul-len))
- (beginning-of-line 2)))))))))
+ (org-with-limited-levels
+ (save-excursion
+ (goto-char end)
+ (setq l2 (org-current-line))
+ (goto-char beg)
+ (beginning-of-line 1)
+ ;; Ignore blank lines at beginning of region
+ (skip-chars-forward " \t\r\n")
+ (beginning-of-line 1)
+ (setq l (1- (org-current-line)))
+ (cond
+ ;; Case 1. Start at an item: de-itemize.
+ ((org-at-item-p)
+ (while (< (setq l (1+ l)) l2)
+ (when (org-at-item-p)
+ (skip-chars-forward " \t")
+ (delete-region (point) (match-end 0)))
+ (beginning-of-line 2)))
+ ;; Case 2. Start an an heading: convert to items.
+ ((org-on-heading-p)
+ (let* ((bul (org-list-bullet-string "-"))
+ (len (length bul))
+ (ind 0) (level 0))
+ (while (< (setq l (1+ l)) l2)
+ (cond
+ ((looking-at outline-regexp)
+ (let* ((lvl (org-reduced-level
+ (- (length (match-string 0)) 2)))
+ (s (concat (make-string (* len lvl) ? ) bul)))
+ (replace-match s t t)
+ (setq ind (length s) level lvl)))
+ ;; Ignore blank lines and inline tasks.
+ ((looking-at "^[ \t]*$"))
+ ((looking-at "^\\*+ "))
+ ;; Ensure normal text belongs to the new item.
+ (t (org-indent-line-to (+ (max (- (org-get-indentation) level 2) 0)
+ ind))))
+ (beginning-of-line 2))))
+ ;; Case 3. Normal line with ARG: turn each of them into items
+ ;; unless they are already one.
+ (arg
+ (while (< (setq l (1+ l)) l2)
+ (unless (or (org-on-heading-p) (org-at-item-p))
+ (if (looking-at "\\([ \t]*\\)\\(\\S-\\)")
+ (replace-match
+ (concat "\\1" (org-list-bullet-string "-") "\\2"))))
+ (beginning-of-line 2)))
+ ;; Case 4. Normal line without ARG: make the first line of
+ ;; region an item, and shift indentation of others
+ ;; lines to set them as item's body.
+ (t (let* ((bul (org-list-bullet-string "-"))
+ (bul-len (length bul))
+ (ref-ind (org-get-indentation)))
+ (skip-chars-forward " \t")
+ (insert bul)
+ (beginning-of-line 2)
+ (while (and (< (setq l (1+ l)) l2) (< (point) end))
+ ;; Ensure that lines less indented than first one
+ ;; still get included in item body.
+ (org-indent-line-to (+ (max ref-ind (org-get-indentation))
+ bul-len))
+ (beginning-of-line 2)))))))))
(defun org-toggle-heading (&optional nstars)
"Convert headings to normal text, or items or text to headings.
If there is no active region, only the current line is considered.
-If the first line is a heading, remove the stars from all headlines
-in the region.
+If the first non blank line is an headline, remove the stars from
+all headlines in the region.
-If the first line is a plain list item, turn all plain list items
-into headings.
+If it is a plain list item, turn all plain list items into headings.
-If the first line is a normal line, turn each and every line in the
-region into a heading.
+If it is a normal line, turn each and every normal line (i.e. not
+an heading or an item) in the region into a heading.
When converting a line into a heading, the number of stars is chosen
such that the lines become children of the current entry. However,
@@ -17653,41 +17666,65 @@ stars to add."
(interactive "P")
(let (l2 l itemp beg end)
(if (org-region-active-p)
- (setq beg (region-beginning) end (region-end))
+ (setq beg (region-beginning) end (copy-marker (region-end)))
(setq beg (point-at-bol)
end (min (1+ (point-at-eol)) (point-max))))
- (save-excursion
- (goto-char end)
- (setq l2 (org-current-line))
- (goto-char beg)
- (beginning-of-line 1)
- (setq l (1- (org-current-line)))
- (if (org-on-heading-p)
- ;; We already have headlines, de-star them
- (while (< (setq l (1+ l)) l2)
- (when (org-on-heading-p t)
- (and (looking-at outline-regexp) (replace-match "")))
- (beginning-of-line 2))
- (setq itemp (org-at-item-p))
- (let* ((stars
- (if nstars
- (make-string (prefix-numeric-value current-prefix-arg)
- ?*)
- (save-excursion
- (if (re-search-backward org-complex-heading-regexp nil t)
- (match-string 1) ""))))
- (add-stars (cond (nstars "")
- ((equal stars "") "*")
- (org-odd-levels-only "**")
- (t "*")))
- (rpl (concat stars add-stars " ")))
- (while (< (setq l (1+ l)) l2)
- (if itemp
- (and (org-at-item-p) (replace-match rpl t t))
- (unless (org-on-heading-p)
- (if (looking-at "\\([ \t]*\\)\\(\\S-\\)")
- (replace-match (concat rpl (match-string 2))))))
- (beginning-of-line 2)))))))
+ ;; Ensure inline tasks don't count as headings.
+ (org-with-limited-levels
+ (save-excursion
+ (goto-char end)
+ (setq l2 (org-current-line))
+ (goto-char beg)
+ (beginning-of-line 1)
+ ;; Ignore blank lines at beginning of region
+ (skip-chars-forward " \t\r\n")
+ (beginning-of-line 1)
+ (setq l (1- (org-current-line)))
+ (cond
+ ;; Case 1. Started at an heading: de-star headings.
+ ((org-on-heading-p)
+ (while (< (setq l (1+ l)) l2)
+ (when (org-on-heading-p t)
+ (looking-at outline-regexp) (replace-match ""))
+ (beginning-of-line 2)))
+ ;; Case 2. Started at an item: change items into headlines.
+ ((org-at-item-p)
+ (let ((stars (make-string
+ (if nstars
+ (prefix-numeric-value current-prefix-arg)
+ (or (org-current-level) 0))
+ ?*)))
+ (while (< (point) end)
+ (when (org-at-item-p)
+ ;; Pay attention to cases when region ends before list.
+ (let* ((struct (org-list-struct))
+ (list-end (min (org-list-get-bottom-point struct) end)))
+ (save-restriction
+ (narrow-to-region (point) list-end)
+ (insert
+ (org-list-to-subtree
+ (org-list-parse-list t)
+ '(:istart (concat stars (funcall get-stars depth))
+ :icount (concat stars
+ (funcall get-stars depth))))))))
+ (beginning-of-line 2))))
+ ;; Case 3. Started at normal text: make every line an heading,
+ ;; skipping headlines and items.
+ (t (let* ((stars (make-string
+ (if nstars
+ (prefix-numeric-value current-prefix-arg)
+ (or (org-current-level) 0))
+ ?*))
+ (add-stars (cond (nstars "")
+ ((equal stars "") "*")
+ (org-odd-levels-only "**")
+ (t "*")))
+ (rpl (concat stars add-stars " ")))
+ (while (< (setq l (1+ l)) l2)
+ (unless (or (org-on-heading-p) (org-at-item-p))
+ (when (looking-at "\\([ \t]*\\)\\(\\S-\\)")
+ (replace-match (concat rpl (match-string 2)))))
+ (beginning-of-line 2)))))))))
(defun org-meta-return (&optional arg)
"Insert a new heading or wrap a region in a table.