From mboxrd@z Thu Jan 1 00:00:00 1970 From: Nicolas Goaziou Subject: Re: [RFC] Rewrite indentation functions Date: Sat, 03 May 2014 13:47:28 +0200 Message-ID: <87tx97ozhr.fsf@gmail.com> References: <87oazjnf55.fsf@gmail.com> <87d2fwhh95.fsf@ericabrahamsen.net> <87fvksoctz.fsf@gmail.com> <87oazffgne.fsf@ericabrahamsen.net> <8738grw8nn.fsf@ericabrahamsen.net> Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" Return-path: Received: from eggs.gnu.org ([2001:4830:134:3::10]:59619) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1WgYPM-0004Gj-31 for emacs-orgmode@gnu.org; Sat, 03 May 2014 07:47:13 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1WgYPG-0003UU-1B for emacs-orgmode@gnu.org; Sat, 03 May 2014 07:47:08 -0400 Received: from mail-wg0-x22a.google.com ([2a00:1450:400c:c00::22a]:58533) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1WgYPF-0003Tc-HK for emacs-orgmode@gnu.org; Sat, 03 May 2014 07:47:01 -0400 Received: by mail-wg0-f42.google.com with SMTP id k14so4602034wgh.1 for ; Sat, 03 May 2014 04:47:00 -0700 (PDT) In-Reply-To: <8738grw8nn.fsf@ericabrahamsen.net> (Eric Abrahamsen's message of "Sat, 03 May 2014 16:47:40 +0800") List-Id: "General discussions about Org-mode." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: emacs-orgmode-bounces+geo-emacs-orgmode=m.gmane.org@gnu.org Sender: emacs-orgmode-bounces+geo-emacs-orgmode=m.gmane.org@gnu.org To: Eric Abrahamsen Cc: emacs-orgmode@gnu.org --=-=-= Content-Type: text/plain Hello, Eric Abrahamsen writes: >> Right now I'm seeing breakage with `org-set-property' -- this only >> happens on the indentation patches branch. Adding an EXPORT_AUTHOR >> property with that command, value of "asdfadsf", gives me this: >> >> * Test Heading >> :PROPERTIES: >> >> :EXPORT_AUTHOR: asdfasdfnil nil >> >> Extra blank nil, spurious "nils", and no :END: [...] > Specifically, in this section of `org-indent-line': > > (when (eq type 'node-property) > (let ((column (current-column))) > (save-excursion > (beginning-of-line) > (looking-at org-property-re)) > (replace-match (concat (match-string 4) > (format org-property-format > (match-string 1) > (match-string 3))) > t t) > (org-move-to-column column))) > > Those match-string calls toward the end both return "nil", and the > "nil"s get inserted directly into the buffer. I tried this with a > minimal setup (load-paths only, and a blank Org file) and could > reproduce. Indeed. I attach a replacement for both patch 1 and 2. Thank you. Regards, -- Nicolas Goaziou --=-=-= Content-Type: text/x-diff Content-Disposition: inline; filename=0001-Rewrite-org-indent-line.patch >From 104a091d127b746662adfa7b3f71a602ef5f816e Mon Sep 17 00:00:00 2001 From: Nicolas Goaziou Date: Tue, 24 Dec 2013 14:04:17 +0100 Subject: [PATCH 1/3] Rewrite `org-indent-line' * lisp/org.el (org--get-expected-indentation, org--align-node-property): New functions. (org-indent-line): Use new function. Also merge functionalities with `org-src-native-tab-command-maybe'. * lisp/org-src.el (org-src-native-tab-command-maybe): Remove function. * testing/lisp/test-org.el (test-org/indent-line): New test. --- lisp/org-src.el | 11 -- lisp/org.el | 281 +++++++++++++++++++++++++++++------------------ testing/lisp/test-org.el | 157 ++++++++++++++++++++++++++ 3 files changed, 330 insertions(+), 119 deletions(-) diff --git a/lisp/org-src.el b/lisp/org-src.el index 791f934..8d60f68 100644 --- a/lisp/org-src.el +++ b/lisp/org-src.el @@ -895,17 +895,6 @@ issued in the language major mode buffer." :version "24.1" :group 'org-babel) -(defun org-src-native-tab-command-maybe () - "Perform language-specific TAB action. -Alter code block according to what TAB does in the language major mode." - (and org-src-tab-acts-natively - (org-in-src-block-p) - (not (equal this-command 'org-shifttab)) - (let ((org-src-strip-leading-and-trailing-blank-lines nil)) - (org-babel-do-key-sequence-in-edit-buffer (kbd "TAB"))))) - -(add-hook 'org-tab-first-hook 'org-src-native-tab-command-maybe) - (defun org-src-font-lock-fontify-block (lang start end) "Fontify code block. This function is called by emacs automatic fontification, as long diff --git a/lisp/org.el b/lisp/org.el index 44a4e44..3559209 100644 --- a/lisp/org.el +++ b/lisp/org.el @@ -22206,116 +22206,181 @@ hierarchy of headlines by UP levels before marking the subtree." ;;; Indentation +(defun org--get-expected-indentation (element contentsp) + "Expected indentation column for current line, according to ELEMENT. +ELEMENT is an element containing point. CONTENTSP is non-nil +when indentation is to be computed according to contents of +ELEMENT." + (let ((type (org-element-type element)) + (start (org-element-property :begin element))) + (org-with-wide-buffer + (cond + (contentsp + (case type + (footnote-definition 0) + ((headline inlinetask nil) + (if (not org-adapt-indentation) 0 + (let ((level (org-current-level))) + (if level (1+ level) 0)))) + ((item plain list) + (org-list-item-body-column + (or (org-element-property :post-affiliated element) start))) + (otherwise + (goto-char start) + (org-get-indentation)))) + ((memq type '(headline inlinetask nil)) + (if (save-excursion (beginning-of-line) (looking-at "[ \t]*$")) + (org--get-expected-indentation element t) + 0)) + ((eq type 'footnote-definition) 0) + ;; First paragraph of a footnote definition or an item. + ;; Indent like parent. + ((< (line-beginning-position) start) + (org--get-expected-indentation + (org-element-property :parent element) t)) + ;; At first line: indent according to previous sibling, if any, + ;; ignoring footnote definitions and inline tasks, or parent's + ;; contents. + ((= (line-beginning-position) start) + (catch 'exit + (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))))))))) + ;; Otherwise, move to the first non-blank line above. + (t + (beginning-of-line) + (let ((pos (point))) + (skip-chars-backward " \r\t\n") + (cond + ;; Two blank lines end a footnote definition or a plain + ;; list. When we indent an empty line after them, the + ;; containing list or footnote definition is over, so it + ;; qualifies as a previous sibling. Therefore, we indent + ;; like its first line. + ((and (memq type '(footnote-definition plain-list)) + (> (count-lines (point) pos) 2)) + (goto-char start) + (org-get-indentation)) + ;; Line above is the first one of a paragraph at the + ;; beginning of an item or a footnote definition. Indent + ;; like parent. + ((< (line-beginning-position) start) + (org--get-expected-indentation + (org-element-property :parent element) t)) + ;; POS is after contents in a greater element. Indent like + ;; the beginning of the element. + ;; + ;; As a special case, if point is at the end of a footnote + ;; definition or an item, indent like the very last element + ;; within. + ((let ((cend (org-element-property :contents-end element))) + (and cend (<= cend pos))) + (if (memq type '(footnote-definition item plain-list)) + (org--get-expected-indentation (org-element-at-point) nil) + (goto-char start) + (org-get-indentation))) + ;; In any other case, indent like the current line. + (t (org-get-indentation))))))))) + +(defun org--align-node-property () + "Align node property at point. +Alignment is done according to `org-property-format', which see." + (when (save-excursion + (beginning-of-line) + (looking-at org-property-re)) + (replace-match + (concat (match-string 4) + (format org-property-format (match-string 1) (match-string 3))) + t t))) + (defun org-indent-line () - "Indent line depending on context." + "Indent line depending on context. + +Indentation is done according to the following rules: + + - Footnote definitions, headlines and inline tasks have to + start at column 0. + + - On the very first line of an element, consider, in order, the + next rules until one matches: + + 1. If there's a sibling element before, ignoring footnote + definitions and inline tasks, indent like its first line. + + 2. If element has a parent, indent like its contents. More + precisely, if parent is an item, indent after the + description part, if any, or the bullet (see + ``org-list-description-max-indent'). Else, indent like + parent's first line. + + 3. Otherwise, indent relatively to current level, if + `org-adapt-indentation' is non-nil, or to left margin. + + - On a blank line at the end of a plain list, an item, or + a footnote definition, indent like the very last element + within. + + - In the code part of a source block, use language major mode + to indent current line if `org-src-tab-acts-natively' is + non-nil. If it is nil, do nothing. + + - Otherwise, indent like the first non-blank line above. + +The function doesn't indent an item as it could break the whole +list structure. Instead, use \\\\[org-shiftmetaleft] or \ +\\[org-shiftmetaright]. + +Also align node properties according to `org-property-format'." (interactive) - (let* ((pos (point)) - (itemp (org-at-item-p)) - (case-fold-search t) - (org-drawer-regexp (or org-drawer-regexp "\000")) - (inline-task-p (and (featurep 'org-inlinetask) - (org-inlinetask-in-task-p))) - (inline-re (and inline-task-p - (org-inlinetask-outline-regexp))) - column) - (if (and orgstruct-is-++ (eq pos (point))) - (let ((indent-line-function (cadadr (assoc 'indent-line-function org-fb-vars)))) - (indent-according-to-mode)) - (beginning-of-line 1) - (cond - ;; Headings - ((looking-at org-outline-regexp) (setq column 0)) - ;; Footnote definition - ((looking-at org-footnote-definition-re) (setq column 0)) - ;; Literal examples - ((looking-at "[ \t]*:\\( \\|$\\)") - (setq column (org-get-indentation))) ; do nothing - ;; Lists - ((ignore-errors (goto-char (org-in-item-p))) - (setq column (if itemp - (org-get-indentation) - (org-list-item-body-column (point)))) - (goto-char pos)) - ;; Drawers - ((and (looking-at "[ \t]*:END:") - (save-excursion (re-search-backward org-drawer-regexp nil t))) - (save-excursion - (goto-char (1- (match-beginning 1))) - (setq column (current-column)))) - ;; Special blocks - ((and (looking-at "[ \t]*#\\+end_\\([a-z]+\\)") - (save-excursion - (re-search-backward - (concat "^[ \t]*#\\+begin_" (downcase (match-string 1))) nil t))) - (setq column (org-get-indentation (match-string 0)))) - ((and (not (looking-at "[ \t]*#\\+begin_")) - (org-between-regexps-p "^[ \t]*#\\+begin_" "[ \t]*#\\+end_")) - (save-excursion - (re-search-backward "^[ \t]*#\\+begin_\\([a-z]+\\)" nil t)) - (setq column - (cond ((equal (downcase (match-string 1)) "src") - ;; src blocks: let `org-edit-src-exit' handle them - (org-get-indentation)) - ((equal (downcase (match-string 1)) "example") - (max (org-get-indentation) - (org-get-indentation (match-string 0)))) - (t - (org-get-indentation (match-string 0)))))) - ;; This line has nothing special, look at the previous relevant - ;; line to compute indentation - (t - (beginning-of-line 0) - (while (and (not (bobp)) - (not (looking-at org-table-line-regexp)) - (not (looking-at org-drawer-regexp)) - ;; When point started in an inline task, do not move - ;; above task starting line. - (not (and inline-task-p (looking-at inline-re))) - ;; Skip drawers, blocks, empty lines, verbatim, - ;; comments, tables, footnotes definitions, lists, - ;; inline tasks. - (or (and (looking-at "[ \t]*:END:") - (re-search-backward org-drawer-regexp nil t)) - (and (looking-at "[ \t]*#\\+end_") - (re-search-backward "[ \t]*#\\+begin_"nil t)) - (looking-at "[ \t]*[\n:#|]") - (looking-at org-footnote-definition-re) - (and (not inline-task-p) - (featurep 'org-inlinetask) - (org-inlinetask-in-task-p) - (or (org-inlinetask-goto-beginning) t)))) - (beginning-of-line 0)) - (cond - ;; There was a list item above. - ((ignore-errors (goto-char (org-in-item-p))) - (goto-char (org-list-get-top-point (org-list-struct))) - (setq column (org-get-indentation))) - ;; There was an heading above. - ((looking-at "\\*+[ \t]+") - (if (not org-adapt-indentation) - (setq column 0) - (goto-char (match-end 0)) - (setq column (current-column)))) - ;; A drawer had started and is unfinished - ((looking-at org-drawer-regexp) - (goto-char (1- (match-beginning 1))) - (setq column (current-column))) - ;; Else, nothing noticeable found: get indentation and go on. - (t (setq column (org-get-indentation)))))) - ;; Now apply indentation and move cursor accordingly - (goto-char pos) - (if (<= (current-column) (current-indentation)) - (org-indent-line-to column) - (save-excursion (org-indent-line-to column))) - ;; Special polishing for properties, see `org-property-format' - (setq column (current-column)) - (beginning-of-line 1) - (if (looking-at org-property-re) - (replace-match (concat (match-string 4) - (format org-property-format - (match-string 1) (match-string 3))) - t t)) - (org-move-to-column column)))) + (cond + (orgstruct-is-++ + (let ((indent-line-function + (cadadr (assq 'indent-line-function org-fb-vars)))) + (indent-according-to-mode))) + ((org-at-heading-p) 'noindent) + (t + (let* ((element (save-excursion (beginning-of-line) (org-element-at-point))) + (type (org-element-type element))) + (cond ((and (memq type '(plain-list item)) + (= (line-beginning-position) + (or (org-element-property :post-affiliated element) + (org-element-property :begin element)))) + 'noindent) + ((and (eq type 'src-block) + (> (line-beginning-position) + (org-element-property :post-affiliated element)) + (< (line-beginning-position) + (org-with-wide-buffer + (goto-char (org-element-property :end element)) + (skip-chars-backward " \r\t\n") + (line-beginning-position)))) + (if (not org-src-tab-acts-natively) 'noindent + (let ((org-src-strip-leading-and-trailing-blank-lines nil)) + (org-babel-do-key-sequence-in-edit-buffer (kbd "TAB"))))) + (t + (let ((column (org--get-expected-indentation element nil))) + ;; Preserve current column. + (if (<= (current-column) (current-indentation)) + (org-indent-line-to column) + (save-excursion (org-indent-line-to column)))) + ;; Align node property. Also preserve current column. + (when (eq type 'node-property) + (let ((column (current-column))) + (org--align-node-property) + (org-move-to-column column))))))))) (defun org-indent-drawer () "Indent the drawer at point." diff --git a/testing/lisp/test-org.el b/testing/lisp/test-org.el index e8d8078..7b31115 100644 --- a/testing/lisp/test-org.el +++ b/testing/lisp/test-org.el @@ -441,6 +441,163 @@ +;;; Indentation + +(ert-deftest test-org/indent-line () + "Test `org-indent-line' specifications." + ;; Do not indent footnote definitions or headlines. + (should + (zerop + (org-test-with-temp-text "* H" + (org-indent-line) + (org-get-indentation)))) + (should + (zerop + (org-test-with-temp-text "[fn:1] fn" + (let ((org-adapt-indentation t)) (org-indent-line)) + (org-get-indentation)))) + ;; Do not indent before first headline. + (should + (zerop + (org-test-with-temp-text "" + (org-indent-line) + (org-get-indentation)))) + ;; Indent according to headline level otherwise, unless + ;; `org-adapt-indentation' is nil. + (should + (= 2 + (org-test-with-temp-text "* H\nA" + (forward-line) + (let ((org-adapt-indentation t)) (org-indent-line)) + (org-get-indentation)))) + (should + (= 2 + (org-test-with-temp-text "* H\n\nA" + (forward-line) + (let ((org-adapt-indentation t)) (org-indent-line)) + (org-get-indentation)))) + (should + (zerop + (org-test-with-temp-text "* H\nA" + (forward-line) + (let ((org-adapt-indentation nil)) (org-indent-line)) + (org-get-indentation)))) + ;; Indenting preserves point position. + (should + (org-test-with-temp-text "* H\nAB" + (forward-line) + (forward-char) + (let ((org-adapt-indentation t)) (org-indent-line)) + (looking-at "B"))) + ;; Do not change indentation at an item. + (should + (= 1 + (org-test-with-temp-text "* H\n - A" + (forward-line) + (let ((org-adapt-indentation t)) (org-indent-line)) + (org-get-indentation)))) + ;; On blank lines at the end of a list, indent like last element + ;; within it if the line is still in the list. Otherwise, indent + ;; like the whole list. + (should + (= 4 + (org-test-with-temp-text "* H\n- A\n - AA\n" + (goto-char (point-max)) + (let ((org-adapt-indentation t)) (org-indent-line)) + (org-get-indentation)))) + (should + (zerop + (org-test-with-temp-text "* H\n- A\n - AA\n\n\n\n" + (goto-char (point-max)) + (let ((org-adapt-indentation t)) (org-indent-line)) + (org-get-indentation)))) + ;; Likewise, on a blank line at the end of a footnote definition, + ;; indent at column 0 if line belongs to the definition. Otherwise, + ;; indent like the definition itself. + (should + (zerop + (org-test-with-temp-text "* H\n[fn:1] Definition\n" + (goto-char (point-max)) + (let ((org-adapt-indentation t)) (org-indent-line)) + (org-get-indentation)))) + (should + (zerop + (org-test-with-temp-text "* H\n[fn:1] Definition\n\n\n\n" + (goto-char (point-max)) + (let ((org-adapt-indentation t)) (org-indent-line)) + (org-get-indentation)))) + ;; After the end of the contents of a greater element, indent like + ;; the beginning of the element. + (should + (= 1 + (org-test-with-temp-text " #+BEGIN_CENTER\n Contents\n#+END_CENTER" + (forward-line 2) + (org-indent-line) + (org-get-indentation)))) + ;; At the first line of an element, indent like previous element's + ;; first line, ignoring footnotes definitions and inline tasks, or + ;; according to parent. + (should + (= 2 + (org-test-with-temp-text "A\n\n B\n\nC" + (goto-char (point-max)) + (org-indent-line) + (org-get-indentation)))) + (should + (= 1 + (org-test-with-temp-text " A\n\n[fn:1] B\n\n\nC" + (goto-char (point-max)) + (org-indent-line) + (org-get-indentation)))) + (should + (= 1 + (org-test-with-temp-text " #+BEGIN_CENTER\n Contents\n#+END_CENTER" + (forward-line 1) + (org-indent-line) + (org-get-indentation)))) + ;; Within code part of a source block, use language major mode if + ;; `org-src-tab-acts-natively' is non-nil. Do nothing otherwise. + (should + (= 6 + (org-test-with-temp-text "#+BEGIN_SRC emacs-lisp\n (and A\nB)\n#+END_SRC" + (forward-line 2) + (let ((org-src-tab-acts-natively t) + (org-edit-src-content-indentation 0)) + (org-indent-line)) + (org-get-indentation)))) + (should + (zerop + (org-test-with-temp-text "#+BEGIN_SRC emacs-lisp\n (and A\nB)\n#+END_SRC" + (forward-line 2) + (let ((org-src-tab-acts-natively nil) + (org-edit-src-content-indentation 0)) + (org-indent-line)) + (org-get-indentation)))) + ;; Otherwise, indent like the first non-blank line above. + (should + (zerop + (org-test-with-temp-text "#+BEGIN_CENTER\nline1\n\n line2\n#+END_CENTER" + (forward-line 3) + (org-indent-line) + (org-get-indentation)))) + ;; 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:" + (forward-line) + (let ((org-property-format "%-10s %s")) + (org-indent-line) + (buffer-string))))) + (should + (equal ":PROPERTIES:\n:key:\n:END:" + (org-test-with-temp-text ":PROPERTIES:\n:key:\n:END:" + (forward-line) + (let ((org-property-format "%-10s %s")) + (org-indent-line) + (buffer-string)))))) + + ;;; Editing ;;;; Insert elements -- 1.9.2 --=-=-= Content-Type: text/x-diff Content-Disposition: inline; filename=0002-Rewrite-org-indent-region.patch >From 4a0440d4178e51410cc1e6911e47f4ab2b8795ac 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 | 100 +++++++++++++++++++++++++++++++++++++++++++---- testing/lisp/test-org.el | 70 +++++++++++++++++++++++++++++++++ 2 files changed, 163 insertions(+), 7 deletions(-) diff --git a/lisp/org.el b/lisp/org.el index 3559209..af34d99 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 -- 1.9.2 --=-=-=--