summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNicolas Goaziou <n.goaziou@gmail.com>2014-04-27 16:48:02 +0200
committerNicolas Goaziou <n.goaziou@gmail.com>2014-05-07 17:39:52 +0200
commitfe129fb352245e6392e98e2f84645ef861b4c966 (patch)
tree12073add601b5138a6e612467457dc4129678795
parent51ffcd02ddd0e9dc79ee293e05ec1bab3f5b4093 (diff)
downloadorg-mode-fe129fb352245e6392e98e2f84645ef861b4c966.tar.gz
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.
-rw-r--r--lisp/org.el100
-rw-r--r--testing/lisp/test-org.el70
2 files changed, 163 insertions, 7 deletions
diff --git a/lisp/org.el b/lisp/org.el
index 2842940..1cd021e 100644
--- a/lisp/org.el
+++ b/lisp/org.el
@@ -22424,15 +22424,101 @@ 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)
+ (org--align-node-property)
+ (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 7b31115..2b2b8f6 100644
--- a/testing/lisp/test-org.el
+++ b/testing/lisp/test-org.el
@@ -597,6 +597,76 @@
(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'. Handle
+ ;; nicely empty values.
+ (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))))
+ (should
+ (equal ":PROPERTIES:\n:key:\n:END:"
+ (org-test-with-temp-text ":PROPERTIES:\n:key:\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