From mboxrd@z Thu Jan 1 00:00:00 1970 From: Nicolas Goaziou Subject: [RFC] Rewrite indentation functions Date: Wed, 30 Apr 2014 15:03:18 +0200 Message-ID: <87oazjnf55.fsf@gmail.com> Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" Return-path: Received: from eggs.gnu.org ([2001:4830:134:3::10]:36328) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1WfUA9-0005Ps-T2 for emacs-orgmode@gnu.org; Wed, 30 Apr 2014 09:03:10 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1WfUA0-0005Bl-Gc for emacs-orgmode@gnu.org; Wed, 30 Apr 2014 09:03:01 -0400 Received: from mail-wg0-x22c.google.com ([2a00:1450:400c:c00::22c]:38920) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1WfU9z-0005BX-VW for emacs-orgmode@gnu.org; Wed, 30 Apr 2014 09:02:52 -0400 Received: by mail-wg0-f44.google.com with SMTP id a1so138968wgh.15 for ; Wed, 30 Apr 2014 06:02:51 -0700 (PDT) Received: from selenimh ([91.224.148.150]) by mx.google.com with ESMTPSA id l12sm36086528wjr.35.2014.04.30.06.02.47 for (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Wed, 30 Apr 2014 06:02:49 -0700 (PDT) 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: Org Mode List --=-=-= Content-Type: text/plain 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 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=0001-Rewrite-org-indent-line.patch >From 054e3e6e4d445da3e3c31283c7ed29dcb651b7ea 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): 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-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 @@ +;;; 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)))))) + + ;;; Editing ;;;; Insert elements -- 1.9.2 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=0002-Rewrite-org-indent-region.patch >From 09aaee6e1bdc5bec6e57b9c0a12ac1d6ea5867d8 Mon Sep 17 00:00:00 2001 From: Nicolas Goaziou Date: Sun, 27 Apr 2014 16:48:02 +0200 Subject: [PATCH 2/3] Rewrite `org-indent-region' * lisp/org.el (org-indent-region): Update function according to recent `org-indent-line' change. Optimize it. * testing/lisp/test-org.el (test-org/indent-region): New test. --- lisp/org.el | 105 +++++++++++++++++++++++++++++++++++++++++++---- testing/lisp/test-org.el | 63 ++++++++++++++++++++++++++++ 2 files changed, 161 insertions(+), 7 deletions(-) diff --git a/lisp/org.el b/lisp/org.el index f70c9c2..0ea5bc8 100644 --- a/lisp/org.el +++ b/lisp/org.el @@ -22410,15 +22410,106 @@ Also align node properties according to `org-property-format'." (message "Block at point indented")) (defun org-indent-region (start end) - "Indent region." + "Indent each non-blank line in the region. +Called from a program, START and END specify the region to +indent. The function will not indent contents of example blocks, +verse blocks and export blocks as leading white spaces are +assumed to be significant there." (interactive "r") (save-excursion - (let ((line-end (org-current-line end))) - (goto-char start) - (while (< (org-current-line) line-end) - (cond ((org-in-src-block-p t) (org-src-native-tab-command-maybe)) - (t (call-interactively 'org-indent-line))) - (move-beginning-of-line 2))))) + (goto-char start) + (beginning-of-line) + (let ((indent-to + (lambda (ind pos) + ;; Set IND as indentation for all lines between point and + ;; POS or END, whichever comes first. Blank lines are + ;; ignored. Leave point after POS once done. + (let ((limit (copy-marker (min end pos)))) + (while (< (point) limit) + (unless (org-looking-at-p "[ \t]*$") (org-indent-line-to ind)) + (forward-line)) + (set-marker limit nil)))) + (end (copy-marker end))) + (while (< (point) end) + (if (or (org-looking-at-p " \r\t\n") (org-at-heading-p)) (forward-line) + (let* ((element (org-element-at-point)) + (type (org-element-type element)) + (element-end (copy-marker (org-element-property :end element))) + (ind (org--get-expected-indentation element nil))) + (if (or (memq type '(paragraph table table-row)) + (not (or (org-element-property :contents-begin element) + (memq type + '(example-block export-block src-block))))) + ;; Elements here are indented as a single block. Also + ;; align node properties. + (progn + (when (eq type 'node-property) + (looking-at org-property-re) + (replace-match (concat (match-string 4) + (format org-property-format + (match-string 1) + (match-string 3))) + t t) + (beginning-of-line)) + (funcall indent-to ind element-end)) + ;; Elements in this category consist of three parts: + ;; before the contents, the contents, and after the + ;; contents. The contents are treated specially, + ;; according to the element type, or not indented at + ;; all. Other parts are indented as a single block. + (let* ((post (copy-marker + (or (org-element-property :post-affiliated element) + (org-element-property :begin element)))) + (cbeg + (copy-marker + (cond + ((not (org-element-property :contents-begin element)) + ;; Fake contents for source blocks. + (org-with-wide-buffer + (goto-char post) + (forward-line) + (point))) + ((memq type '(footnote-definition item plain-list)) + ;; Contents in these elements could start on + ;; the same line as the beginning of the + ;; element. Make sure we start indenting + ;; from the second line. + (org-with-wide-buffer + (goto-char post) + (forward-line) + (point))) + (t (org-element-property :contents-begin element))))) + (cend (copy-marker + (or (org-element-property :contents-end element) + ;; Fake contents for source blocks. + (org-with-wide-buffer + (goto-char element-end) + (skip-chars-backward " \r\t\n") + (line-beginning-position)))))) + (funcall indent-to ind cbeg) + (when (< (point) end) + (case type + ((example-block export-block verse-block)) + (src-block + ;; In a source block, indent source code according + ;; to language major mode, but only if + ;; `org-src-tab-acts-natively' is non-nil. + (when (and (< (point) end) org-src-tab-acts-natively) + (ignore-errors + (let (org-src-strip-leading-and-trailing-blank-lines + ;; Region boundaries in edit buffer. + (start (1+ (- (point) cbeg))) + (end (- (min cend end) cbeg))) + (org-babel-do-in-edit-buffer + (indent-region start end)))))) + (t (org-indent-region (point) (min cend end)))) + (goto-char (min cend end)) + (when (< (point) end) (funcall indent-to ind element-end))) + (set-marker post nil) + (set-marker cbeg nil) + (set-marker cend nil))) + (set-marker element-end nil)))) + (set-marker end nil)))) ;;; Filling diff --git a/testing/lisp/test-org.el b/testing/lisp/test-org.el index 3aea52e..d88c235 100644 --- a/testing/lisp/test-org.el +++ b/testing/lisp/test-org.el @@ -576,6 +576,69 @@ (org-indent-line) (buffer-string)))))) +(ert-deftest test-org/indent-region () + "Test `org-indent-region' specifications." + ;; Indent paragraph. + (should + (equal "A\nB\nC" + (org-test-with-temp-text " A\nB\n C" + (org-indent-region (point-min) (point-max)) + (buffer-string)))) + ;; Indent greater elements along with their contents. + (should + (equal "#+BEGIN_CENTER\nA\nB\n#+END_CENTER" + (org-test-with-temp-text "#+BEGIN_CENTER\n A\n B\n#+END_CENTER" + (org-indent-region (point-min) (point-max)) + (buffer-string)))) + ;; Ignore contents of verse blocks and example blocks. + (should + (equal "#+BEGIN_VERSE\n A\n B\n#+END_VERSE" + (org-test-with-temp-text "#+BEGIN_VERSE\n A\n B\n#+END_VERSE" + (org-indent-region (point-min) (point-max)) + (buffer-string)))) + (should + (equal "#+BEGIN_EXAMPLE\n A\n B\n#+END_EXAMPLE" + (org-test-with-temp-text "#+BEGIN_EXAMPLE\n A\n B\n#+END_EXAMPLE" + (org-indent-region (point-min) (point-max)) + (buffer-string)))) + ;; Indent according to mode if `org-src-tab-acts-natively' is + ;; non-nil. Otherwise, do not indent code at all. + (should + (equal "#+BEGIN_SRC emacs-lisp\n(and A\n B)\n#+END_SRC" + (org-test-with-temp-text + "#+BEGIN_SRC emacs-lisp\n (and A\nB)\n#+END_SRC" + (let ((org-src-tab-acts-natively t) + (org-edit-src-content-indentation 0)) + (org-indent-region (point-min) (point-max))) + (buffer-string)))) + (should + (equal "#+BEGIN_SRC emacs-lisp\n (and A\nB)\n#+END_SRC" + (org-test-with-temp-text + "#+BEGIN_SRC emacs-lisp\n (and A\nB)\n#+END_SRC" + (let ((org-src-tab-acts-natively nil) + (org-edit-src-content-indentation 0)) + (org-indent-region (point-min) (point-max))) + (buffer-string)))) + ;; Align node properties according to `org-property-format'. + (should + (equal ":PROPERTIES:\n:key: value\n:END:" + (org-test-with-temp-text ":PROPERTIES:\n:key: value\n:END:" + (let ((org-property-format "%-10s %s")) + (org-indent-region (point-min) (point-max))) + (buffer-string)))) + ;; Special case: plain lists and footnote definitions. + (should + (equal "- A\n B\n - C\n\n D" + (org-test-with-temp-text "- A\n B\n - C\n\n D" + (org-indent-region (point-min) (point-max)) + (buffer-string)))) + (should + (equal "[fn:1] Definition\n\nDefinition" + (org-test-with-temp-text "[fn:1] Definition\n\n Definition" + (org-indent-region (point-min) (point-max)) + (buffer-string))))) + + ;;; Editing -- 1.9.2 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=0003-Rewrite-org-indent-drawer-and-org-indent-block.patch >From c7a9e0475f5aaedd9feccddbdfcbcb524c872aef Mon Sep 17 00:00:00 2001 From: Nicolas Goaziou 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 --=-=-=--