Function: org-toggle-item

org-toggle-item is an interactive and byte-compiled function defined in org-list.el.gz.

Signature

(org-toggle-item ARG)

Documentation

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.

Key Bindings

Source Code

;; Defined in /usr/src/emacs/lisp/org/org-list.el.gz
(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 ((extract-footnote-definitions
         (lambda (end)
           ;; Remove footnote definitions from point to END.
           ;; Return the list of the extracted definitions.
           (let (definitions element)
             (save-excursion
               (while (re-search-forward org-footnote-definition-re end t)
                 (setq element (org-element-at-point))
                 (when (eq 'footnote-definition
                           (org-element-type element))
                   (push (buffer-substring-no-properties
                          (org-element-property :begin element)
                          (org-element-property :end element))
                         definitions)
                   ;; Ensure at least 2 blank lines after the last
                   ;; footnote definition, thus not slurping the
                   ;; following element.
                   (unless (<= 2 (org-element-property
                                 :post-blank
                                 (org-element-at-point)))
                     (setf (car definitions)
                           (concat (car definitions)
                                   (make-string
                                    (- 2 (org-element-property
                                          :post-blank
                                          (org-element-at-point)))
                                    ?\n))))
                   (delete-region
                    (org-element-property :begin element)
                    (org-element-property :end element))))
               definitions))))
        (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-current-text-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-current-text-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")
             (line-beginning-position))))
	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 (line-beginning-position)
            end (copy-marker (line-end-position))))
    ;; 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 a heading: convert to items.
	((org-at-heading-p)
	 ;; Remove metadata
	 (let (org-loop-over-headlines-in-active-region)
	   (org-list--delete-metadata))
	 (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)))
                (footnote-definitions
                 (funcall extract-footnote-definitions end)))
	   (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 metadata
	       (let (org-loop-over-headlines-in-active-region)
		 (org-list--delete-metadata))
	       ;; Remove stars and TODO keyword.
	       (let ((case-fold-search nil)) (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)))))
           (when footnote-definitions
             (goto-char end)
             ;; Insert footnote definitions after the list.
             (unless (bolp) (beginning-of-line 2))
             ;; At (point-max).
             (unless (bolp) (insert "\n"))
             (dolist (def footnote-definitions)
               (insert def)))))
	;; 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-current-text-indentation))
                    (footnote-definitions
                     (funcall extract-footnote-definitions end)))
	       (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))
               (when footnote-definitions
                 ;; If the new list is followed by same-level items,
                 ;; move past them as well.
                 (goto-char (org-element-property
                             :end
                             (org-element-lineage
                              (org-element-at-point (1- end))
                              '(plain-list) t)))
                 ;; Insert footnote definitions after the list.
                 (unless (bolp) (beginning-of-line 2))
                 ;; At (point-max).
                 (unless (bolp) (insert "\n"))
                 (dolist (def footnote-definitions)
                   (insert def)))))
	;; 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))))))))