emacs-orgmode@gnu.org archives
 help / color / mirror / code / Atom feed
* [RFC] Rewrite indentation functions
@ 2014-04-30 13:03 Nicolas Goaziou
  2014-04-30 17:08 ` Vikas Rawal
                   ` (3 more replies)
  0 siblings, 4 replies; 18+ messages in thread
From: Nicolas Goaziou @ 2014-04-30 13:03 UTC (permalink / raw)
  To: Org Mode List

[-- Attachment #1: Type: text/plain, Size: 1801 bytes --]

Hello,

I would like to install the following patches on master. Basically, they
consist of a full rewrite of all indentation related functions, with
explicit rules in docstrings, comprehensive test suites, and backed-up
by the parser.

The following changes in `org-indent-line' are expected:

  1. Indentation of the first line of an element should be, when
     applicable, relative to the /first line/ of the element before.
     Therefore, in the following example

         Some long paragraph
       with multiple line

       XAnother paragraph

     indenting line starting with "X" will align it with "Some", not
     "with".  This is consistent with plain lists

         - A list with some
           long paragraph

       XAnother paragraph

     where last line should be indented like "-", not "long".

  2. It should be possible to indent example block, verse block or
     export block contents, as `org-indent-line' usually happens on
     behalf of the user, who is assumed to know what he is doing.

     Though, this will not be the case in `org-indent-region', as
     changes could happen without the user knowing about it (e.g., when
     indenting a complete, mostly hidden, buffer).

  3. It should be possible to indent fixed-width areas.

`org-indent-region' also applies on hidden lines, with a few exceptions,
as explained above. Also, it should be a lot faster when
`org-src-tab-acts-natively' is non-nil, and complete without errors. It
could be made faster, but the main bottleneck in this function is now
`org-edit-src-code', which will need to be revamped at some point.

Internally, `org-src-native-tab-command-maybe' is merged into
`org-indent-line' since this should be a core feature, not something
installed via a hook.


WDYT?


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: 17192 bytes --]

From 054e3e6e4d445da3e3c31283c7ed29dcb651b7ea 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): New function.
(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              | 267 ++++++++++++++++++++++++++++-------------------
 testing/lisp/test-org.el | 136 ++++++++++++++++++++++++
 3 files changed, 295 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..f70c9c2 100644
--- a/lisp/org.el
+++ b/lisp/org.el
@@ -22206,116 +22206,167 @@ 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 '(footnote-definition headline inlinetask nil)) 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, or to parent's contents.
+      ((= (line-beginning-position) start)
+       (if (= (point-min) start) 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))))
+	   (if (or (not previous)
+		   (> (org-element-property :end previous) start))
+	       (org--get-expected-indentation previous t)
+	     (goto-char (org-element-property :begin previous))
+	     (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
+	  ;; 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))
+	  ;; 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))
+	  ;; 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.
+	  ((and (org-element-property :contents-end element)
+		(<= (org-element-property :contents-end element) 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-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, 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)))
+		 (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)))))))))
 
 (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..3aea52e 100644
--- a/testing/lisp/test-org.el
+++ b/testing/lisp/test-org.el
@@ -441,6 +441,142 @@
 
 
 \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
+   (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 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 " #+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'.
+  (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))))))
+
+\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: 7898 bytes --]

From 09aaee6e1bdc5bec6e57b9c0a12ac1d6ea5867d8 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              | 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)))))
+
+
 \f
 ;;; Editing
 
-- 
1.9.2


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #4: 0003-Rewrite-org-indent-drawer-and-org-indent-block.patch --]
[-- Type: text/x-diff, Size: 3440 bytes --]

From c7a9e0475f5aaedd9feccddbdfcbcb524c872aef Mon Sep 17 00:00:00 2001
From: Nicolas Goaziou <n.goaziou@gmail.com>
Date: Mon, 28 Apr 2014 18:38:31 +0200
Subject: [PATCH 3/3] Rewrite `org-indent-drawer' and `org-indent-block'

