9

Q: how can I format/fill poorly-formatted items in an org-mode bulleted list?

I'm trying to figure out how to make fill-paragraph work on poorly formatted bulleted items in an org-mode list. By "poorly formatted," I mean long-ish bullets that use multiple lines but need to be cleaned up to make them readable. The difficulty is that org-mode does not seem to recognize subsequent lines as part of the same bullet when the first non-whitespace character is in the same column as the bullet itself.

Hence, in the example below, bullet (1) does not need filling, bullets (2) and (3) need filling and respond as expected to fill-paragraph, but bullets (4) and (5) do not fill as expected:

* a header 1) a simple bullet 2) a bullet that responds nicely to fill-paragraph (try it!) 3) another bullet that also responds nicely to fill-paragraph even though the "t" in "to" and the "e" in "even" line up with the space in between the "4)" and the start of the bullet 4) a bullet that does NOT respond to fill-paragraph because the "N" in "NOT" is in the same column as the start of the bullet 5) a similar problem: the text is flush entirely to the left 

First of all, why does this happen? Second, and more to the point, how can I convince org-mode to treat all of the lines up to the next bullet as part of the same bullet for fill-paragraph purposes?

3 Answers 3

10

It happens because Org uses indentation to define text as belonging to an item.

(info "(org)Plain lists") Items belonging to the same list must have the same indentation on the first line. 

To convince Org to do what you want, you need to get the text indented to the level of the item. You can use C-x r t (string-rectangle) or C-x C-i (indent-rigidly) to do this and then filling will work like in #2 and #3.

If you are on the item line and you want to enter text below it, you can use C-j (org-return-indent) to move to the same indentation on the next line. That way, you can avoid formatting like in #4 and #5.

2

As @KyleMeyer explains in his answer, org-mode does not consider text to be part of a list item if it is not indented past the level of the corresponding bullet. So essentially, we need a way to absorb paragraphs into list items.

With point positioned inside a list item, the following command lets you do that without having to take care of indentation manually:

(defun org-back-to-item () (re-search-backward "^ *[-+*]\\|^ *[1-9]+[)\.] " nil nil 1)) (defun org-fill-paragraph-handle-lists (&optional num-paragraphs) (interactive "p") (save-excursion (let ((bound (if mark-active (- (region-end) 2) (progn (org-back-to-item) (while (>= num-paragraphs 0) (call-interactively 'org-mark-element) (setq num-paragraphs (1- num-paragraphs))) (- (region-end) 2))))) (while (search-forward "\n" bound t) (replace-match " "))) (org-fill-paragraph))) (define-key org-mode-map (kbd "C-M-q") 'org-fill-paragraph-handle-lists) 

The main idea is to replace occurrences of \n with SPC in the list item to fill before calling org-fill-paragraph on it.

Note that if you have more than one paragraph inside a list item:

 ... 4) a bullet that does NOT respond to fill-paragraph because the "N" in "NOT" is in the same column as the start of the bullet Some more text 5) ... 

you can make Some more text (the second paragraph) become part of the list item by calling the command like this:

M-2 C-M-q

More specifically, org-fill-paragraph-handle-lists will absorb a single paragraph below a list item by default, but can be instructed to absorb any number of paragraphs by calling it with a numeric prefix arg.

2
  • Clever! There's one point that's not quite working yet. If point is in the text that org would, by default, not consider part of the item (e.g., if we're on the "NOT..." line in the example), it does not fill as intended -- that is, it fills as a separate paragraph and slurps up item 5) as well. The issue seems to be in how org-mark-element chooses the element. Will give it some more thought. Commented Dec 11, 2014 at 13:11
  • @Dan This issue can be addressed by defining a function that moves point back to the previous list item, and calling it before marking paragraphs. See the updated code in my answer. Commented Dec 11, 2014 at 14:22
0
  1. repeat: go to the next line. If next line is not blank, not list and just a text, replace \n with ' '
  2. finally fill-paragraph
(defun current-line-blank () "Return non-nil if line is empty line." (eq (progn (end-of-line) (point)) (progn (beginning-of-line) (point)) )) (defun current-line-list () "Return boolean, non-nil if line is a list in Org mode." (or (eq (org-element-type (org-element-property :parent (org-element-at-point))) 'plain-list) (eq (org-element-type (org-element-at-point)) 'plain-list))) (defun my/fill-paragraph-list () "Fix for list in Org mode. Properly apply fill-paragraph in Org mode." (interactive) ;; go backward - cases: 1 at list, 2 uder list, 3 at paragraph (when (not (current-line-list)) ; 1 (forward-line -1) (while (let ((r (and (not (current-line-blank)) (not (current-line-list)) ; 2 (eq (org-element-type (org-element-at-point)) 'paragraph)))) r) (forward-line -1)) (if (or (current-line-blank) (not (current-line-list))) ; 3, 2 (forward-line))) ;; go forward (let ((v t)) (while v (search-forward "\n" nil t) (setq v (and (not (current-line-blank)) (not (current-line-list)) (eq (org-element-type (org-element-at-point)) 'paragraph))) (if v (replace-match " ")) )) (forward-line -1) (org-fill-paragraph)) 

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.