From ce2090ccfde4288d8306d108b92d6713b55ee2ab Mon Sep 17 00:00:00 2001 From: Nicolas Goaziou Date: Mon, 13 Oct 2014 19:03:14 +0200 Subject: [PATCH] Fix indentation in lists * lisp/org-list.el (org-list-item-body-column): Take into consideration empty items and bullets followed by two spaces. * lisp/org.el (org--get-expected-indentation): Fix return value for items in lists. (org-indent-region): Fix infloop when indenting some types of plain lists. Also fix error when region starts with blank lines at the beginning of the buffer. * testing/lisp/test-org.el (test-org/indent-region): Add tests. --- lisp/org-list.el | 23 +++++----- lisp/org.el | 90 +++++++++++++++++++++++++--------------- testing/lisp/test-org.el | 14 ++++++- 3 files changed, 83 insertions(+), 44 deletions(-) diff --git a/lisp/org-list.el b/lisp/org-list.el index 774d87f83..b1d47c995 100644 --- a/lisp/org-list.el +++ b/lisp/org-list.el @@ -2051,16 +2051,19 @@ Possible values are: `folded', `children' or `subtree'. See (defun org-list-item-body-column (item) "Return column at which body of ITEM should start." - (let (bpos bcol tpos tcol) - (save-excursion - (goto-char item) - (looking-at "[ \t]*\\(\\S-+\\)\\(.*[ \t]+::\\)?\\([ \t]+\\|$\\)") - (setq bpos (match-beginning 1) tpos (match-end 0) - bcol (progn (goto-char bpos) (current-column)) - tcol (progn (goto-char tpos) (current-column))) - (when (> tcol (+ bcol org-description-max-indent)) - (setq tcol (+ bcol 5)))) - tcol)) + (save-excursion + (goto-char item) + (looking-at "[ \t]*\\(\\S-+\\)\\(.*[ \t]+::\\)?\\([ \t]+\\|$\\)") + (if (match-beginning 2) + (let ((start (1+ (match-end 2))) + (ind (org-get-indentation))) + (if (> start (+ ind org-description-max-indent)) (+ ind 5) start)) + (+ (progn (goto-char (match-end 1)) (current-column)) + (if (and org-list-two-spaces-after-bullet-regexp + (org-string-match-p org-list-two-spaces-after-bullet-regexp + (match-string 1))) + 2 + 1))))) diff --git a/lisp/org.el b/lisp/org.el index d702cf5c6..157006331 100755 --- a/lisp/org.el +++ b/lisp/org.el @@ -22415,9 +22415,13 @@ ELEMENT." (if (not org-adapt-indentation) 0 (let ((level (org-current-level))) (if level (1+ level) 0)))) - ((item plain list) + (item (org-list-item-body-column (org-element-property :post-affiliated element))) + (plain-list + (save-excursion + (goto-char (org-element-property :post-affiliated element)) + (org-get-indentation))) (otherwise (goto-char start) (org-get-indentation)))) @@ -22439,19 +22443,25 @@ ELEMENT." (while t (if (= (point-min) start) (throw 'exit 0) (goto-char (1- start)) - (let ((previous (org-element-at-point))) - (while (let ((parent (org-element-property :parent previous))) - (and parent - (setq previous parent) - (<= (org-element-property :end parent) start)))) - (cond ((or (not previous) - (> (org-element-property :end previous) start)) - (throw 'exit (org--get-expected-indentation previous t))) - ((memq (org-element-type previous) - '(footnote-definition inlinetask)) - (setq start (org-element-property :begin previous))) - (t (goto-char (org-element-property :begin previous)) - (throw 'exit (org-get-indentation))))))))) + (let* ((previous (org-element-at-point)) + (parent previous)) + (while (and parent (<= (org-element-property :end parent) start)) + (setq previous parent + parent (org-element-property :parent parent))) + (cond + ((not previous) (throw 'exit 0)) + ((> (org-element-property :end previous) start) + (throw 'exit (org--get-expected-indentation previous t))) + ((memq (org-element-type previous) + '(footnote-definition inlinetask)) + (setq start (org-element-property :begin previous))) + (t (goto-char (org-element-property :begin previous)) + (throw 'exit + (if (bolp) (org-get-indentation) + ;; At first paragraph in an item or + ;; a footnote definition. + (org--get-expected-indentation + (org-element-property :parent previous) t)))))))))) ;; Otherwise, move to the first non-blank line above. (t (beginning-of-line) @@ -22584,13 +22594,14 @@ assumed to be significant there." (interactive "r") (save-excursion (goto-char start) - (beginning-of-line) + (skip-chars-forward " \r\t\n") + (unless (eobp) (beginning-of-line)) (let ((indent-to (lambda (ind pos) ;; Set IND as indentation for all lines between point and ;; POS or END, whichever comes first. Blank lines are ;; ignored. Leave point after POS once done. - (let ((limit (copy-marker (min end pos)))) + (let ((limit (copy-marker (min end pos)))) (while (< (point) limit) (unless (org-looking-at-p "[ \t]*$") (org-indent-line-to ind)) (forward-line)) @@ -22602,17 +22613,18 @@ assumed to be significant there." (type (org-element-type element)) (element-end (copy-marker (org-element-property :end element))) (ind (org--get-expected-indentation element nil))) - (if (or (memq type '(paragraph table table-row)) - (not (or (org-element-property :contents-begin element) - (memq type - '(example-block export-block src-block))))) - ;; Elements here are indented as a single block. Also - ;; align node properties. - (progn - (when (eq type 'node-property) - (org--align-node-property) - (beginning-of-line)) - (funcall indent-to ind element-end)) + (cond + ((or (memq type '(paragraph table table-row)) + (not (or (org-element-property :contents-begin element) + (memq type + '(example-block export-block src-block))))) + ;; Elements here are indented as a single block. Also + ;; align node properties. + (when (eq type 'node-property) + (org--align-node-property) + (beginning-of-line)) + (funcall indent-to ind element-end)) + (t ;; Elements in this category consist of three parts: ;; before the contents, the contents, and after the ;; contents. The contents are treated specially, @@ -22636,8 +22648,9 @@ assumed to be significant there." ;; from the second line. (org-with-wide-buffer (goto-char post) - (forward-line) - (point))) + (end-of-line) + (skip-chars-forward " \r\t\n") + (if (eobp) (point) (line-beginning-position)))) (t (org-element-property :contents-begin element))))) (cend (copy-marker (or (org-element-property :contents-end element) @@ -22646,13 +22659,24 @@ assumed to be significant there." (goto-char element-end) (skip-chars-backward " \r\t\n") (line-beginning-position)))))) - (funcall indent-to ind cbeg) + ;; Do not change items indentation individually as it + ;; might break the list as a whole. On the other + ;; hand, when at a plain list, indent it as a whole. + (cond ((eq type 'plain-list) + (let ((offset (- ind (org-get-indentation)))) + (unless (zerop offset) + (indent-rigidly (org-element-property :begin element) + (org-element-property :end element) + offset)) + (goto-char cbeg))) + ((eq type 'item) (goto-char cbeg)) + (t (funcall indent-to ind cbeg))) (when (< (point) end) (case type ((example-block export-block verse-block)) (src-block - ;; In a source block, indent source code according - ;; to language major mode, but only if + ;; In a source block, indent source code + ;; according to language major mode, but only if ;; `org-src-tab-acts-natively' is non-nil. (when (and (< (point) end) org-src-tab-acts-natively) (ignore-errors @@ -22667,7 +22691,7 @@ assumed to be significant there." (when (< (point) end) (funcall indent-to ind element-end))) (set-marker post nil) (set-marker cbeg nil) - (set-marker cend nil))) + (set-marker cend nil)))) (set-marker element-end nil)))) (set-marker end nil)))) diff --git a/testing/lisp/test-org.el b/testing/lisp/test-org.el index c91dcbeb9..6a103838c 100644 --- a/testing/lisp/test-org.el +++ b/testing/lisp/test-org.el @@ -655,15 +655,27 @@ (let ((org-property-format "%-10s %s")) (org-indent-region (point-min) (point-max))) (buffer-string)))) - ;; Special case: plain lists and footnote definitions. + ;; Indent plain lists. (should (equal "- A\n B\n - C\n\n D" (org-test-with-temp-text "- A\n B\n - C\n\n D" (org-indent-region (point-min) (point-max)) (buffer-string)))) + (should + (equal "- A\n\n- B" + (org-test-with-temp-text " - A\n\n - B" + (org-indent-region (point-min) (point-max)) + (buffer-string)))) + ;; Indent footnote definitions. (should (equal "[fn:1] Definition\n\nDefinition" (org-test-with-temp-text "[fn:1] Definition\n\n Definition" + (org-indent-region (point-min) (point-max)) + (buffer-string)))) + ;; Special case: Start indenting on a blank line. + (should + (equal "\nParagraph" + (org-test-with-temp-text "\n Paragraph" (org-indent-region (point-min) (point-max)) (buffer-string)))))