Commit | Line | Data |
---|---|---|
c8d0cf5c CD |
1 | ;;; org-src.el --- Source code examples in Org |
2 | ;; | |
ba318903 | 3 | ;; Copyright (C) 2004-2014 Free Software Foundation, Inc. |
c8d0cf5c CD |
4 | ;; |
5 | ;; Author: Carsten Dominik <carsten at orgmode dot org> | |
271672fa | 6 | ;; Bastien Guerry <bzg@gnu.org> |
8bfe682a | 7 | ;; Dan Davison <davison at stats dot ox dot ac dot uk> |
c8d0cf5c CD |
8 | ;; Keywords: outlines, hypermedia, calendar, wp |
9 | ;; Homepage: http://orgmode.org | |
c8d0cf5c CD |
10 | ;; |
11 | ;; This file is part of GNU Emacs. | |
12 | ;; | |
13 | ;; GNU Emacs is free software: you can redistribute it and/or modify | |
14 | ;; it under the terms of the GNU General Public License as published by | |
15 | ;; the Free Software Foundation, either version 3 of the License, or | |
16 | ;; (at your option) any later version. | |
17 | ||
18 | ;; GNU Emacs is distributed in the hope that it will be useful, | |
19 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | |
20 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
21 | ;; GNU General Public License for more details. | |
22 | ||
23 | ;; You should have received a copy of the GNU General Public License | |
24 | ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. | |
25 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
26 | ;; | |
27 | ;;; Commentary: | |
28 | ||
29 | ;; This file contains the code dealing with source code examples in Org-mode. | |
30 | ||
31 | ;;; Code: | |
32 | ||
33 | (require 'org-macs) | |
34 | (require 'org-compat) | |
afe98dfa CD |
35 | (require 'ob-keys) |
36 | (require 'ob-comint) | |
8bfe682a CD |
37 | (eval-when-compile |
38 | (require 'cl)) | |
c8d0cf5c CD |
39 | |
40 | (declare-function org-do-remove-indentation "org" (&optional n)) | |
ed21c5c8 | 41 | (declare-function org-at-table.el-p "org" ()) |
c7cf0ebc BG |
42 | (declare-function org-in-src-block-p "org" (&optional inside)) |
43 | (declare-function org-in-block-p "org" (names)) | |
c8d0cf5c | 44 | (declare-function org-get-indentation "org" (&optional line)) |
8bfe682a | 45 | (declare-function org-switch-to-buffer-other-window "org" (&rest args)) |
14e1337f | 46 | (declare-function org-pop-to-buffer-same-window |
e66ba1df | 47 | "org-compat" (&optional buffer-or-name norecord label)) |
153ae947 | 48 | (declare-function org-base-buffer "org" (buffer)) |
c8d0cf5c CD |
49 | |
50 | (defcustom org-edit-src-region-extra nil | |
51 | "Additional regexps to identify regions for editing with `org-edit-src-code'. | |
52 | For examples see the function `org-edit-src-find-region-and-lang'. | |
53 | The regular expression identifying the begin marker should end with a newline, | |
54 | and the regexp marking the end line should start with a newline, to make sure | |
55 | there are kept outside the narrowed region." | |
56 | :group 'org-edit-structure | |
57 | :type '(repeat | |
58 | (list | |
59 | (regexp :tag "begin regexp") | |
60 | (regexp :tag "end regexp") | |
61 | (choice :tag "language" | |
62 | (string :tag "specify") | |
63 | (integer :tag "from match group") | |
64 | (const :tag "from `lang' element") | |
65 | (const :tag "from `style' element"))))) | |
66 | ||
271672fa BG |
67 | (defcustom org-edit-src-turn-on-auto-save nil |
68 | "Non-nil means turn `auto-save-mode' on when editing a source block. | |
69 | This will save the content of the source code editing buffer into | |
70 | a newly created file, not the base buffer for this source block. | |
71 | ||
d1389828 | 72 | If you want to regularly save the base buffer instead of the source |
271672fa BG |
73 | code editing buffer, see `org-edit-src-auto-save-idle-delay' instead." |
74 | :group 'org-edit-structure | |
75 | :version "24.4" | |
76 | :package-version '(Org . "8.0") | |
77 | :type 'boolean) | |
78 | ||
79 | (defcustom org-edit-src-auto-save-idle-delay 0 | |
80 | "Delay before saving a source code buffer back into its base buffer. | |
81 | When a positive integer N, save after N seconds of idle time. | |
82 | When 0 (the default), don't auto-save. | |
83 | ||
84 | If you want to save the source code buffer itself, don't use this. | |
85 | Check `org-edit-src-turn-on-auto-save' instead." | |
86 | :group 'org-edit-structure | |
87 | :version "24.4" | |
88 | :package-version '(Org . "8.0") | |
89 | :type 'integer) | |
90 | ||
c8d0cf5c CD |
91 | (defcustom org-coderef-label-format "(ref:%s)" |
92 | "The default coderef format. | |
93 | This format string will be used to search for coderef labels in literal | |
94 | examples (EXAMPLE and SRC blocks). The format can be overwritten in | |
86fbb8ca | 95 | an individual literal example with the -l option, like |
c8d0cf5c CD |
96 | |
97 | #+BEGIN_SRC pascal +n -r -l \"((%s))\" | |
98 | ... | |
99 | #+END_SRC | |
100 | ||
101 | If you want to use this for HTML export, make sure that the format does | |
102 | not introduce special font-locking, and avoid the HTML special | |
103 | characters `<', `>', and `&'. The reason for this restriction is that | |
104 | the labels are searched for only after htmlize has done its job." | |
105 | :group 'org-edit-structure ; FIXME this is not in the right group | |
106 | :type 'string) | |
107 | ||
108 | (defcustom org-edit-fixed-width-region-mode 'artist-mode | |
109 | "The mode that should be used to edit fixed-width regions. | |
110 | These are the regions where each line starts with a colon." | |
111 | :group 'org-edit-structure | |
112 | :type '(choice | |
113 | (const artist-mode) | |
114 | (const picture-mode) | |
115 | (const fundamental-mode) | |
116 | (function :tag "Other (specify)"))) | |
117 | ||
8bfe682a | 118 | (defcustom org-src-preserve-indentation nil |
86fbb8ca CD |
119 | "If non-nil preserve leading whitespace characters on export. |
120 | If non-nil leading whitespace characters in source code blocks | |
121 | are preserved on export, and when switching between the org | |
122 | buffer and the language mode edit buffer. If this variable is nil | |
123 | then, after editing with \\[org-edit-src-code], the | |
8bfe682a CD |
124 | minimum (across-lines) number of leading whitespace characters |
125 | are removed from all lines, and the code block is uniformly | |
126 | indented according to the value of `org-edit-src-content-indentation'." | |
127 | :group 'org-edit-structure | |
128 | :type 'boolean) | |
129 | ||
c8d0cf5c | 130 | (defcustom org-edit-src-content-indentation 2 |
8bfe682a | 131 | "Indentation for the content of a source code block. |
c8d0cf5c CD |
132 | This should be the number of spaces added to the indentation of the #+begin |
133 | line in order to compute the indentation of the block content after | |
86fbb8ca | 134 | editing it with \\[org-edit-src-code]. Has no effect if |
8bfe682a | 135 | `org-src-preserve-indentation' is non-nil." |
c8d0cf5c CD |
136 | :group 'org-edit-structure |
137 | :type 'integer) | |
138 | ||
afe98dfa | 139 | (defvar org-src-strip-leading-and-trailing-blank-lines nil |
8223b1d2 | 140 | "If non-nil, blank lines are removed when exiting the code edit buffer.") |
afe98dfa | 141 | |
c8d0cf5c CD |
142 | (defcustom org-edit-src-persistent-message t |
143 | "Non-nil means show persistent exit help message while editing src examples. | |
144 | The message is shown in the header-line, which will be created in the | |
3ab2c837 | 145 | first line of the window showing the editing buffer." |
c8d0cf5c CD |
146 | :group 'org-edit-structure |
147 | :type 'boolean) | |
148 | ||
8bfe682a CD |
149 | (defcustom org-src-window-setup 'reorganize-frame |
150 | "How the source code edit buffer should be displayed. | |
151 | Possible values for this option are: | |
152 | ||
153 | current-window Show edit buffer in the current window, keeping all other | |
154 | windows. | |
155 | other-window Use `switch-to-buffer-other-window' to display edit buffer. | |
156 | reorganize-frame Show only two windows on the current frame, the current | |
8223b1d2 | 157 | window and the edit buffer. When exiting the edit buffer, |
8bfe682a CD |
158 | return to one window. |
159 | other-frame Use `switch-to-buffer-other-frame' to display edit buffer. | |
160 | Also, when exiting the edit buffer, kill that frame." | |
161 | :group 'org-edit-structure | |
162 | :type '(choice | |
163 | (const current-window) | |
164 | (const other-frame) | |
165 | (const other-window) | |
166 | (const reorganize-frame))) | |
167 | ||
c8d0cf5c CD |
168 | (defvar org-src-mode-hook nil |
169 | "Hook run after Org switched a source code snippet to its Emacs mode. | |
170 | This hook will run | |
171 | ||
172 | - when editing a source code snippet with \"C-c '\". | |
173 | - When formatting a source code snippet for export with htmlize. | |
174 | ||
175 | You may want to use this hook for example to turn off `outline-minor-mode' | |
176 | or similar things which you want to have when editing a source code file, | |
177 | but which mess up the display of a snippet in Org exported files.") | |
178 | ||
54a0dee5 | 179 | (defcustom org-src-lang-modes |
8d642074 | 180 | '(("ocaml" . tuareg) ("elisp" . emacs-lisp) ("ditaa" . artist) |
afe98dfa | 181 | ("asymptote" . asy) ("dot" . fundamental) ("sqlite" . sql) |
271672fa | 182 | ("calc" . fundamental) ("C" . c) ("cpp" . c++) ("C++" . c++) |
153ae947 | 183 | ("screen" . shell-script)) |
54a0dee5 CD |
184 | "Alist mapping languages to their major mode. |
185 | The key is the language name, the value is the string that should | |
186 | be inserted as the name of the major mode. For many languages this is | |
187 | simple, but for language where this is not the case, this variable | |
188 | provides a way to simplify things on the user side. | |
189 | For example, there is no ocaml-mode in Emacs, but the mode to use is | |
190 | `tuareg-mode'." | |
191 | :group 'org-edit-structure | |
192 | :type '(repeat | |
193 | (cons | |
194 | (string "Language name") | |
195 | (symbol "Major mode")))) | |
196 | ||
c8d0cf5c CD |
197 | ;;; Editing source examples |
198 | ||
3ab2c837 BG |
199 | (defvar org-src-mode-map (make-sparse-keymap)) |
200 | (define-key org-src-mode-map "\C-c'" 'org-edit-src-exit) | |
271672fa | 201 | (define-key org-src-mode-map "\C-c\C-k" 'org-edit-src-abort) |
8223b1d2 | 202 | (define-key org-src-mode-map "\C-x\C-s" 'org-edit-src-save) |
afe98dfa | 203 | |
c8d0cf5c CD |
204 | (defvar org-edit-src-force-single-line nil) |
205 | (defvar org-edit-src-from-org-mode nil) | |
86fbb8ca | 206 | (defvar org-edit-src-allow-write-back-p t) |
c8d0cf5c CD |
207 | (defvar org-edit-src-picture nil) |
208 | (defvar org-edit-src-beg-marker nil) | |
209 | (defvar org-edit-src-end-marker nil) | |
210 | (defvar org-edit-src-overlay nil) | |
8bfe682a CD |
211 | (defvar org-edit-src-block-indentation nil) |
212 | (defvar org-edit-src-saved-temp-window-config nil) | |
213 | ||
271672fa | 214 | (defcustom org-src-ask-before-returning-to-edit-buffer t |
8bfe682a | 215 | "If nil, when org-edit-src code is used on a block that already |
8223b1d2 BG |
216 | has an active edit buffer, it will switch to that edit buffer |
217 | immediately; otherwise it will ask whether you want to return to | |
271672fa BG |
218 | the existing edit buffer." |
219 | :group 'org-edit-structure | |
220 | :version "24.4" | |
221 | :package-version '(Org . "8.0") | |
222 | :type 'boolean) | |
c8d0cf5c | 223 | |
afe98dfa CD |
224 | (defvar org-src-babel-info nil) |
225 | ||
c8d0cf5c CD |
226 | (define-minor-mode org-src-mode |
227 | "Minor mode for language major mode buffers generated by org. | |
228 | This minor mode is turned on in two situations: | |
229 | - when editing a source code snippet with \"C-c '\". | |
230 | - When formatting a source code snippet for export with htmlize. | |
231 | There is a mode hook, and keybindings for `org-edit-src-exit' and | |
232 | `org-edit-src-save'") | |
233 | ||
271672fa | 234 | (defvar org-edit-src-code-timer nil) |
3ab2c837 | 235 | (defun org-edit-src-code (&optional context code edit-buffer-name) |
c7cf0ebc BG |
236 | "Edit the source CODE block at point. |
237 | The code is copied to a separate buffer and the appropriate mode | |
238 | is turned on. When done, exit with \\[org-edit-src-exit]. This will | |
239 | remove the original code in the Org buffer, and replace it with the | |
240 | edited version. An optional argument CONTEXT is used by \\[org-edit-src-save] | |
241 | when calling this function. See `org-src-window-setup' to configure | |
242 | the display of windows containing the Org buffer and the code buffer." | |
c8d0cf5c | 243 | (interactive) |
6d3e4c80 BG |
244 | (if (not (or (org-in-block-p '("src" "example" "latex" "html")) |
245 | (org-at-table.el-p))) | |
c7cf0ebc BG |
246 | (user-error "Not in a source code or example block") |
247 | (unless (eq context 'save) | |
248 | (setq org-edit-src-saved-temp-window-config (current-window-configuration))) | |
249 | (let* ((mark (and (org-region-active-p) (mark))) | |
250 | (case-fold-search t) | |
251 | (info | |
252 | ;; If the src region consists in no lines, we insert a blank | |
253 | ;; line. | |
254 | (let* ((temp (org-edit-src-find-region-and-lang)) | |
255 | (beg (nth 0 temp)) | |
256 | (end (nth 1 temp))) | |
257 | (if (>= end beg) temp | |
258 | (goto-char beg) | |
259 | (insert "\n") | |
260 | (org-edit-src-find-region-and-lang)))) | |
261 | (full-info (org-babel-get-src-block-info 'light)) | |
262 | (org-mode-p (derived-mode-p 'org-mode)) ;; derived-mode-p is reflexive | |
263 | (beg (make-marker)) | |
264 | ;; Move marker with inserted text for case when src block is | |
265 | ;; just one empty line, i.e. beg == end. | |
266 | (end (copy-marker (make-marker) t)) | |
267 | (allow-write-back-p (null code)) | |
268 | block-nindent total-nindent ovl lang lang-f single lfmt buffer msg | |
269 | begline markline markcol line col transmitted-variables) | |
c8d0cf5c CD |
270 | (setq beg (move-marker beg (nth 0 info)) |
271 | end (move-marker end (nth 1 info)) | |
86fbb8ca CD |
272 | msg (if allow-write-back-p |
273 | (substitute-command-keys | |
271672fa BG |
274 | "Edit, then exit with C-c ' (C-c and single quote) -- C-c C-k to abort") |
275 | "Exit with C-c ' (C-c and single quote) -- C-c C-k to abort") | |
86fbb8ca | 276 | code (or code (buffer-substring-no-properties beg end)) |
54a0dee5 | 277 | lang (or (cdr (assoc (nth 2 info) org-src-lang-modes)) |
c7cf0ebc | 278 | (nth 2 info)) |
54a0dee5 | 279 | lang (if (symbolp lang) (symbol-name lang) lang) |
c8d0cf5c | 280 | single (nth 3 info) |
8bfe682a | 281 | block-nindent (nth 5 info) |
c8d0cf5c | 282 | lang-f (intern (concat lang "-mode")) |
3ab2c837 BG |
283 | begline (save-excursion (goto-char beg) (org-current-line)) |
284 | transmitted-variables | |
285 | `((org-edit-src-content-indentation | |
286 | ,org-edit-src-content-indentation) | |
287 | (org-edit-src-force-single-line ,single) | |
288 | (org-edit-src-from-org-mode ,org-mode-p) | |
289 | (org-edit-src-allow-write-back-p ,allow-write-back-p) | |
290 | (org-src-preserve-indentation ,org-src-preserve-indentation) | |
291 | (org-src-babel-info ,(org-babel-get-src-block-info 'light)) | |
292 | (org-coderef-label-format | |
293 | ,(or (nth 4 info) org-coderef-label-format)) | |
294 | (org-edit-src-beg-marker ,beg) | |
295 | (org-edit-src-end-marker ,end) | |
296 | (org-edit-src-block-indentation ,block-nindent))) | |
acedf35c CD |
297 | (if (and mark (>= mark beg) (<= mark (1+ end))) |
298 | (save-excursion (goto-char (min mark end)) | |
afe98dfa CD |
299 | (setq markline (org-current-line) |
300 | markcol (current-column)))) | |
ed21c5c8 CD |
301 | (if (equal lang-f 'table.el-mode) |
302 | (setq lang-f (lambda () | |
303 | (text-mode) | |
304 | (if (org-bound-and-true-p flyspell-mode) | |
305 | (flyspell-mode -1)) | |
306 | (table-recognize) | |
307 | (org-set-local 'org-edit-src-content-indentation 0)))) | |
c8d0cf5c CD |
308 | (unless (functionp lang-f) |
309 | (error "No such language mode: %s" lang-f)) | |
afe98dfa CD |
310 | (save-excursion |
311 | (if (> (point) end) (goto-char end)) | |
312 | (setq line (org-current-line) | |
313 | col (current-column))) | |
c8d0cf5c | 314 | (if (and (setq buffer (org-edit-src-find-buffer beg end)) |
8223b1d2 BG |
315 | (or (eq context 'save) |
316 | (if org-src-ask-before-returning-to-edit-buffer | |
317 | (y-or-n-p "Return to existing edit buffer ([n] will revert changes)? ") t))) | |
8bfe682a | 318 | (org-src-switch-to-buffer buffer 'return) |
c8d0cf5c CD |
319 | (when buffer |
320 | (with-current-buffer buffer | |
321 | (if (boundp 'org-edit-src-overlay) | |
86fbb8ca | 322 | (delete-overlay org-edit-src-overlay))) |
c8d0cf5c | 323 | (kill-buffer buffer)) |
54a0dee5 | 324 | (setq buffer (generate-new-buffer |
86fbb8ca CD |
325 | (or edit-buffer-name |
326 | (org-src-construct-edit-buffer-name (buffer-name) lang)))) | |
327 | (setq ovl (make-overlay beg end)) | |
328 | (overlay-put ovl 'edit-buffer buffer) | |
329 | (overlay-put ovl 'help-echo "Click with mouse-1 to switch to buffer editing this segment") | |
330 | (overlay-put ovl 'face 'secondary-selection) | |
331 | (overlay-put ovl | |
332 | 'keymap | |
333 | (let ((map (make-sparse-keymap))) | |
334 | (define-key map [mouse-1] 'org-edit-src-continue) | |
335 | map)) | |
336 | (overlay-put ovl :read-only "Leave me alone") | |
3ab2c837 BG |
337 | (setq transmitted-variables |
338 | (append transmitted-variables `((org-edit-src-overlay ,ovl)))) | |
8bfe682a CD |
339 | (org-src-switch-to-buffer buffer 'edit) |
340 | (if (eq single 'macro-definition) | |
341 | (setq code (replace-regexp-in-string "\\\\n" "\n" code t t))) | |
c8d0cf5c CD |
342 | (insert code) |
343 | (remove-text-properties (point-min) (point-max) | |
344 | '(display nil invisible nil intangible nil)) | |
3ab2c837 | 345 | (unless (cadr (assq 'org-src-preserve-indentation transmitted-variables)) |
8bfe682a | 346 | (setq total-nindent (or (org-do-remove-indentation) 0))) |
c8d0cf5c | 347 | (let ((org-inhibit-startup t)) |
afe98dfa CD |
348 | (condition-case e |
349 | (funcall lang-f) | |
350 | (error | |
30cb51f1 | 351 | (message "Language mode `%s' fails with: %S" lang-f (nth 1 e))))) |
3ab2c837 BG |
352 | (dolist (pair transmitted-variables) |
353 | (org-set-local (car pair) (cadr pair))) | |
bdebdb64 BG |
354 | ;; Remove protecting commas from visible part of buffer. |
355 | (org-unescape-code-in-region (point-min) (point-max)) | |
afe98dfa CD |
356 | (when markline |
357 | (org-goto-line (1+ (- markline begline))) | |
358 | (org-move-to-column | |
3ab2c837 BG |
359 | (if org-src-preserve-indentation markcol |
360 | (max 0 (- markcol total-nindent)))) | |
afe98dfa CD |
361 | (push-mark (point) 'no-message t) |
362 | (setq deactivate-mark nil)) | |
54a0dee5 | 363 | (org-goto-line (1+ (- line begline))) |
8bfe682a | 364 | (org-move-to-column |
3ab2c837 | 365 | (if org-src-preserve-indentation col (max 0 (- col total-nindent)))) |
54a0dee5 CD |
366 | (org-src-mode) |
367 | (set-buffer-modified-p nil) | |
8223b1d2 | 368 | (setq buffer-file-name nil) |
271672fa BG |
369 | (when org-edit-src-turn-on-auto-save |
370 | (setq buffer-auto-save-file-name | |
371 | (concat (make-temp-name "org-src-") | |
372 | (format-time-string "-%Y-%d-%m") ".txt"))) | |
c8d0cf5c | 373 | (and org-edit-src-persistent-message |
3ab2c837 BG |
374 | (org-set-local 'header-line-format msg)) |
375 | (let ((edit-prep-func (intern (concat "org-babel-edit-prep:" lang)))) | |
376 | (when (fboundp edit-prep-func) | |
271672fa BG |
377 | (funcall edit-prep-func full-info))) |
378 | (or org-edit-src-code-timer | |
379 | (setq org-edit-src-code-timer | |
380 | (unless (zerop org-edit-src-auto-save-idle-delay) | |
381 | (run-with-idle-timer | |
382 | org-edit-src-auto-save-idle-delay t | |
383 | (lambda () | |
384 | (cond | |
385 | ((and (string-match "\*Org Src" (buffer-name)) | |
386 | (buffer-modified-p)) | |
387 | (org-edit-src-save)) | |
388 | ((not | |
389 | (delq nil (mapcar | |
390 | (lambda (b) | |
391 | (string-match "\*Org Src" (buffer-name b))) | |
392 | (buffer-list)))) | |
393 | (cancel-timer org-edit-src-code-timer) | |
394 | (setq org-edit-src-code-timer))))))))) | |
395 | t))) | |
c8d0cf5c CD |
396 | |
397 | (defun org-edit-src-continue (e) | |
8223b1d2 | 398 | "Continue editing source blocks." ;; Fixme: be more accurate |
c8d0cf5c CD |
399 | (interactive "e") |
400 | (mouse-set-point e) | |
401 | (let ((buf (get-char-property (point) 'edit-buffer))) | |
8bfe682a | 402 | (if buf (org-src-switch-to-buffer buf 'continue) |
c8d0cf5c CD |
403 | (error "Something is wrong here")))) |
404 | ||
8bfe682a CD |
405 | (defun org-src-switch-to-buffer (buffer context) |
406 | (case org-src-window-setup | |
3ab2c837 | 407 | ('current-window |
e66ba1df | 408 | (org-pop-to-buffer-same-window buffer)) |
3ab2c837 | 409 | ('other-window |
8bfe682a | 410 | (switch-to-buffer-other-window buffer)) |
3ab2c837 | 411 | ('other-frame |
8bfe682a | 412 | (case context |
3ab2c837 | 413 | ('exit |
8bfe682a CD |
414 | (let ((frame (selected-frame))) |
415 | (switch-to-buffer-other-frame buffer) | |
416 | (delete-frame frame))) | |
3ab2c837 | 417 | ('save |
8bfe682a | 418 | (kill-buffer (current-buffer)) |
e66ba1df | 419 | (org-pop-to-buffer-same-window buffer)) |
8bfe682a CD |
420 | (t |
421 | (switch-to-buffer-other-frame buffer)))) | |
3ab2c837 | 422 | ('reorganize-frame |
8bfe682a CD |
423 | (if (eq context 'edit) (delete-other-windows)) |
424 | (org-switch-to-buffer-other-window buffer) | |
425 | (if (eq context 'exit) (delete-other-windows))) | |
3ab2c837 | 426 | ('switch-invisibly |
afe98dfa | 427 | (set-buffer buffer)) |
8bfe682a CD |
428 | (t |
429 | (message "Invalid value %s for org-src-window-setup" | |
430 | (symbol-name org-src-window-setup)) | |
e66ba1df | 431 | (org-pop-to-buffer-same-window buffer)))) |
8bfe682a | 432 | |
8d642074 | 433 | (defun org-src-construct-edit-buffer-name (org-buffer-name lang) |
86fbb8ca | 434 | "Construct the buffer name for a source editing buffer." |
8d642074 CD |
435 | (concat "*Org Src " org-buffer-name "[ " lang " ]*")) |
436 | ||
153ae947 BG |
437 | (defun org-src-edit-buffer-p (&optional buffer) |
438 | "Test whether BUFFER (or the current buffer if BUFFER is nil) | |
439 | is a source block editing buffer." | |
440 | (let ((buffer (org-base-buffer (or buffer (current-buffer))))) | |
441 | (and (buffer-name buffer) | |
442 | (string-match "\\`*Org Src " (buffer-name buffer)) | |
443 | (local-variable-p 'org-edit-src-beg-marker buffer) | |
444 | (local-variable-p 'org-edit-src-end-marker buffer)))) | |
445 | ||
c8d0cf5c CD |
446 | (defun org-edit-src-find-buffer (beg end) |
447 | "Find a source editing buffer that is already editing the region BEG to END." | |
448 | (catch 'exit | |
449 | (mapc | |
450 | (lambda (b) | |
451 | (with-current-buffer b | |
8d642074 | 452 | (if (and (string-match "\\`*Org Src " (buffer-name)) |
c8d0cf5c CD |
453 | (local-variable-p 'org-edit-src-beg-marker (current-buffer)) |
454 | (local-variable-p 'org-edit-src-end-marker (current-buffer)) | |
455 | (equal beg org-edit-src-beg-marker) | |
456 | (equal end org-edit-src-end-marker)) | |
457 | (throw 'exit (current-buffer))))) | |
458 | (buffer-list)) | |
459 | nil)) | |
460 | ||
461 | (defun org-edit-fixed-width-region () | |
462 | "Edit the fixed-width ascii drawing at point. | |
463 | This must be a region where each line starts with a colon followed by | |
464 | a space character. | |
465 | An new buffer is created and the fixed-width region is copied into it, | |
466 | and the buffer is switched into `artist-mode' for editing. When done, | |
467 | exit with \\[org-edit-src-exit]. The edited text will then replace | |
468 | the fragment in the Org-mode buffer." | |
469 | (interactive) | |
470 | (let ((line (org-current-line)) | |
8bfe682a | 471 | (col (current-column)) |
c8d0cf5c CD |
472 | (case-fold-search t) |
473 | (msg (substitute-command-keys | |
271672fa | 474 | "Edit, then exit with C-c ' (C-c and single quote) -- C-c C-k to abort")) |
8223b1d2 | 475 | (org-mode-p (derived-mode-p 'org-mode)) |
c8d0cf5c CD |
476 | (beg (make-marker)) |
477 | (end (make-marker)) | |
8bfe682a CD |
478 | (preserve-indentation org-src-preserve-indentation) |
479 | block-nindent ovl beg1 end1 code begline buffer) | |
c8d0cf5c CD |
480 | (beginning-of-line 1) |
481 | (if (looking-at "[ \t]*[^:\n \t]") | |
482 | nil | |
483 | (if (looking-at "[ \t]*\\(\n\\|\\'\\)") | |
484 | (setq beg1 (point) end1 beg1) | |
485 | (save-excursion | |
486 | (if (re-search-backward "^[ \t]*[^: \t]" nil 'move) | |
487 | (setq beg1 (point-at-bol 2)) | |
488 | (setq beg1 (point)))) | |
489 | (save-excursion | |
490 | (if (re-search-forward "^[ \t]*[^: \t]" nil 'move) | |
491 | (setq end1 (1- (match-beginning 0))) | |
492 | (setq end1 (point)))) | |
54a0dee5 | 493 | (org-goto-line line)) |
c8d0cf5c CD |
494 | (setq beg (move-marker beg beg1) |
495 | end (move-marker end end1) | |
496 | code (buffer-substring-no-properties beg end) | |
497 | begline (save-excursion (goto-char beg) (org-current-line))) | |
498 | (if (and (setq buffer (org-edit-src-find-buffer beg end)) | |
8223b1d2 | 499 | (y-or-n-p "Return to existing edit buffer ([n] will revert changes)? ")) |
e66ba1df | 500 | (org-pop-to-buffer-same-window buffer) |
c8d0cf5c CD |
501 | (when buffer |
502 | (with-current-buffer buffer | |
503 | (if (boundp 'org-edit-src-overlay) | |
86fbb8ca | 504 | (delete-overlay org-edit-src-overlay))) |
c8d0cf5c | 505 | (kill-buffer buffer)) |
8d642074 CD |
506 | (setq buffer (generate-new-buffer |
507 | (org-src-construct-edit-buffer-name | |
508 | (buffer-name) "Fixed Width"))) | |
86fbb8ca CD |
509 | (setq ovl (make-overlay beg end)) |
510 | (overlay-put ovl 'face 'secondary-selection) | |
511 | (overlay-put ovl 'edit-buffer buffer) | |
512 | (overlay-put ovl 'help-echo "Click with mouse-1 to switch to buffer editing this segment") | |
513 | (overlay-put ovl 'face 'secondary-selection) | |
514 | (overlay-put ovl | |
8223b1d2 BG |
515 | 'keymap |
516 | (let ((map (make-sparse-keymap))) | |
517 | (define-key map [mouse-1] 'org-edit-src-continue) | |
518 | map)) | |
86fbb8ca | 519 | (overlay-put ovl :read-only "Leave me alone") |
e66ba1df | 520 | (org-pop-to-buffer-same-window buffer) |
c8d0cf5c CD |
521 | (insert code) |
522 | (remove-text-properties (point-min) (point-max) | |
523 | '(display nil invisible nil intangible nil)) | |
8bfe682a | 524 | (setq block-nindent (or (org-do-remove-indentation) 0)) |
c8d0cf5c CD |
525 | (cond |
526 | ((eq org-edit-fixed-width-region-mode 'artist-mode) | |
527 | (fundamental-mode) | |
528 | (artist-mode 1)) | |
86fbb8ca | 529 | (t (funcall org-edit-fixed-width-region-mode))) |
c8d0cf5c CD |
530 | (set (make-local-variable 'org-edit-src-force-single-line) nil) |
531 | (set (make-local-variable 'org-edit-src-from-org-mode) org-mode-p) | |
532 | (set (make-local-variable 'org-edit-src-picture) t) | |
533 | (goto-char (point-min)) | |
534 | (while (re-search-forward "^[ \t]*: ?" nil t) | |
535 | (replace-match "")) | |
54a0dee5 | 536 | (org-goto-line (1+ (- line begline))) |
8bfe682a | 537 | (org-move-to-column (max 0 (- col block-nindent 2))) |
c8d0cf5c CD |
538 | (org-set-local 'org-edit-src-beg-marker beg) |
539 | (org-set-local 'org-edit-src-end-marker end) | |
540 | (org-set-local 'org-edit-src-overlay ovl) | |
8bfe682a CD |
541 | (org-set-local 'org-edit-src-block-indentation block-nindent) |
542 | (org-set-local 'org-edit-src-content-indentation 0) | |
543 | (org-set-local 'org-src-preserve-indentation nil) | |
54a0dee5 CD |
544 | (org-src-mode) |
545 | (set-buffer-modified-p nil) | |
c8d0cf5c CD |
546 | (and org-edit-src-persistent-message |
547 | (org-set-local 'header-line-format msg))) | |
548 | (message "%s" msg) | |
549 | t))) | |
550 | ||
551 | (defun org-edit-src-find-region-and-lang () | |
552 | "Find the region and language for a local edit. | |
553 | Return a list with beginning and end of the region, a string representing | |
8bfe682a | 554 | the language, a switch telling if the content should be in a single line." |
c8d0cf5c CD |
555 | (let ((re-list |
556 | (append | |
557 | org-edit-src-region-extra | |
558 | '( | |
559 | ("<src\\>[^<]*>[ \t]*\n?" "\n?[ \t]*</src>" lang) | |
560 | ("<literal\\>[^<]*>[ \t]*\n?" "\n?[ \t]*</literal>" style) | |
561 | ("<example>[ \t]*\n?" "\n?[ \t]*</example>" "fundamental") | |
562 | ("<lisp>[ \t]*\n?" "\n?[ \t]*</lisp>" "emacs-lisp") | |
563 | ("<perl>[ \t]*\n?" "\n?[ \t]*</perl>" "perl") | |
564 | ("<python>[ \t]*\n?" "\n?[ \t]*</python>" "python") | |
565 | ("<ruby>[ \t]*\n?" "\n?[ \t]*</ruby>" "ruby") | |
566 | ("^[ \t]*#\\+begin_src\\( \\([^ \t\n]+\\)\\)?.*\n" "\n[ \t]*#\\+end_src" 2) | |
567 | ("^[ \t]*#\\+begin_example.*\n" "\n[ \t]*#\\+end_example" "fundamental") | |
568 | ("^[ \t]*#\\+html:" "\n" "html" single-line) | |
569 | ("^[ \t]*#\\+begin_html.*\n" "\n[ \t]*#\\+end_html" "html") | |
570 | ("^[ \t]*#\\+latex:" "\n" "latex" single-line) | |
571 | ("^[ \t]*#\\+begin_latex.*\n" "\n[ \t]*#\\+end_latex" "latex") | |
572 | ("^[ \t]*#\\+ascii:" "\n" "fundamental" single-line) | |
573 | ("^[ \t]*#\\+begin_ascii.*\n" "\n[ \t]*#\\+end_ascii" "fundamental") | |
8bfe682a CD |
574 | ("^[ \t]*#\\+macro:[ \t]+\\S-+\\( \\|$\\)" |
575 | "\n" "fundamental" macro-definition) | |
c8d0cf5c CD |
576 | ))) |
577 | (pos (point)) | |
578 | re1 re2 single beg end lang lfmt match-re1 ind entry) | |
579 | (catch 'exit | |
580 | (while (setq entry (pop re-list)) | |
581 | (setq re1 (car entry) re2 (nth 1 entry) lang (nth 2 entry) | |
582 | single (nth 3 entry)) | |
583 | (save-excursion | |
584 | (if (or (looking-at re1) | |
585 | (re-search-backward re1 nil t)) | |
586 | (progn | |
587 | (setq match-re1 (match-string 0)) | |
588 | (setq beg (match-end 0) | |
589 | lang (org-edit-src-get-lang lang) | |
590 | lfmt (org-edit-src-get-label-format match-re1) | |
591 | ind (org-edit-src-get-indentation (match-beginning 0))) | |
592 | (if (and (re-search-forward re2 nil t) | |
593 | (>= (match-end 0) pos)) | |
594 | (throw 'exit (list beg (match-beginning 0) | |
595 | lang single lfmt ind)))) | |
596 | (if (or (looking-at re2) | |
597 | (re-search-forward re2 nil t)) | |
598 | (progn | |
599 | (setq end (match-beginning 0)) | |
600 | (if (and (re-search-backward re1 nil t) | |
601 | (<= (match-beginning 0) pos)) | |
602 | (progn | |
603 | (setq lfmt (org-edit-src-get-label-format | |
604 | (match-string 0)) | |
605 | ind (org-edit-src-get-indentation | |
606 | (match-beginning 0))) | |
607 | (throw 'exit | |
608 | (list (match-end 0) end | |
609 | (org-edit-src-get-lang lang) | |
30cb51f1 BG |
610 | single lfmt ind))))))))) |
611 | (when (org-at-table.el-p) | |
612 | (re-search-backward "^[\t]*[^ \t|\\+]" nil t) | |
613 | (setq beg (1+ (point-at-eol))) | |
614 | (goto-char beg) | |
615 | (or (re-search-forward "^[\t]*[^ \t|\\+]" nil t) | |
616 | (progn (goto-char (point-max)) (newline))) | |
617 | (setq end (1- (point-at-bol))) | |
618 | (throw 'exit (list beg end 'table.el nil nil 0)))))) | |
c8d0cf5c CD |
619 | |
620 | (defun org-edit-src-get-lang (lang) | |
621 | "Extract the src language." | |
622 | (let ((m (match-string 0))) | |
623 | (cond | |
624 | ((stringp lang) lang) | |
625 | ((integerp lang) (match-string lang)) | |
626 | ((and (eq lang 'lang) | |
627 | (string-match "\\<lang=\"\\([^ \t\n\"]+\\)\"" m)) | |
628 | (match-string 1 m)) | |
629 | ((and (eq lang 'style) | |
630 | (string-match "\\<style=\"\\([^ \t\n\"]+\\)\"" m)) | |
631 | (match-string 1 m)) | |
632 | (t "fundamental")))) | |
633 | ||
634 | (defun org-edit-src-get-label-format (s) | |
635 | "Extract the label format." | |
636 | (save-match-data | |
637 | (if (string-match "-l[ \t]+\\\\?\"\\([^\t\r\n\"]+\\)\\\\?\"" s) | |
638 | (match-string 1 s)))) | |
639 | ||
640 | (defun org-edit-src-get-indentation (pos) | |
86fbb8ca | 641 | "Count leading whitespace characters on line." |
c8d0cf5c CD |
642 | (save-match-data |
643 | (goto-char pos) | |
644 | (org-get-indentation))) | |
645 | ||
bdebdb64 BG |
646 | (defun org-escape-code-in-region (beg end) |
647 | "Escape lines between BEG and END. | |
648 | Escaping happens when a line starts with \"*\", \"#+\", \",*\" or | |
649 | \",#+\" by appending a comma to it." | |
8223b1d2 | 650 | (interactive "r") |
bdebdb64 BG |
651 | (save-excursion |
652 | (goto-char beg) | |
653 | (while (re-search-forward "^[ \t]*,?\\(\\*\\|#\\+\\)" end t) | |
654 | (replace-match ",\\1" nil nil nil 1)))) | |
655 | ||
656 | (defun org-escape-code-in-string (s) | |
657 | "Escape lines in string S. | |
658 | Escaping happens when a line starts with \"*\", \"#+\", \",*\" or | |
659 | \",#+\" by appending a comma to it." | |
660 | (replace-regexp-in-string "^[ \t]*,?\\(\\*\\|#\\+\\)" ",\\1" s nil nil 1)) | |
661 | ||
662 | (defun org-unescape-code-in-region (beg end) | |
663 | "Un-escape lines between BEG and END. | |
664 | Un-escaping happens by removing the first comma on lines starting | |
665 | with \",*\", \",#+\", \",,*\" and \",,#+\"." | |
666 | (interactive "r") | |
667 | (save-excursion | |
668 | (goto-char beg) | |
669 | (while (re-search-forward "^[ \t]*,?\\(,\\)\\(?:\\*\\|#\\+\\)" end t) | |
670 | (replace-match "" nil nil nil 1)))) | |
671 | ||
672 | (defun org-unescape-code-in-string (s) | |
673 | "Un-escape lines in string S. | |
674 | Un-escaping happens by removing the first comma on lines starting | |
675 | with \",*\", \",#+\", \",,*\" and \",,#+\"." | |
676 | (replace-regexp-in-string | |
677 | "^[ \t]*,?\\(,\\)\\(?:\\*\\|#\\+\\)" "" s nil nil 1)) | |
8223b1d2 | 678 | |
8bfe682a | 679 | (defun org-edit-src-exit (&optional context) |
c8d0cf5c CD |
680 | "Exit special edit and protect problematic lines." |
681 | (interactive) | |
86fbb8ca CD |
682 | (unless (org-bound-and-true-p org-edit-src-from-org-mode) |
683 | (error "This is not a sub-editing buffer, something is wrong")) | |
ed21c5c8 | 684 | (widen) |
c7cf0ebc BG |
685 | (let* ((fixed-width-p (string-match "Fixed Width" (buffer-name))) |
686 | (beg org-edit-src-beg-marker) | |
8bfe682a CD |
687 | (end org-edit-src-end-marker) |
688 | (ovl org-edit-src-overlay) | |
8223b1d2 | 689 | (bufstr (buffer-string)) |
8bfe682a CD |
690 | (buffer (current-buffer)) |
691 | (single (org-bound-and-true-p org-edit-src-force-single-line)) | |
692 | (macro (eq single 'macro-definition)) | |
693 | (total-nindent (+ (or org-edit-src-block-indentation 0) | |
694 | org-edit-src-content-indentation)) | |
695 | (preserve-indentation org-src-preserve-indentation) | |
86fbb8ca | 696 | (allow-write-back-p (org-bound-and-true-p org-edit-src-allow-write-back-p)) |
8bfe682a | 697 | (delta 0) code line col indent) |
86fbb8ca CD |
698 | (when allow-write-back-p |
699 | (unless preserve-indentation (untabify (point-min) (point-max))) | |
afe98dfa CD |
700 | (if org-src-strip-leading-and-trailing-blank-lines |
701 | (save-excursion | |
702 | (goto-char (point-min)) | |
703 | (if (looking-at "[ \t\n]*\n") (replace-match "")) | |
704 | (unless macro | |
705 | (if (re-search-forward "\n[ \t\n]*\\'" nil t) (replace-match "")))))) | |
c8d0cf5c CD |
706 | (setq line (if (org-bound-and-true-p org-edit-src-force-single-line) |
707 | 1 | |
8bfe682a CD |
708 | (org-current-line)) |
709 | col (current-column)) | |
86fbb8ca CD |
710 | (when allow-write-back-p |
711 | (when single | |
712 | (goto-char (point-min)) | |
713 | (if (re-search-forward "\\s-+\\'" nil t) (replace-match "")) | |
714 | (goto-char (point-min)) | |
715 | (let ((cnt 0)) | |
716 | (while (re-search-forward "\n" nil t) | |
717 | (setq cnt (1+ cnt)) | |
718 | (replace-match (if macro "\\n" " ") t t)) | |
719 | (when (and macro (> cnt 0)) | |
720 | (goto-char (point-max)) (insert "\\n"))) | |
721 | (goto-char (point-min)) | |
722 | (if (looking-at "\\s-*") (replace-match " "))) | |
c7cf0ebc BG |
723 | (when (and (org-bound-and-true-p org-edit-src-from-org-mode) |
724 | (not fixed-width-p)) | |
bdebdb64 BG |
725 | (org-escape-code-in-region (point-min) (point-max)) |
726 | (setq delta (+ delta | |
727 | (save-excursion | |
728 | (org-goto-line line) | |
729 | (if (looking-at "[ \t]*\\(,,\\)?\\(\\*\\|#+\\)") 1 | |
730 | 0))))) | |
86fbb8ca CD |
731 | (when (org-bound-and-true-p org-edit-src-picture) |
732 | (setq preserve-indentation nil) | |
733 | (untabify (point-min) (point-max)) | |
734 | (goto-char (point-min)) | |
735 | (while (re-search-forward "^" nil t) | |
736 | (replace-match ": "))) | |
737 | (unless (or single preserve-indentation (= total-nindent 0)) | |
738 | (setq indent (make-string total-nindent ?\ )) | |
739 | (goto-char (point-min)) | |
30cb51f1 BG |
740 | (while (re-search-forward "\\(^\\).+" nil t) |
741 | (replace-match indent nil nil nil 1))) | |
86fbb8ca CD |
742 | (if (org-bound-and-true-p org-edit-src-picture) |
743 | (setq total-nindent (+ total-nindent 2))) | |
744 | (setq code (buffer-string)) | |
8223b1d2 BG |
745 | (when (eq context 'save) |
746 | (erase-buffer) | |
747 | (insert bufstr)) | |
86fbb8ca | 748 | (set-buffer-modified-p nil)) |
8bfe682a | 749 | (org-src-switch-to-buffer (marker-buffer beg) (or context 'exit)) |
8223b1d2 | 750 | (if (eq context 'save) (save-buffer) |
271672fa BG |
751 | (with-current-buffer buffer |
752 | (set-buffer-modified-p nil)) | |
8223b1d2 | 753 | (kill-buffer buffer)) |
c8d0cf5c | 754 | (goto-char beg) |
86fbb8ca | 755 | (when allow-write-back-p |
30cb51f1 BG |
756 | (undo-boundary) |
757 | (delete-region beg (max beg end)) | |
758 | (unless (string-match "\\`[ \t]*\\'" code) | |
759 | (insert code)) | |
760 | ;; Make sure the overlay stays in place | |
271672fa | 761 | (when (eq context 'save) (move-overlay ovl beg (point))) |
30cb51f1 BG |
762 | (goto-char beg) |
763 | (if single (just-one-space))) | |
ed21c5c8 | 764 | (if (memq t (mapcar (lambda (overlay) |
86fbb8ca | 765 | (eq (overlay-get overlay 'invisible) |
ed21c5c8 | 766 | 'org-hide-block)) |
86fbb8ca | 767 | (overlays-at (point)))) |
ed21c5c8 CD |
768 | ;; Block is hidden; put point at start of block |
769 | (beginning-of-line 0) | |
770 | ;; Block is visible, put point where it was in the code buffer | |
271672fa BG |
771 | (when allow-write-back-p |
772 | (org-goto-line (1- (+ (org-current-line) line))) | |
773 | (org-move-to-column (if preserve-indentation col (+ col total-nindent delta))))) | |
8223b1d2 BG |
774 | (unless (eq context 'save) |
775 | (move-marker beg nil) | |
776 | (move-marker end nil))) | |
271672fa BG |
777 | (when org-edit-src-code-timer |
778 | (cancel-timer org-edit-src-code-timer) | |
779 | (setq org-edit-src-code-timer nil)) | |
8bfe682a CD |
780 | (unless (eq context 'save) |
781 | (when org-edit-src-saved-temp-window-config | |
782 | (set-window-configuration org-edit-src-saved-temp-window-config) | |
783 | (setq org-edit-src-saved-temp-window-config nil)))) | |
c8d0cf5c | 784 | |
271672fa BG |
785 | (defun org-edit-src-abort () |
786 | "Abort editing of the src code and return to the Org buffer." | |
787 | (interactive) | |
788 | (let (org-edit-src-allow-write-back-p) | |
789 | (org-edit-src-exit 'exit))) | |
790 | ||
8223b1d2 BG |
791 | (defmacro org-src-in-org-buffer (&rest body) |
792 | `(let ((p (point)) (m (mark)) (ul buffer-undo-list) msg) | |
793 | (save-window-excursion | |
794 | (org-edit-src-exit 'save) | |
795 | ,@body | |
796 | (setq msg (current-message)) | |
797 | (if (eq org-src-window-setup 'other-frame) | |
798 | (let ((org-src-window-setup 'current-window)) | |
799 | (org-edit-src-code 'save)) | |
800 | (org-edit-src-code 'save))) | |
801 | (setq buffer-undo-list ul) | |
802 | (push-mark m 'nomessage) | |
803 | (goto-char (min p (point-max))) | |
804 | (message (or msg "")))) | |
805 | (def-edebug-spec org-src-in-org-buffer (body)) | |
806 | ||
c8d0cf5c CD |
807 | (defun org-edit-src-save () |
808 | "Save parent buffer with current state source-code buffer." | |
809 | (interactive) | |
271672fa BG |
810 | (if (string-match "Fixed Width" (buffer-name)) |
811 | (user-error "Use C-c ' to save and exit, C-c C-k to abort editing") | |
812 | (org-src-in-org-buffer (save-buffer)))) | |
8223b1d2 | 813 | |
271672fa | 814 | (declare-function org-babel-tangle "ob-tangle" (&optional arg target-file lang)) |
8223b1d2 BG |
815 | |
816 | (defun org-src-tangle (arg) | |
817 | "Tangle the parent buffer." | |
818 | (interactive) | |
819 | (org-src-in-org-buffer (org-babel-tangle arg))) | |
c8d0cf5c | 820 | |
54a0dee5 | 821 | (defun org-src-mode-configure-edit-buffer () |
86fbb8ca | 822 | (when (org-bound-and-true-p org-edit-src-from-org-mode) |
54a0dee5 | 823 | (org-add-hook 'kill-buffer-hook |
3ab2c837 | 824 | #'(lambda () (delete-overlay org-edit-src-overlay)) nil 'local) |
86fbb8ca CD |
825 | (if (org-bound-and-true-p org-edit-src-allow-write-back-p) |
826 | (progn | |
827 | (setq buffer-offer-save t) | |
828 | (setq buffer-file-name | |
829 | (concat (buffer-file-name (marker-buffer org-edit-src-beg-marker)) | |
830 | "[" (buffer-name) "]")) | |
831 | (if (featurep 'xemacs) | |
832 | (progn | |
833 | (make-variable-buffer-local 'write-contents-hooks) ; needed only for 21.4 | |
834 | (setq write-contents-hooks '(org-edit-src-save))) | |
835 | (setq write-contents-functions '(org-edit-src-save)))) | |
836 | (setq buffer-read-only t)))) | |
54a0dee5 CD |
837 | |
838 | (org-add-hook 'org-src-mode-hook 'org-src-mode-configure-edit-buffer) | |
839 | ||
afe98dfa CD |
840 | |
841 | (defun org-src-associate-babel-session (info) | |
842 | "Associate edit buffer with comint session." | |
843 | (interactive) | |
844 | (let ((session (cdr (assoc :session (nth 2 info))))) | |
845 | (and session (not (string= session "none")) | |
846 | (org-babel-comint-buffer-livep session) | |
666ffc7e SM |
847 | (let ((f (intern (format "org-babel-%s-associate-session" |
848 | (nth 0 info))))) | |
849 | (and (fboundp f) (funcall f session)))))) | |
afe98dfa CD |
850 | |
851 | (defun org-src-babel-configure-edit-buffer () | |
852 | (when org-src-babel-info | |
853 | (org-src-associate-babel-session org-src-babel-info))) | |
854 | ||
855 | (org-add-hook 'org-src-mode-hook 'org-src-babel-configure-edit-buffer) | |
856 | (defmacro org-src-do-at-code-block (&rest body) | |
857 | "Execute a command from an edit buffer in the Org-mode buffer." | |
858 | `(let ((beg-marker org-edit-src-beg-marker)) | |
859 | (if beg-marker | |
860 | (with-current-buffer (marker-buffer beg-marker) | |
861 | (goto-char (marker-position beg-marker)) | |
862 | ,@body)))) | |
e66ba1df | 863 | (def-edebug-spec org-src-do-at-code-block (body)) |
afe98dfa CD |
864 | |
865 | (defun org-src-do-key-sequence-at-code-block (&optional key) | |
866 | "Execute key sequence at code block in the source Org buffer. | |
867 | The command bound to KEY in the Org-babel key map is executed | |
868 | remotely with point temporarily at the start of the code block in | |
869 | the Org buffer. | |
870 | ||
871 | This command is not bound to a key by default, to avoid conflicts | |
8223b1d2 | 872 | with language major mode bindings. To bind it to C-c @ in all |
afe98dfa CD |
873 | language major modes, you could use |
874 | ||
875 | (add-hook 'org-src-mode-hook | |
876 | (lambda () (define-key org-src-mode-map \"\\C-c@\" | |
877 | 'org-src-do-key-sequence-at-code-block))) | |
878 | ||
879 | In that case, for example, C-c @ t issued in code edit buffers | |
880 | would tangle the current Org code block, C-c @ e would execute | |
881 | the block and C-c @ h would display the other available | |
882 | Org-babel commands." | |
883 | (interactive "kOrg-babel key: ") | |
884 | (if (equal key (kbd "C-g")) (keyboard-quit) | |
885 | (org-edit-src-save) | |
886 | (org-src-do-at-code-block | |
887 | (call-interactively | |
888 | (lookup-key org-babel-map key))))) | |
889 | ||
890 | (defcustom org-src-tab-acts-natively nil | |
891 | "If non-nil, the effect of TAB in a code block is as if it were | |
892 | issued in the language major mode buffer." | |
893 | :type 'boolean | |
372d7b21 | 894 | :version "24.1" |
afe98dfa CD |
895 | :group 'org-babel) |
896 | ||
897 | (defun org-src-native-tab-command-maybe () | |
898 | "Perform language-specific TAB action. | |
271672fa | 899 | Alter code block according to what TAB does in the language major mode." |
afe98dfa | 900 | (and org-src-tab-acts-natively |
271672fa | 901 | (org-in-src-block-p) |
63aa0982 | 902 | (not (equal this-command 'org-shifttab)) |
afe98dfa CD |
903 | (let ((org-src-strip-leading-and-trailing-blank-lines nil)) |
904 | (org-babel-do-key-sequence-in-edit-buffer (kbd "TAB"))))) | |
905 | ||
906 | (add-hook 'org-tab-first-hook 'org-src-native-tab-command-maybe) | |
907 | ||
908 | (defun org-src-font-lock-fontify-block (lang start end) | |
909 | "Fontify code block. | |
910 | This function is called by emacs automatic fontification, as long | |
8223b1d2 | 911 | as `org-src-fontify-natively' is non-nil. For manual |
afe98dfa CD |
912 | fontification of code blocks see `org-src-fontify-block' and |
913 | `org-src-fontify-buffer'" | |
3ab2c837 BG |
914 | (let ((lang-mode (org-src-get-lang-mode lang))) |
915 | (if (fboundp lang-mode) | |
916 | (let ((string (buffer-substring-no-properties start end)) | |
917 | (modified (buffer-modified-p)) | |
918 | (org-buffer (current-buffer)) pos next) | |
919 | (remove-text-properties start end '(face nil)) | |
920 | (with-current-buffer | |
921 | (get-buffer-create | |
922 | (concat " org-src-fontification:" (symbol-name lang-mode))) | |
923 | (delete-region (point-min) (point-max)) | |
8223b1d2 | 924 | (insert string " ") ;; so there's a final property change |
3ab2c837 BG |
925 | (unless (eq major-mode lang-mode) (funcall lang-mode)) |
926 | (font-lock-fontify-buffer) | |
927 | (setq pos (point-min)) | |
928 | (while (setq next (next-single-property-change pos 'face)) | |
929 | (put-text-property | |
8223b1d2 | 930 | (+ start (1- pos)) (1- (+ start next)) 'face |
3ab2c837 BG |
931 | (get-text-property pos 'face) org-buffer) |
932 | (setq pos next))) | |
933 | (add-text-properties | |
934 | start end | |
935 | '(font-lock-fontified t fontified t font-lock-multiline t)) | |
936 | (set-buffer-modified-p modified))))) | |
afe98dfa CD |
937 | |
938 | (defun org-src-fontify-block () | |
939 | "Fontify code block at point." | |
940 | (interactive) | |
941 | (save-excursion | |
942 | (let ((org-src-fontify-natively t) | |
943 | (info (org-edit-src-find-region-and-lang))) | |
944 | (font-lock-fontify-region (nth 0 info) (nth 1 info))))) | |
945 | ||
946 | (defun org-src-fontify-buffer () | |
8223b1d2 | 947 | "Fontify all code blocks in the current buffer." |
afe98dfa CD |
948 | (interactive) |
949 | (org-babel-map-src-blocks nil | |
950 | (org-src-fontify-block))) | |
951 | ||
952 | (defun org-src-get-lang-mode (lang) | |
953 | "Return major mode that should be used for LANG. | |
954 | LANG is a string, and the returned major mode is a symbol." | |
955 | (intern | |
956 | (concat | |
666ffc7e SM |
957 | (let ((l (or (cdr (assoc lang org-src-lang-modes)) lang))) |
958 | (if (symbolp l) (symbol-name l) l)) | |
959 | "-mode"))) | |
afe98dfa | 960 | |
c8d0cf5c CD |
961 | (provide 'org-src) |
962 | ||
c8d0cf5c | 963 | ;;; org-src.el ends here |