emacs-orgmode@gnu.org archives
 help / color / mirror / code / Atom feed
From: Dan Davison <davison@stats.ox.ac.uk>
To: emacs org-mode mailing list <emacs-orgmode@gnu.org>
Subject: [PATCH] org-src code indentation, etc
Date: Thu, 08 Oct 2009 12:44:34 -0400	[thread overview]
Message-ID: <878wfln8ct.fsf@stats.ox.ac.uk> (raw)

Branch ded-src-indent at


proposes two improvements to org-src-mode, one related improvement to
org-babel, and fixes two small bugs in org-mode. The diff is below for a
quick look. Carsten, if you approve, could you pull this from the git
repo? It should add 4 commits to current org.

Improvements to org-src-mode

- Point stays in same location relative to text when switching between
  org and code buffers.

- A new customisable variable org-src-preserve-indentation allows the
  user to prevent org from making any automatic alterations to code
  indentation when moving between org and code buffers. One requirement
  for this is when extracting source code embedded in an org buffer (see

Org Bugs fixed

- Setting org-edit-src-content-indentation to zero resulted in an
  infinite loop in org-edit-src-exit.

- Fixed width regions are defined by lines starting with a colon, with
  optional leading whitespace. However, if there was any leading
  whitespace, org-edit-src-exit behaved incorrectly causing the leading
  whitespace to grow.

- Also some docstring typos and a docstring correction, and some
  variable name clarification.

Org-babel new feature

- Make the tangling (source code extraction) process respect the new
  variable org-src-preserve-indentation. This allows python code to be
  tangled correctly, regardless of how classes/methods/functions are
  distributed among org code blocks. Previously, for example, a class
  method in a separate source block would have been incorrectly indented
  as a top-level function in tangled output.

In addition, there is an unimportant org-mode bug that is *not* yet

- If a source block contains an empty line and nothing else, repeated
  invocations of org-edit-src-code followed by org-edit-src-exit will
  add spaces to that line, indefinitely. This is because
  org-do-remove-indentation does not do anything when the 'code'
  consists only of space characters.


Please obtain commits from branch ded-src-indent at


--8<---------------cut here---------------start------------->8---
diff --git a/contrib/babel/lisp/org-babel.el b/contrib/babel/lisp/org-babel.el
index db09231..8b0b11e 100644
--- a/contrib/babel/lisp/org-babel.el
+++ b/contrib/babel/lisp/org-babel.el
@@ -366,13 +366,14 @@ may be specified in the properties of the current outline entry."
 (defun org-babel-parse-src-block-match ()
   (let* ((lang (org-babel-clean-text-properties (match-string 1)))
          (lang-headers (intern (concat "org-babel-default-header-args:" lang)))
-         (body (org-babel-clean-text-properties (match-string 4))))
+         (body (org-babel-clean-text-properties (match-string 4)))
+	 (preserve-indentation org-src-preserve-indentation))
     (list lang
           ;; get src block body removing properties, protective commas, and indentation
               (insert (org-babel-strip-protective-commas body))
-              (org-do-remove-indentation)
+	      (unless preserve-indentation (org-do-remove-indentation))
diff --git a/lisp/org-src.el b/lisp/org-src.el
index 3427b7f..796e914 100644
--- a/lisp/org-src.el
+++ b/lisp/org-src.el
@@ -81,11 +81,22 @@ These are the regions where each line starts with a colon."
 	  (const fundamental-mode)
 	  (function :tag "Other (specify)")))
+(defcustom org-src-preserve-indentation nil
+  "If non-nil, leading whitespace characters in source code
+  blocks are preserved. Otherwise, after editing with
+  \\[org-edit-src-code], the minimum (across-lines) number of
+  leading whitespace characters are removed from all lines, and
+  the code block is then uniformly indented according to the
+  value of `org-edit-src-content-indentation'."
+  :group 'org-edit-structure
+  :type 'boolean)
 (defcustom org-edit-src-content-indentation 2
-  "Indentation for the content is a source code block.
+  "Indentation for the content of a source code block.
 This should be the number of spaces added to the indentation of the #+begin
 line in order to compute the indentation of the block content after
-editing it with \\[org-edit-src-code]."
+editing it with \\[org-edit-src-code]. Has no effect if
+`org-src-preserve-indentation' is non-nil."
   :group 'org-edit-structure
   :type 'integer)
@@ -135,7 +146,7 @@ For example, there is no ocaml-mode in Emacs, but the mode to use is
 (defvar org-edit-src-beg-marker nil)
 (defvar org-edit-src-end-marker nil)
 (defvar org-edit-src-overlay nil)
-(defvar org-edit-src-nindent nil)
+(defvar org-edit-src-block-indentation nil)
 (define-minor-mode org-src-mode
   "Minor mode for language major mode buffers generated by org.
@@ -153,6 +164,7 @@ This will remove the original code in the Org buffer, and replace it with
 the edited version."
   (let ((line (org-current-line))
+	(col (current-column))
 	(case-fold-search t)
 	(msg (substitute-command-keys
 	      "Edit, then exit with C-c ' (C-c and single quote)"))
@@ -160,7 +172,8 @@ the edited version."
 	(org-mode-p (eq major-mode 'org-mode))
 	(beg (make-marker))
 	(end (make-marker))
-	nindent	ovl lang lang-f single lfmt code begline buffer)
+	(preserve-indentation org-src-preserve-indentation)
+	block-nindent total-nindent ovl lang lang-f single lfmt code begline buffer)
     (if (not info)
       (setq beg (move-marker beg (nth 0 info))
@@ -171,7 +184,7 @@ the edited version."
 	    lang (if (symbolp lang) (symbol-name lang) lang)
 	    single (nth 3 info)
 	    lfmt (nth 4 info)
-	    nindent (nth 5 info)
+	    block-nindent (nth 5 info)
 	    lang-f (intern (concat lang "-mode"))
 	    begline (save-excursion (goto-char beg) (org-current-line)))
       (unless (functionp lang-f)
@@ -204,11 +217,13 @@ the edited version."
 	(insert code)
 	(remove-text-properties (point-min) (point-max)
 				'(display nil invisible nil intangible nil))
-	(org-do-remove-indentation)
+	(unless preserve-indentation
+	  (setq total-nindent (or (org-do-remove-indentation) 0)))
 	(let ((org-inhibit-startup t))
 	  (funcall lang-f))
 	(set (make-local-variable 'org-edit-src-force-single-line) single)
 	(set (make-local-variable 'org-edit-src-from-org-mode) org-mode-p)
+	(set (make-local-variable 'org-src-preserve-indentation) preserve-indentation)
 	(when lfmt
 	  (set (make-local-variable 'org-coderef-label-format) lfmt))
 	(when org-mode-p
@@ -216,10 +231,12 @@ the edited version."
 	  (while (re-search-forward "^," nil t)
 	    (replace-match "")))
 	(org-goto-line (1+ (- line begline)))
+	(org-move-to-column
+	 (if preserve-indentation col (max 0 (- col total-nindent))))
 	(org-set-local 'org-edit-src-beg-marker beg)
 	(org-set-local 'org-edit-src-end-marker end)
 	(org-set-local 'org-edit-src-overlay ovl)
-	(org-set-local 'org-edit-src-nindent nindent)
+	(org-set-local 'org-edit-src-block-indentation block-nindent)
 	(set-buffer-modified-p nil)
 	(and org-edit-src-persistent-message
@@ -263,13 +280,15 @@ exit with \\[org-edit-src-exit].  The edited text will then replace
 the fragment in the Org-mode buffer."
   (let ((line (org-current-line))
+	(col (current-column))
 	(case-fold-search t)
 	(msg (substitute-command-keys
 	      "Edit, then exit with C-c ' (C-c and single quote)"))
 	(org-mode-p (eq major-mode 'org-mode))
 	(beg (make-marker))
 	(end (make-marker))
-	nindent ovl beg1 end1 code begline buffer)
+	(preserve-indentation org-src-preserve-indentation)
+	block-nindent ovl beg1 end1 code begline buffer)
     (beginning-of-line 1)
     (if (looking-at "[ \t]*[^:\n \t]")
@@ -314,7 +333,7 @@ the fragment in the Org-mode buffer."
 	(insert code)
 	(remove-text-properties (point-min) (point-max)
 				'(display nil invisible nil intangible nil))
-	(setq nindent (org-do-remove-indentation))
+	(setq block-nindent (or (org-do-remove-indentation) 0))
 	 ((eq org-edit-fixed-width-region-mode 'artist-mode)
@@ -327,10 +346,13 @@ the fragment in the Org-mode buffer."
 	(while (re-search-forward "^[ \t]*: ?" nil t)
 	  (replace-match ""))
 	(org-goto-line (1+ (- line begline)))
+	(org-move-to-column (max 0 (- col block-nindent 2)))
 	(org-set-local 'org-edit-src-beg-marker beg)
 	(org-set-local 'org-edit-src-end-marker end)
 	(org-set-local 'org-edit-src-overlay ovl)
-	(org-set-local 'org-edit-src-nindent nindent)
+	(org-set-local 'org-edit-src-block-indentation block-nindent)
+	(org-set-local 'org-edit-src-content-indentation 0)
+	(org-set-local 'org-src-preserve-indentation nil)
 	(set-buffer-modified-p nil)
 	(and org-edit-src-persistent-message
@@ -341,7 +363,7 @@ the fragment in the Org-mode buffer."
 (defun org-edit-src-find-region-and-lang ()
   "Find the region and language for a local edit.
 Return a list with beginning and end of the region, a string representing
-the language, a switch telling of the content should be in a single line."
+the language, a switch telling if the content should be in a single line."
   (let ((re-list
@@ -422,7 +444,7 @@ the language, a switch telling of the content should be in a single line."
 	(match-string 1 s))))
 (defun org-edit-src-get-indentation (pos)
-  "Extract the label format."
+  "Count leading whitespace characters on line"
     (goto-char pos)
@@ -438,8 +460,10 @@ the language, a switch telling of the content should be in a single line."
 	 (buffer (current-buffer))
 	 (single (org-bound-and-true-p org-edit-src-force-single-line))
 	 (macro (eq single 'macro-definition))
-	 (nindent org-edit-src-nindent)
-	 code line)
+	 (total-nindent (+ (or org-edit-src-block-indentation 0)
+			   org-edit-src-content-indentation))
+	 (preserve-indentation org-src-preserve-indentation)
+	 code line col indent)
     (untabify (point-min) (point-max))
       (goto-char (point-min))
@@ -448,7 +472,8 @@ the language, a switch telling of the content should be in a single line."
 	(if (re-search-forward "\n[ \t\n]*\\'" nil t) (replace-match ""))))
     (setq line (if (org-bound-and-true-p org-edit-src-force-single-line)
-		 (org-current-line)))
+		 (org-current-line))
+	  col (current-column))
     (when single
       (goto-char (point-min))
       (if (re-search-forward "\\s-+\\'" nil t) (replace-match ""))
@@ -467,16 +492,18 @@ the language, a switch telling of the content should be in a single line."
 	      (if (org-mode-p) "^\\(.\\)" "^\\([*]\\|[ \t]*#\\+\\)") nil t)
 	(replace-match ",\\1")))
     (when (org-bound-and-true-p org-edit-src-picture)
+      (setq preserve-indentation nil)
       (untabify (point-min) (point-max))
       (goto-char (point-min))
       (while (re-search-forward "^" nil t)
 	(replace-match ": ")))
-    (when (and nindent (not single))
-      (setq nindent (make-string (+ org-edit-src-content-indentation nindent)
-				 ?\ ))
+    (unless (or single preserve-indentation (= total-nindent 0))
+      (setq indent (make-string total-nindent ?\ ))
       (goto-char (point-min))
       (while (re-search-forward "^" nil t)
-      (replace-match nindent)))
+	(replace-match indent)))
+    (if (org-bound-and-true-p org-edit-src-picture)
+	(setq total-nindent (+ total-nindent 2)))
     (setq code (buffer-string))
     (set-buffer-modified-p nil)
     (switch-to-buffer (marker-buffer beg))
@@ -487,6 +514,7 @@ the language, a switch telling of the content should be in a single line."
     (goto-char beg)
     (if single (just-one-space))
     (org-goto-line (1- (+ (org-current-line) line)))
+    (org-move-to-column (if preserve-indentation col (+ col total-nindent)))
     (move-marker beg nil)
     (move-marker end nil)))
--8<---------------cut here---------------end--------------->8---

             reply	other threads:[~2009-10-08 16:44 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2009-10-08 16:44 Dan Davison [this message]
2009-10-08 19:34 ` [PATCH] org-src code indentation, etc Carsten Dominik

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:

  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=878wfln8ct.fsf@stats.ox.ac.uk \
    --to=davison@stats.ox.ac.uk \
    --cc=emacs-orgmode@gnu.org \


* 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


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