Browse Source

Move `org-toggle-item' in "org-list.el"

* lisp/org.el (org-toggle-item): Move function ...
* lisp/org-list.el (org-toggle-item): ... here.

* testing/lisp/test-org-list.el (test-org-list/toggle-item): New test.
Nicolas Goaziou 8 years ago
parent
commit
610ec90cf4
3 changed files with 260 additions and 148 deletions
  1. 150 0
      lisp/org-list.el
  2. 0 148
      lisp/org.el
  3. 110 0
      testing/lisp/test-org-list.el

+ 150 - 0
lisp/org-list.el

@@ -87,11 +87,14 @@
 (defvar org-closed-string)
 (defvar org-deadline-string)
 (defvar org-description-max-indent)
+(defvar org-done-keywords)
 (defvar org-drawer-regexp)
 (defvar org-element-all-objects)
 (defvar org-inhibit-startup)
 (defvar org-odd-levels-only)
+(defvar org-outline-regexp-bol)
 (defvar org-scheduled-string)
+(defvar org-todo-line-regexp)
 (defvar org-ts-regexp)
 (defvar org-ts-regexp-both)
 
@@ -131,6 +134,7 @@
 		  (backend data &optional contents info))
 (declare-function org-fix-tags-on-the-fly "org" ())
 (declare-function org-get-indentation "org" (&optional line))
+(declare-function org-get-todo-state "org" ())
 (declare-function org-in-block-p "org" (names))
 (declare-function org-in-regexp "org" (re &optional nlines visually))
 (declare-function org-inlinetask-goto-beginning "org-inlinetask" ())
@@ -139,6 +143,7 @@
 (declare-function org-inlinetask-outline-regexp "org-inlinetask" ())
 (declare-function org-level-increment "org" ())
 (declare-function org-narrow-to-subtree "org" ())
+(declare-function org-outline-level "org" ())
 (declare-function org-previous-line-empty-p "org" ())
 (declare-function org-reduced-level "org" (L))
 (declare-function org-remove-indentation "org" (code &optional n))
@@ -2929,6 +2934,151 @@ ignores hidden links."
 	(run-hooks 'org-after-sorting-entries-or-items-hook)
 	(message "Sorting items...done")))))
 
+(defun org-toggle-item (arg)
+  "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 non blank line in the region is a headline, convert
+all headlines to items, shifting text accordingly.
+
+If it is an item, convert all items to normal lines.
+
+If it is normal text, change region into a list of items.
+With a prefix argument ARG, change the region in a single item."
+  (interactive "P")
+  (let ((shift-text
+	 (lambda (ind end)
+	   ;; Shift text in current section to IND, from point to END.
+	   ;; The function leaves point to END line.
+	   (let ((min-i 1000) (end (copy-marker end)))
+	     ;; First determine the minimum indentation (MIN-I) of
+	     ;; the text.
+	     (save-excursion
+	       (catch 'exit
+		 (while (< (point) end)
+		   (let ((i (org-get-indentation)))
+		     (cond
+		      ;; Skip blank lines and inline tasks.
+		      ((looking-at "^[ \t]*$"))
+		      ((looking-at org-outline-regexp-bol))
+		      ;; We can't find less than 0 indentation.
+		      ((zerop i) (throw 'exit (setq min-i 0)))
+		      ((< i min-i) (setq min-i i))))
+		   (forward-line))))
+	     ;; Then indent each line so that a line indented to
+	     ;; MIN-I becomes indented to IND.  Ignore blank lines
+	     ;; and inline tasks in the process.
+	     (let ((delta (- ind min-i)))
+	       (while (< (point) end)
+		 (unless (or (looking-at "^[ \t]*$")
+			     (looking-at org-outline-regexp-bol))
+		   (indent-line-to (+ (org-get-indentation) delta)))
+		 (forward-line))))))
+	(skip-blanks
+	 (lambda (pos)
+	   ;; Return beginning of first non-blank line, starting from
+	   ;; line at POS.
+	   (save-excursion
+	     (goto-char pos)
+	     (skip-chars-forward " \r\t\n")
+	     (point-at-bol))))
+	beg end)
+    ;; Determine boundaries of changes.
+    (if (org-region-active-p)
+	(setq beg (funcall skip-blanks (region-beginning))
+	      end (copy-marker (region-end)))
+      (setq beg (funcall skip-blanks (point-at-bol))
+	    end (copy-marker (point-at-eol))))
+    ;; Depending on the starting line, choose an action on the text
+    ;; between BEG and END.
+    (org-with-limited-levels
+     (save-excursion
+       (goto-char beg)
+       (cond
+	;; Case 1. Start at an item: de-itemize.  Note that it only
+	;;         happens when a region is active: `org-ctrl-c-minus'
+	;;         would call `org-cycle-list-bullet' otherwise.
+	((org-at-item-p)
+	 (while (< (point) end)
+	   (when (org-at-item-p)
+	     (skip-chars-forward " \t")
+	     (delete-region (point) (match-end 0)))
+	   (forward-line)))
+	;; Case 2. Start at an heading: convert to items.
+	((org-at-heading-p)
+	 (let* ((bul (org-list-bullet-string "-"))
+		(bul-len (length bul))
+		;; Indentation of the first heading.  It should be
+		;; relative to the indentation of its parent, if any.
+		(start-ind (save-excursion
+			     (cond
+			      ((not org-adapt-indentation) 0)
+			      ((not (outline-previous-heading)) 0)
+			      (t (length (match-string 0))))))
+		;; Level of first heading.  Further headings will be
+		;; compared to it to determine hierarchy in the list.
+		(ref-level (org-reduced-level (org-outline-level))))
+	   (while (< (point) end)
+	     (let* ((level (org-reduced-level (org-outline-level)))
+		    (delta (max 0 (- level ref-level)))
+		    (todo-state (org-get-todo-state)))
+	       ;; If current headline is less indented than the first
+	       ;; one, set it as reference, in order to preserve
+	       ;; subtrees.
+	       (when (< level ref-level) (setq ref-level level))
+	       ;; Remove stars and TODO keyword.
+	       (looking-at org-todo-line-regexp)
+	       (delete-region (point) (or (match-beginning 3)
+					  (line-end-position)))
+	       (insert bul)
+	       (indent-line-to (+ start-ind (* delta bul-len)))
+	       ;; Turn TODO keyword into a check box.
+	       (when todo-state
+		 (let* ((struct (org-list-struct))
+			(old (copy-tree struct)))
+		   (org-list-set-checkbox
+		    (line-beginning-position)
+		    struct
+		    (if (member todo-state org-done-keywords)
+			"[X]"
+		      "[ ]"))
+		   (org-list-write-struct struct
+					  (org-list-parents-alist struct)
+					  old)))
+	       ;; Ensure all text down to END (or SECTION-END) belongs
+	       ;; to the newly created item.
+	       (let ((section-end (save-excursion
+				    (or (outline-next-heading) (point)))))
+		 (forward-line)
+		 (funcall shift-text
+			  (+ start-ind (* (1+ delta) bul-len))
+			  (min end section-end)))))))
+	;; Case 3. Normal line with ARG: make the first line of region
+	;;         an item, and shift indentation of others lines to
+	;;         set them as item's body.
+	(arg (let* ((bul (org-list-bullet-string "-"))
+		    (bul-len (length bul))
+		    (ref-ind (org-get-indentation)))
+	       (skip-chars-forward " \t")
+	       (insert bul)
+	       (forward-line)
+	       (while (< (point) end)
+		 ;; Ensure that lines less indented than first one
+		 ;; still get included in item body.
+		 (funcall shift-text
+			  (+ ref-ind bul-len)
+			  (min end (save-excursion (or (outline-next-heading)
+						       (point)))))
+		 (forward-line))))
+	;; Case 4. Normal line without ARG: turn each non-item line
+	;;         into an item.
+	(t
+	 (while (< (point) end)
+	   (unless (or (org-at-heading-p) (org-at-item-p))
+	     (when (looking-at "\\([ \t]*\\)\\(\\S-\\)")
+	       (replace-match
+		(concat "\\1" (org-list-bullet-string "-") "\\2"))))
+	   (forward-line))))))))
 
 
 ;;; Send and receive lists

+ 0 - 148
lisp/org.el

@@ -21330,154 +21330,6 @@ Calls `org-table-insert-hline', `org-toggle-item', or
    (t
     (call-interactively 'org-toggle-item))))
 
-(defun org-toggle-item (arg)
-  "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 non blank line in the region is a headline, convert
-all headlines to items, shifting text accordingly.
-
-If it is an item, convert all items to normal lines.
-
-If it is normal text, change region into a list of items.
-With a prefix argument ARG, change the region in a single item."
-  (interactive "P")
-  (let ((shift-text
-	 (function
-	  ;; Shift text in current section to IND, from point to END.
-	  ;; The function leaves point to END line.
-	  (lambda (ind end)
-	    (let ((min-i 1000) (end (copy-marker end)))
-	      ;; First determine the minimum indentation (MIN-I) of
-	      ;; the text.
-	      (save-excursion
-		(catch 'exit
-		  (while (< (point) end)
-		    (let ((i (org-get-indentation)))
-		      (cond
-		       ;; Skip blank lines and inline tasks.
-		       ((looking-at "^[ \t]*$"))
-		       ((looking-at org-outline-regexp-bol))
-		       ;; We can't find less than 0 indentation.
-		       ((zerop i) (throw 'exit (setq min-i 0)))
-		       ((< i min-i) (setq min-i i))))
-		    (forward-line))))
-	      ;; Then indent each line so that a line indented to
-	      ;; MIN-I becomes indented to IND.  Ignore blank lines
-	      ;; and inline tasks in the process.
-	      (let ((delta (- ind min-i)))
-		(while (< (point) end)
-		  (unless (or (looking-at "^[ \t]*$")
-			      (looking-at org-outline-regexp-bol))
-		    (indent-line-to (+ (org-get-indentation) delta)))
-		  (forward-line)))))))
-	(skip-blanks
-	 (function
-	  ;; Return beginning of first non-blank line, starting from
-	  ;; line at POS.
-	  (lambda (pos)
-	    (save-excursion
-	      (goto-char pos)
-	      (skip-chars-forward " \r\t\n")
-	      (point-at-bol)))))
-	beg end)
-    ;; Determine boundaries of changes.
-    (if (org-region-active-p)
-	(setq beg (funcall skip-blanks (region-beginning))
-	      end (copy-marker (region-end)))
-      (setq beg (funcall skip-blanks (point-at-bol))
-	    end (copy-marker (point-at-eol))))
-    ;; Depending on the starting line, choose an action on the text
-    ;; between BEG and END.
-    (org-with-limited-levels
-     (save-excursion
-       (goto-char beg)
-       (cond
-	;; Case 1. Start at an item: de-itemize.  Note that it only
-	;;         happens when a region is active: `org-ctrl-c-minus'
-	;;         would call `org-cycle-list-bullet' otherwise.
-	((org-at-item-p)
-	 (while (< (point) end)
-	   (when (org-at-item-p)
-	     (skip-chars-forward " \t")
-	     (delete-region (point) (match-end 0)))
-	   (forward-line)))
-	;; Case 2. Start at an heading: convert to items.
-	((org-at-heading-p)
-	 (let* ((bul (org-list-bullet-string "-"))
-		(bul-len (length bul))
-		;; Indentation of the first heading.  It should be
-		;; relative to the indentation of its parent, if any.
-		(start-ind (save-excursion
-			     (cond
-			      ((not org-adapt-indentation) 0)
-			      ((not (outline-previous-heading)) 0)
-			      (t (length (match-string 0))))))
-		;; Level of first heading.  Further headings will be
-		;; compared to it to determine hierarchy in the list.
-		(ref-level (org-reduced-level (org-outline-level))))
-	   (while (< (point) end)
-	     (let* ((level (org-reduced-level (org-outline-level)))
-		    (delta (max 0 (- level ref-level)))
-		    (todo-state (org-get-todo-state)))
-	       ;; If current headline is less indented than the first
-	       ;; one, set it as reference, in order to preserve
-	       ;; subtrees.
-	       (when (< level ref-level) (setq ref-level level))
-	       ;; Remove stars and TODO keyword.
-	       (looking-at org-todo-line-regexp)
-	       (delete-region (point) (or (match-beginning 3)
-					  (line-end-position)))
-	       (insert bul)
-	       (indent-line-to (+ start-ind (* delta bul-len)))
-	       ;; Turn TODO keyword into a check box.
-	       (when todo-state
-		 (let* ((struct (org-list-struct))
-			(old (copy-tree struct)))
-		   (org-list-set-checkbox
-		    (line-beginning-position)
-		    struct
-		    (if (member todo-state org-done-keywords)
-			"[X]"
-		      "[ ]"))
-		   (org-list-write-struct struct
-					  (org-list-parents-alist struct)
-					  old)))
-	       ;; Ensure all text down to END (or SECTION-END) belongs
-	       ;; to the newly created item.
-	       (let ((section-end (save-excursion
-				    (or (outline-next-heading) (point)))))
-		 (forward-line)
-		 (funcall shift-text
-			  (+ start-ind (* (1+ delta) bul-len))
-			  (min end section-end)))))))
-	;; Case 3. Normal line with ARG: make the first line of region
-	;;         an item, and shift indentation of others lines to
-	;;         set them as item's body.
-	(arg (let* ((bul (org-list-bullet-string "-"))
-		    (bul-len (length bul))
-		    (ref-ind (org-get-indentation)))
-	       (skip-chars-forward " \t")
-	       (insert bul)
-	       (forward-line)
-	       (while (< (point) end)
-		 ;; Ensure that lines less indented than first one
-		 ;; still get included in item body.
-		 (funcall shift-text
-			  (+ ref-ind bul-len)
-			  (min end (save-excursion (or (outline-next-heading)
-						       (point)))))
-		 (forward-line))))
-	;; Case 4. Normal line without ARG: turn each non-item line
-	;;         into an item.
-	(t
-	 (while (< (point) end)
-	   (unless (or (org-at-heading-p) (org-at-item-p))
-	     (when (looking-at "\\([ \t]*\\)\\(\\S-\\)")
-	       (replace-match
-		(concat "\\1" (org-list-bullet-string "-") "\\2"))))
-	   (forward-line))))))))
-
 (defun org-toggle-heading (&optional nstars)
   "Convert headings to normal text, or items or text to headings.
 If there is no active region, only convert the current line.

+ 110 - 0
testing/lisp/test-org-list.el

@@ -869,6 +869,116 @@
 		   (buffer-string)))))
 
 
+
+;;; Miscellaneous
+
+(ert-deftest test-org-list/toggle-item ()
+  "Test `org-toggle-item' specifications."
+  ;; Convert normal lines to items.
+  (should
+   (equal "- line"
+	  (org-test-with-temp-text "line"
+	    (org-toggle-item nil)
+	    (buffer-string))))
+  ;; Convert items to normal lines.
+  (should
+   (equal "line"
+	  (org-test-with-temp-text "- line"
+	    (org-toggle-item nil)
+	    (buffer-string))))
+  ;; Convert headlines to items.
+  (should
+   (equal "- line"
+	  (org-test-with-temp-text "* line"
+	    (org-toggle-item nil)
+	    (buffer-string))))
+  ;; When converting a headline to a list item, TODO keywords become
+  ;; checkboxes.
+  (should
+   (equal "- [X] line"
+	  (org-test-with-temp-text "* DONE line"
+	    (org-toggle-item nil)
+	    (buffer-string))))
+  (should
+   (equal "- [ ] line"
+	  (org-test-with-temp-text "* TODO line"
+	    (org-toggle-item nil)
+	    (buffer-string))))
+  ;; When a region is marked and first line is a headline, all
+  ;; headlines are turned into items.
+  (should
+   (equal "- H1\n  - H2"
+	  (org-test-with-temp-text "* H1\n** H2"
+	    (transient-mark-mode 1)
+	    (push-mark (point) t t)
+	    (goto-char (point-max))
+	    (org-toggle-item nil)
+	    (buffer-string))))
+  (should
+   (equal "- [ ] H1\n  - [ ] H2"
+	  (org-test-with-temp-text "* TODO H1\n** TODO H2"
+	    (transient-mark-mode 1)
+	    (push-mark (point) t t)
+	    (goto-char (point-max))
+	    (org-toggle-item nil)
+	    (buffer-string))))
+  ;; When turning headlines into items, make sure headings contents
+  ;; are kept within items.
+  (should
+   (equal "- H1\n  Text"
+	  (org-test-with-temp-text "* H1\nText"
+	    (transient-mark-mode 1)
+	    (push-mark (point) t t)
+	    (goto-char (point-max))
+	    (org-toggle-item nil)
+	    (buffer-string))))
+  ;; When a region is marked and first line is an item, all items are
+  ;; turned into normal lines.
+  (should
+   (equal "1\n  2"
+	  (org-test-with-temp-text "- 1\n  - 2"
+	    (transient-mark-mode 1)
+	    (push-mark (point) t t)
+	    (goto-char (point-max))
+	    (org-toggle-item nil)
+	    (buffer-string))))
+  (should
+   (equal "1\n2"
+	  (org-test-with-temp-text "- 1\n2"
+	    (transient-mark-mode 1)
+	    (push-mark (point) t t)
+	    (goto-char (point-max))
+	    (org-toggle-item nil)
+	    (buffer-string))))
+  ;; When a region is marked and first line is an item, all normal
+  ;; lines are turned into items.
+  (should
+   (equal "- line 1\n- line 2"
+	  (org-test-with-temp-text "line 1\nline 2"
+	    (transient-mark-mode 1)
+	    (push-mark (point) t t)
+	    (goto-char (point-max))
+	    (org-toggle-item nil)
+	    (buffer-string))))
+  (should
+   (equal "- line 1\n- line 2"
+	  (org-test-with-temp-text "line 1\n- line 2"
+	    (transient-mark-mode 1)
+	    (push-mark (point) t t)
+	    (goto-char (point-max))
+	    (org-toggle-item nil)
+	    (buffer-string))))
+  ;; When argument ARG is non-nil, change the whole region into
+  ;; a single item.
+  (should
+   (equal "- line 1\n  line 2"
+	  (org-test-with-temp-text "line 1\nline 2"
+	    (transient-mark-mode 1)
+	    (push-mark (point) t t)
+	    (goto-char (point-max))
+	    (org-toggle-item t)
+	    (buffer-string)))))
+
 
 ;;; Radio Lists