From: Nicolas Goaziou <n.goaziou@gmail.com>
To: Eric Abrahamsen <eric@ericabrahamsen.net>
Cc: emacs-orgmode@gnu.org
Subject: Re: [RFC] Rewrite indentation functions
Date: Sat, 03 May 2014 13:47:28 +0200 [thread overview]
Message-ID: <87tx97ozhr.fsf@gmail.com> (raw)
In-Reply-To: <8738grw8nn.fsf@ericabrahamsen.net> (Eric Abrahamsen's message of "Sat, 03 May 2014 16:47:40 +0800")
[-- Attachment #1: Type: text/plain, Size: 1126 bytes --]
Hello,
Eric Abrahamsen <eric@ericabrahamsen.net> 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
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-Rewrite-org-indent-line.patch --]
[-- Type: text/x-diff, Size: 18503 bytes --]
From 104a091d127b746662adfa7b3f71a602ef5f816e Mon Sep 17 00:00:00 2001
From: Nicolas Goaziou <n.goaziou@gmail.com>
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-mode-map>\\[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 @@
\f
+;;; 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))))))
+
+\f
;;; Editing
;;;; Insert elements
--
1.9.2
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #3: 0002-Rewrite-org-indent-region.patch --]
[-- Type: text/x-diff, Size: 8016 bytes --]
From 4a0440d4178e51410cc1e6911e47f4ab2b8795ac Mon Sep 17 00:00:00 2001
From: Nicolas Goaziou <n.goaziou@gmail.com>
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)))))
+
+
\f
;;; Editing
--
1.9.2
next prev parent reply other threads:[~2014-05-03 11:47 UTC|newest]
Thread overview: 18+ messages / expand[flat|nested] mbox.gz Atom feed top
2014-04-30 13:03 [RFC] Rewrite indentation functions Nicolas Goaziou
2014-04-30 17:08 ` Vikas Rawal
2014-04-30 18:59 ` Sebastien Vauban
2014-05-01 19:11 ` Nicolas Goaziou
2014-05-02 5:38 ` Eric Abrahamsen
2014-05-02 7:32 ` Nicolas Goaziou
2014-05-02 10:01 ` Eric Abrahamsen
2014-05-03 7:47 ` Eric Abrahamsen
2014-05-03 8:47 ` Eric Abrahamsen
2014-05-03 11:47 ` Nicolas Goaziou [this message]
2014-05-04 3:25 ` Eric Abrahamsen
2014-05-04 3:30 ` Eric Abrahamsen
2014-05-04 19:45 ` Nicolas Goaziou
2014-05-04 3:39 ` Eric Abrahamsen
2014-05-05 9:30 ` Nicolas Goaziou
2014-05-06 9:41 ` Bastien
2014-05-07 0:51 ` Eric Abrahamsen
2014-05-07 15:40 ` Nicolas Goaziou
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
List information: https://www.orgmode.org/
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=87tx97ozhr.fsf@gmail.com \
--to=n.goaziou@gmail.com \
--cc=emacs-orgmode@gnu.org \
--cc=eric@ericabrahamsen.net \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
Code repositories for project(s) associated with this public inbox
https://git.savannah.gnu.org/cgit/emacs/org-mode.git
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).