From 09aaee6e1bdc5bec6e57b9c0a12ac1d6ea5867d8 Mon Sep 17 00:00:00 2001 From: Nicolas Goaziou Date: Sun, 27 Apr 2014 16:48:02 +0200 Subject: [PATCH 2/3] Rewrite `org-indent-region' * lisp/org.el (org-indent-region): Update function according to recent `org-indent-line' change. Optimize it. * testing/lisp/test-org.el (test-org/indent-region): New test. --- lisp/org.el | 105 +++++++++++++++++++++++++++++++++++++++++++---- testing/lisp/test-org.el | 63 ++++++++++++++++++++++++++++ 2 files changed, 161 insertions(+), 7 deletions(-) diff --git a/lisp/org.el b/lisp/org.el index f70c9c2..0ea5bc8 100644 --- a/lisp/org.el +++ b/lisp/org.el @@ -22410,15 +22410,106 @@ Also align node properties according to `org-property-format'." (message "Block at point indented")) (defun org-indent-region (start end) - "Indent region." + "Indent each non-blank line in the region. +Called from a program, START and END specify the region to +indent. The function will not indent contents of example blocks, +verse blocks and export blocks as leading white spaces are +assumed to be significant there." (interactive "r") (save-excursion - (let ((line-end (org-current-line end))) - (goto-char start) - (while (< (org-current-line) line-end) - (cond ((org-in-src-block-p t) (org-src-native-tab-command-maybe)) - (t (call-interactively 'org-indent-line))) - (move-beginning-of-line 2))))) + (goto-char start) + (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)))) + (while (< (point) limit) + (unless (org-looking-at-p "[ \t]*$") (org-indent-line-to ind)) + (forward-line)) + (set-marker limit nil)))) + (end (copy-marker end))) + (while (< (point) end) + (if (or (org-looking-at-p " \r\t\n") (org-at-heading-p)) (forward-line) + (let* ((element (org-element-at-point)) + (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) + (looking-at org-property-re) + (replace-match (concat (match-string 4) + (format org-property-format + (match-string 1) + (match-string 3))) + t t) + (beginning-of-line)) + (funcall indent-to ind element-end)) + ;; Elements in this category consist of three parts: + ;; before the contents, the contents, and after the + ;; contents. The contents are treated specially, + ;; according to the element type, or not indented at + ;; all. Other parts are indented as a single block. + (let* ((post (copy-marker + (or (org-element-property :post-affiliated element) + (org-element-property :begin element)))) + (cbeg + (copy-marker + (cond + ((not (org-element-property :contents-begin element)) + ;; Fake contents for source blocks. + (org-with-wide-buffer + (goto-char post) + (forward-line) + (point))) + ((memq type '(footnote-definition item plain-list)) + ;; Contents in these elements could start on + ;; the same line as the beginning of the + ;; element. Make sure we start indenting + ;; from the second line. + (org-with-wide-buffer + (goto-char post) + (forward-line) + (point))) + (t (org-element-property :contents-begin element))))) + (cend (copy-marker + (or (org-element-property :contents-end element) + ;; Fake contents for source blocks. + (org-with-wide-buffer + (goto-char element-end) + (skip-chars-backward " \r\t\n") + (line-beginning-position)))))) + (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 + ;; `org-src-tab-acts-natively' is non-nil. + (when (and (< (point) end) org-src-tab-acts-natively) + (ignore-errors + (let (org-src-strip-leading-and-trailing-blank-lines + ;; Region boundaries in edit buffer. + (start (1+ (- (point) cbeg))) + (end (- (min cend end) cbeg))) + (org-babel-do-in-edit-buffer + (indent-region start end)))))) + (t (org-indent-region (point) (min cend end)))) + (goto-char (min cend end)) + (when (< (point) end) (funcall indent-to ind element-end))) + (set-marker post nil) + (set-marker cbeg nil) + (set-marker cend nil))) + (set-marker element-end nil)))) + (set-marker end nil)))) ;;; Filling diff --git a/testing/lisp/test-org.el b/testing/lisp/test-org.el index 3aea52e..d88c235 100644 --- a/testing/lisp/test-org.el +++ b/testing/lisp/test-org.el @@ -576,6 +576,69 @@ (org-indent-line) (buffer-string)))))) +(ert-deftest test-org/indent-region () + "Test `org-indent-region' specifications." + ;; Indent paragraph. + (should + (equal "A\nB\nC" + (org-test-with-temp-text " A\nB\n C" + (org-indent-region (point-min) (point-max)) + (buffer-string)))) + ;; Indent greater elements along with their contents. + (should + (equal "#+BEGIN_CENTER\nA\nB\n#+END_CENTER" + (org-test-with-temp-text "#+BEGIN_CENTER\n A\n B\n#+END_CENTER" + (org-indent-region (point-min) (point-max)) + (buffer-string)))) + ;; Ignore contents of verse blocks and example blocks. + (should + (equal "#+BEGIN_VERSE\n A\n B\n#+END_VERSE" + (org-test-with-temp-text "#+BEGIN_VERSE\n A\n B\n#+END_VERSE" + (org-indent-region (point-min) (point-max)) + (buffer-string)))) + (should + (equal "#+BEGIN_EXAMPLE\n A\n B\n#+END_EXAMPLE" + (org-test-with-temp-text "#+BEGIN_EXAMPLE\n A\n B\n#+END_EXAMPLE" + (org-indent-region (point-min) (point-max)) + (buffer-string)))) + ;; Indent according to mode if `org-src-tab-acts-natively' is + ;; non-nil. Otherwise, do not indent code at all. + (should + (equal "#+BEGIN_SRC emacs-lisp\n(and A\n B)\n#+END_SRC" + (org-test-with-temp-text + "#+BEGIN_SRC emacs-lisp\n (and A\nB)\n#+END_SRC" + (let ((org-src-tab-acts-natively t) + (org-edit-src-content-indentation 0)) + (org-indent-region (point-min) (point-max))) + (buffer-string)))) + (should + (equal "#+BEGIN_SRC emacs-lisp\n (and A\nB)\n#+END_SRC" + (org-test-with-temp-text + "#+BEGIN_SRC emacs-lisp\n (and A\nB)\n#+END_SRC" + (let ((org-src-tab-acts-natively nil) + (org-edit-src-content-indentation 0)) + (org-indent-region (point-min) (point-max))) + (buffer-string)))) + ;; Align node properties according to `org-property-format'. + (should + (equal ":PROPERTIES:\n:key: value\n:END:" + (org-test-with-temp-text ":PROPERTIES:\n:key: value\n:END:" + (let ((org-property-format "%-10s %s")) + (org-indent-region (point-min) (point-max))) + (buffer-string)))) + ;; Special case: plain lists and footnote definitions. + (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 "[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))))) + + ;;; Editing -- 1.9.2