* lisp/org.el (org-indent-block, org-indent-drawer): Rewrite functions.
---
 lisp/org.el | 75 ++++++++++++++++++++++++++++---------------------------------
 1 file changed, 34 insertions(+), 41 deletions(-)

diff --git a/lisp/org.el b/lisp/org.el
index 0ea5bc8..b5b2cf8 100644
--- a/lisp/org.el
+++ b/lisp/org.el
@@ -22368,47 +22368,6 @@ Also align node properties according to `org-property-format'."
 				t t)
 		 (org-move-to-column column)))))))))
 
-(defun org-indent-drawer ()
-  "Indent the drawer at point."
-  (interactive)
-  (let ((p (point))
-	(e (and (save-excursion (re-search-forward ":END:" nil t))
-		(match-end 0)))
-	(folded
-	 (save-excursion
-	   (end-of-line)
-	   (when (overlays-at (point))
-	     (member 'invisible (overlay-properties
-				 (car (overlays-at (point)))))))))
-    (when folded (org-cycle))
-    (indent-for-tab-command)
-    (while (and (move-beginning-of-line 2) (< (point) e))
-      (indent-for-tab-command))
-    (goto-char p)
-    (when folded (org-cycle)))
-  (message "Drawer at point indented"))
-
-(defun org-indent-block ()
-  "Indent the block at point."
-  (interactive)
-  (let ((p (point))
-	(case-fold-search t)
-	(e (and (save-excursion (re-search-forward "#\\+end_?\\(?:[a-z]+\\)?" nil t))
-		(match-end 0)))
-	(folded
-	 (save-excursion
-	   (end-of-line)
-	   (when (overlays-at (point))
-	     (member 'invisible (overlay-properties
-				 (car (overlays-at (point)))))))))
-    (when folded (org-cycle))
-    (indent-for-tab-command)
-    (while (and (move-beginning-of-line 2) (< (point) e))
-      (indent-for-tab-command))
-    (goto-char p)
-    (when folded (org-cycle)))
-  (message "Block at point indented"))
-
 (defun org-indent-region (start end)
   "Indent each non-blank line in the region.
 Called from a program, START and END specify the region to
@@ -22511,6 +22470,40 @@ assumed to be significant there."
 	    (set-marker element-end nil))))
       (set-marker end nil))))
 
+(defun org-indent-drawer ()
+  "Indent the drawer at point."
+  (interactive)
+  (unless (save-excursion
+	    (beginning-of-line)
+	    (org-looking-at-p org-drawer-regexp))
+    (user-error "Not at a drawer"))
+  (let ((element (org-element-at-point)))
+    (unless (memq (org-element-type element) '(drawer property-drawer))
+      (user-error "Not at a drawer"))
+    (org-with-wide-buffer
+     (org-indent-region (org-element-property :begin element)
+			(org-element-property :end element))))
+  (message "Drawer at point indented"))
+
+(defun org-indent-block ()
+  "Indent the block at point."
+  (interactive)
+  (unless (save-excursion
+	    (beginning-of-line)
+	    (let ((case-fold-search t))
+	      (org-looking-at-p "[ \t]*#\\+\\(begin\\|end\\)_")))
+    (user-error "Not at a block"))
+  (let ((element (org-element-at-point)))
+    (unless (memq (org-element-type element)
+		  '(comment-block center-block example-block export-block
+				  quote-block special-block src-block
+				  verse-block))
+      (user-error "Not at a block"))
+    (org-with-wide-buffer
+     (org-indent-region (org-element-property :begin element)
+			(org-element-property :end element))))
+  (message "Block at point indented"))
+
 
 ;;; Filling
 
-- 
1.9.2


^ permalink raw reply related	[flat|nested] 18+ messages in thread

end of thread, other threads:[~2014-05-07 15:40 UTC | newest]

Thread overview: 18+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
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
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

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).