* image-mode.el (image-mode): Add image-after-revert-hook to after-revert-hook.
[bpt/emacs.git] / lisp / org / org-src.el
index b7536b2..395c592 100644 (file)
@@ -1,13 +1,14 @@
 ;;; org-src.el --- Source code examples in Org
 ;;
-;; Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009
+;; Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010
 ;;   Free Software Foundation, Inc.
 ;;
 ;; Author: Carsten Dominik <carsten at orgmode dot org>
 ;;        Bastien Guerry <bzg AT altern DOT org>
+;;         Dan Davison <davison at stats dot ox dot ac dot uk>
 ;; Keywords: outlines, hypermedia, calendar, wp
 ;; Homepage: http://orgmode.org
-;; Version: 6.30c
+;; Version: 6.35i
 ;;
 ;; This file is part of GNU Emacs.
 ;;
 
 (require 'org-macs)
 (require 'org-compat)
+(eval-when-compile
+  (require 'cl))
 
 (declare-function org-do-remove-indentation "org" (&optional n))
+(declare-function org-at-table.el-p "org" ())
 (declare-function org-get-indentation "org" (&optional line))
+(declare-function org-switch-to-buffer-other-window "org" (&rest args))
 
 (defcustom org-edit-src-region-extra nil
   "Additional regexps to identify regions for editing with `org-edit-src-code'.
@@ -81,11 +86,23 @@ 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 on export, and when switching between the
+org buffer and the language mode edit buffer. If this variable
+is nil then, 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 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)
 
@@ -97,6 +114,24 @@ When nil, the message will only be shown intermittently in the echo area."
   :group 'org-edit-structure
   :type 'boolean)
 
+(defcustom org-src-window-setup 'reorganize-frame
+  "How the source code edit buffer should be displayed.
+Possible values for this option are:
+
+current-window    Show edit buffer in the current window, keeping all other
+                  windows.
+other-window      Use `switch-to-buffer-other-window' to display edit buffer.
+reorganize-frame  Show only two windows on the current frame, the current
+                  window and the edit buffer. When exiting the edit buffer,
+                  return to one window.
+other-frame       Use `switch-to-buffer-other-frame' to display edit buffer.
+                  Also, when exiting the edit buffer, kill that frame."
+  :group 'org-edit-structure
+  :type '(choice
+         (const current-window)
+         (const other-frame)
+         (const other-window)
+         (const reorganize-frame)))
 
 (defvar org-src-mode-hook nil
   "Hook  run after Org switched a source code snippet to its Emacs mode.
@@ -110,7 +145,8 @@ or similar things which you want to have when editing a source code file,
 but which mess up the display of a snippet in Org exported files.")
 
 (defcustom org-src-lang-modes
-  '(("ocaml" . tuareg) ("elisp" . emacs-lisp) ("ditaa" . artist))
+  '(("ocaml" . tuareg) ("elisp" . emacs-lisp) ("ditaa" . artist)
+    ("asymptote" . asy) ("dot" . fundamental))
   "Alist mapping languages to their major mode.
 The key is the language name, the value is the string that should
 be inserted as the name of the major mode.  For many languages this is
@@ -134,7 +170,14 @@ 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)
+(defvar org-edit-src-saved-temp-window-config nil)
+
+(defvar org-src-ask-before-returning-to-edit-buffer t
+  "If nil, when org-edit-src code is used on a block that already
+  has an active edit buffer, it will switch to that edit buffer
+  immediately; otherwise it will ask whether you want to return
+  to the existing edit buffer.")
 
 (define-minor-mode org-src-mode
   "Minor mode for language major mode buffers generated by org.
@@ -144,14 +187,18 @@ This minor mode is turned on in two situations:
 There is a mode hook, and keybindings for `org-edit-src-exit' and
 `org-edit-src-save'")
 
-(defun org-edit-src-code ()
+(defun org-edit-src-code (&optional context)
   "Edit the source code example at point.
 The example is copied to a separate buffer, and that buffer is switched
 to the correct language mode.  When done, exit with \\[org-edit-src-exit].
 This will remove the original code in the Org buffer, and replace it with
-the edited version."
+the edited version. Optional argument CONTEXT is used by
+\\[org-edit-src-save] when calling this function."
   (interactive)
+  (unless (eq context 'save)
+    (setq org-edit-src-saved-temp-window-config (current-window-configuration)))
   (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)"))
@@ -159,7 +206,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)
        nil
       (setq beg (move-marker beg (nth 0 info))
@@ -170,24 +218,31 @@ 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)))
+      (if (equal lang-f 'table.el-mode)
+         (setq lang-f (lambda ()
+                        (text-mode)
+                        (if (org-bound-and-true-p flyspell-mode)
+                            (flyspell-mode -1))
+                        (table-recognize)
+                        (org-set-local 'org-edit-src-content-indentation 0))))
       (unless (functionp lang-f)
        (error "No such language mode: %s" lang-f))
       (org-goto-line line)
       (if (and (setq buffer (org-edit-src-find-buffer beg end))
-              (y-or-n-p "Return to existing edit buffer? [n] will revert changes: "))
-         (switch-to-buffer buffer)
+              (if org-src-ask-before-returning-to-edit-buffer
+                  (y-or-n-p "Return to existing edit buffer? [n] will revert changes: ") t))
+         (org-src-switch-to-buffer buffer 'return)
        (when buffer
          (with-current-buffer buffer
            (if (boundp 'org-edit-src-overlay)
                (org-delete-overlay org-edit-src-overlay)))
          (kill-buffer buffer))
        (setq buffer (generate-new-buffer
-                     (concat "*Org Src " (file-name-nondirectory buffer-file-name) "[" lang "]*")))
+                     (org-src-construct-edit-buffer-name (buffer-name) lang)))
        (setq ovl (org-make-overlay beg end))
-       (org-overlay-put ovl 'face 'secondary-selection)
        (org-overlay-put ovl 'edit-buffer buffer)
        (org-overlay-put ovl 'help-echo "Click with mouse-1 to switch to buffer editing this segment")
        (org-overlay-put ovl 'face 'secondary-selection)
@@ -197,26 +252,33 @@ the edited version."
                           (define-key map [mouse-1] 'org-edit-src-continue)
                           map))
        (org-overlay-put ovl :read-only "Leave me alone")
-       (switch-to-buffer buffer)
+       (org-src-switch-to-buffer buffer 'edit)
+       (if (eq single 'macro-definition)
+           (setq code (replace-regexp-in-string "\\\\n" "\n" code t t)))
        (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
          (goto-char (point-min))
          (while (re-search-forward "^," nil t)
+           (if (eq (org-current-line) line) (setq total-nindent (1+ total-nindent)))
            (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)
        (org-src-mode)
        (set-buffer-modified-p nil)
        (and org-edit-src-persistent-message
@@ -228,16 +290,46 @@ the edited version."
   (interactive "e")
   (mouse-set-point e)
   (let ((buf (get-char-property (point) 'edit-buffer)))
-    (if buf (switch-to-buffer buf)
+    (if buf (org-src-switch-to-buffer buf 'continue)
       (error "Something is wrong here"))))
 
+(defun org-src-switch-to-buffer (buffer context)
+  (case org-src-window-setup
+    ('current-window
+     (switch-to-buffer buffer))
+    ('other-window
+     (switch-to-buffer-other-window buffer))
+    ('other-frame
+     (case context
+       ('exit
+       (let ((frame (selected-frame)))
+         (switch-to-buffer-other-frame buffer)
+         (delete-frame frame)))
+       ('save
+       (kill-buffer (current-buffer))
+       (switch-to-buffer buffer))
+       (t
+       (switch-to-buffer-other-frame buffer))))
+    ('reorganize-frame
+     (if (eq context 'edit) (delete-other-windows))
+     (org-switch-to-buffer-other-window buffer)
+     (if (eq context 'exit) (delete-other-windows)))
+    (t
+     (message "Invalid value %s for org-src-window-setup"
+             (symbol-name org-src-window-setup))
+     (switch-to-buffer buffer))))
+
+(defun org-src-construct-edit-buffer-name (org-buffer-name lang)
+  "Construct the buffer name for a source editing buffer"
+  (concat "*Org Src " org-buffer-name "[ " lang " ]*"))
+
 (defun org-edit-src-find-buffer (beg end)
   "Find a source editing buffer that is already editing the region BEG to END."
   (catch 'exit
     (mapc
      (lambda (b)
        (with-current-buffer b
-        (if (and (string-match "\\`*Org Edit " (buffer-name))
+        (if (and (string-match "\\`*Org Src " (buffer-name))
                  (local-variable-p 'org-edit-src-beg-marker (current-buffer))
                  (local-variable-p 'org-edit-src-end-marker (current-buffer))
                  (equal beg org-edit-src-beg-marker)
@@ -256,13 +348,15 @@ exit with \\[org-edit-src-exit].  The edited text will then replace
 the fragment in the Org-mode buffer."
   (interactive)
   (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]")
        nil
@@ -289,7 +383,9 @@ the fragment in the Org-mode buffer."
            (if (boundp 'org-edit-src-overlay)
                (org-delete-overlay org-edit-src-overlay)))
          (kill-buffer buffer))
-       (setq buffer (generate-new-buffer "*Org Edit Src Example*"))
+       (setq buffer (generate-new-buffer
+                     (org-src-construct-edit-buffer-name
+                      (buffer-name) "Fixed Width")))
        (setq ovl (org-make-overlay beg end))
        (org-overlay-put ovl 'face 'secondary-selection)
        (org-overlay-put ovl 'edit-buffer buffer)
@@ -305,7 +401,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))
        (cond
         ((eq org-edit-fixed-width-region-mode 'artist-mode)
          (fundamental-mode)
@@ -318,10 +414,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)
        (org-src-mode)
        (set-buffer-modified-p nil)
        (and org-edit-src-persistent-message
@@ -332,7 +431,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
         (append
          org-edit-src-region-extra
@@ -353,11 +452,22 @@ the language, a switch telling of the content should be in a single line."
            ("^[ \t]*#\\+ascii:" "\n" "fundamental" single-line)
            ("^[ \t]*#\\+begin_ascii.*\n" "\n[ \t]*#\\+end_ascii" "fundamental")
            ("^[ \t]*#\\+docbook:" "\n" "xml" single-line)
+           ("^[ \t]*#\\+macro:[ \t]+\\S-+\\( \\|$\\)"
+            "\n" "fundamental" macro-definition)
            ("^[ \t]*#\\+begin_docbook.*\n" "\n[ \t]*#\\+end_docbook" "xml")
            )))
        (pos (point))
        re1 re2 single beg end lang lfmt match-re1 ind entry)
     (catch 'exit
+      (when (org-at-table.el-p)
+       (re-search-backward "^[\t]*[^ \t|\\+]" nil t)
+       (setq beg (1+ (point-at-eol)))
+       (goto-char beg)
+       (or (re-search-forward "^[\t]*[^ \t|\\+]" nil t)
+           (progn (goto-char (point-max)) (newline)))
+       (setq end (point-at-bol))
+       (setq ind (org-edit-src-get-indentation beg))
+       (throw 'exit (list beg end 'table.el nil nil ind)))
       (while (setq entry (pop re-list))
        (setq re1 (car entry) re2 (nth 1 entry) lang (nth 2 entry)
              single (nth 3 entry))
@@ -411,74 +521,105 @@ 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"
   (save-match-data
     (goto-char pos)
     (org-get-indentation)))
 
-(defun org-edit-src-exit ()
+(defun org-edit-src-exit (&optional context)
   "Exit special edit and protect problematic lines."
   (interactive)
   (unless org-edit-src-from-org-mode
     (error "This is not a sub-editing buffer, something is wrong..."))
-  (let ((beg org-edit-src-beg-marker)
-       (end org-edit-src-end-marker)
-       (ovl org-edit-src-overlay)
-       (buffer (current-buffer))
-       (nindent org-edit-src-nindent)
-       code line)
-    (untabify (point-min) (point-max))
+  (widen)
+  (let* ((beg org-edit-src-beg-marker)
+        (end org-edit-src-end-marker)
+        (ovl org-edit-src-overlay)
+        (buffer (current-buffer))
+        (single (org-bound-and-true-p org-edit-src-force-single-line))
+        (macro (eq single 'macro-definition))
+        (total-nindent (+ (or org-edit-src-block-indentation 0)
+                          org-edit-src-content-indentation))
+        (preserve-indentation org-src-preserve-indentation)
+        (delta 0) code line col indent)
+    (unless preserve-indentation (untabify (point-min) (point-max)))
     (save-excursion
       (goto-char (point-min))
       (if (looking-at "[ \t\n]*\n") (replace-match ""))
-      (if (re-search-forward "\n[ \t\n]*\\'" nil t) (replace-match "")))
+      (unless macro
+       (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)
                   1
-                (org-current-line)))
-    (when (org-bound-and-true-p org-edit-src-force-single-line)
+                (org-current-line))
+         col (current-column))
+    (when single
+      (goto-char (point-min))
+      (if (re-search-forward "\\s-+\\'" nil t) (replace-match ""))
       (goto-char (point-min))
-      (while (re-search-forward "\n" nil t)
-       (replace-match " "))
+      (let ((cnt 0))
+       (while (re-search-forward "\n" nil t)
+         (setq cnt (1+ cnt))
+         (replace-match (if macro "\\n" " ") t t))
+       (when (and macro (> cnt 0))
+         (goto-char (point-max)) (insert "\\n")))
       (goto-char (point-min))
-      (if (looking-at "\\s-*") (replace-match " "))
-      (if (re-search-forward "\\s-+\\'" nil t)
-         (replace-match "")))
+      (if (looking-at "\\s-*") (replace-match " ")))
     (when (org-bound-and-true-p org-edit-src-from-org-mode)
       (goto-char (point-min))
       (while (re-search-forward
              (if (org-mode-p) "^\\(.\\)" "^\\([*]\\|[ \t]*#\\+\\)") nil t)
+       (if (eq (org-current-line) line) (setq delta (1+ delta)))
        (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 nindent
-      (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))
+    (org-src-switch-to-buffer (marker-buffer beg) (or context 'exit))
     (kill-buffer buffer)
     (goto-char beg)
     (delete-region beg end)
     (insert code)
     (goto-char beg)
-    (org-goto-line (1- (+ (org-current-line) line)))
+    (if single (just-one-space))
+    (if (memq t (mapcar (lambda (overlay)
+                         (eq (org-overlay-get overlay 'invisible)
+                             'org-hide-block))
+                       (org-overlays-at (point))))
+       ;; Block is hidden; put point at start of block
+       (beginning-of-line 0)
+      ;; Block is visible, put point where it was in the code buffer
+      (org-goto-line (1- (+ (org-current-line) line)))
+      (org-move-to-column (if preserve-indentation col (+ col total-nindent delta))))
     (move-marker beg nil)
-    (move-marker end nil)))
+    (move-marker end nil))
+  (unless (eq context 'save)
+    (when org-edit-src-saved-temp-window-config
+      (set-window-configuration org-edit-src-saved-temp-window-config)
+      (setq org-edit-src-saved-temp-window-config nil))))
 
 (defun org-edit-src-save ()
   "Save parent buffer with current state source-code buffer."
   (interactive)
   (let ((p (point)) (m (mark)) msg)
-    (org-edit-src-exit)
-    (save-buffer)
-    (setq msg (current-message))
-    (org-edit-src-code)
+    (save-window-excursion
+      (org-edit-src-exit 'save)
+      (save-buffer)
+      (setq msg (current-message))
+      (if (eq org-src-window-setup 'other-frame)
+         (let ((org-src-window-setup 'current-window))
+           (org-edit-src-code 'save))
+       (org-edit-src-code 'save)))
     (push-mark m 'nomessage)
     (goto-char (min p (point-max)))
     (message (or msg ""))))