commit | author | age
|
76bbd0
|
1 |
;;; org-src.el --- Source code examples in Org -*- lexical-binding: t; -*- |
C |
2 |
;; |
|
3 |
;; Copyright (C) 2004-2018 Free Software Foundation, Inc. |
|
4 |
;; |
|
5 |
;; Author: Carsten Dominik <carsten at orgmode dot org> |
|
6 |
;; Bastien Guerry <bzg@gnu.org> |
|
7 |
;; Dan Davison <davison at stats dot ox dot ac dot uk> |
|
8 |
;; Keywords: outlines, hypermedia, calendar, wp |
|
9 |
;; Homepage: https://orgmode.org |
|
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 <https://www.gnu.org/licenses/>. |
|
25 |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
|
26 |
;; |
|
27 |
;;; Commentary: |
|
28 |
|
|
29 |
;; This file contains the code dealing with source code examples in |
|
30 |
;; Org mode. |
|
31 |
|
|
32 |
;;; Code: |
|
33 |
|
|
34 |
(require 'cl-lib) |
|
35 |
(require 'org-macs) |
|
36 |
(require 'org-compat) |
|
37 |
(require 'ob-keys) |
|
38 |
(require 'ob-comint) |
|
39 |
|
|
40 |
(declare-function org-base-buffer "org" (buffer)) |
|
41 |
(declare-function org-do-remove-indentation "org" (&optional n)) |
|
42 |
(declare-function org-element-at-point "org-element" ()) |
|
43 |
(declare-function org-element-class "org-element" (datum &optional parent)) |
|
44 |
(declare-function org-element-context "org-element" (&optional element)) |
|
45 |
(declare-function org-element-lineage "org-element" |
|
46 |
(blob &optional types with-self)) |
|
47 |
(declare-function org-element-property "org-element" (property element)) |
|
48 |
(declare-function org-element-type "org-element" (element)) |
|
49 |
(declare-function org-footnote-goto-definition "org-footnote" |
|
50 |
(label &optional location)) |
|
51 |
(declare-function org-get-indentation "org" (&optional line)) |
|
52 |
(declare-function org-switch-to-buffer-other-window "org" (&rest args)) |
|
53 |
(declare-function org-trim "org" (s &optional keep-lead)) |
|
54 |
|
|
55 |
(defvar org-inhibit-startup) |
|
56 |
|
|
57 |
(defcustom org-edit-src-turn-on-auto-save nil |
|
58 |
"Non-nil means turn `auto-save-mode' on when editing a source block. |
|
59 |
This will save the content of the source code editing buffer into |
|
60 |
a newly created file, not the base buffer for this source block. |
|
61 |
|
|
62 |
If you want to regularly save the base buffer instead of the source |
|
63 |
code editing buffer, see `org-edit-src-auto-save-idle-delay' instead." |
|
64 |
:group 'org-edit-structure |
|
65 |
:version "24.4" |
|
66 |
:package-version '(Org . "8.0") |
|
67 |
:type 'boolean) |
|
68 |
|
|
69 |
(defcustom org-edit-src-auto-save-idle-delay 0 |
|
70 |
"Delay before saving a source code buffer back into its base buffer. |
|
71 |
When a positive integer N, save after N seconds of idle time. |
|
72 |
When 0 (the default), don't auto-save. |
|
73 |
|
|
74 |
If you want to save the source code buffer itself, don't use this. |
|
75 |
Check `org-edit-src-turn-on-auto-save' instead." |
|
76 |
:group 'org-edit-structure |
|
77 |
:version "24.4" |
|
78 |
:package-version '(Org . "8.0") |
|
79 |
:type 'integer) |
|
80 |
|
|
81 |
(defcustom org-coderef-label-format "(ref:%s)" |
|
82 |
"The default coderef format. |
|
83 |
This format string will be used to search for coderef labels in literal |
|
84 |
examples (EXAMPLE and SRC blocks). The format can be overwritten in |
|
85 |
an individual literal example with the -l option, like |
|
86 |
|
|
87 |
#+BEGIN_SRC pascal +n -r -l \"((%s))\" |
|
88 |
... |
|
89 |
#+END_SRC |
|
90 |
|
|
91 |
If you want to use this for HTML export, make sure that the format does |
|
92 |
not introduce special font-locking, and avoid the HTML special |
|
93 |
characters `<', `>', and `&'. The reason for this restriction is that |
|
94 |
the labels are searched for only after htmlize has done its job." |
|
95 |
:group 'org-edit-structure ; FIXME this is not in the right group |
|
96 |
:type 'string) |
|
97 |
|
|
98 |
(defcustom org-edit-fixed-width-region-mode 'artist-mode |
|
99 |
"The mode that should be used to edit fixed-width regions. |
|
100 |
These are the regions where each line starts with a colon." |
|
101 |
:group 'org-edit-structure |
|
102 |
:type '(choice |
|
103 |
(const artist-mode) |
|
104 |
(const picture-mode) |
|
105 |
(const fundamental-mode) |
|
106 |
(function :tag "Other (specify)"))) |
|
107 |
|
|
108 |
(defcustom org-src-preserve-indentation nil |
|
109 |
"If non-nil preserve leading whitespace characters on export. |
|
110 |
\\<org-mode-map> |
|
111 |
If non-nil leading whitespace characters in source code blocks |
|
112 |
are preserved on export, and when switching between the org |
|
113 |
buffer and the language mode edit buffer. |
|
114 |
|
|
115 |
When this variable is nil, after editing with `\\[org-edit-src-code]', |
|
116 |
the minimum (across-lines) number of leading whitespace characters |
|
117 |
are removed from all lines, and the code block is uniformly indented |
|
118 |
according to the value of `org-edit-src-content-indentation'." |
|
119 |
:group 'org-edit-structure |
|
120 |
:type 'boolean) |
|
121 |
|
|
122 |
(defcustom org-edit-src-content-indentation 2 |
|
123 |
"Indentation for the content of a source code block. |
|
124 |
|
|
125 |
This should be the number of spaces added to the indentation of the #+begin |
|
126 |
line in order to compute the indentation of the block content after |
|
127 |
editing it with `\\[org-edit-src-code]'. |
|
128 |
|
|
129 |
It has no effect if `org-src-preserve-indentation' is non-nil." |
|
130 |
:group 'org-edit-structure |
|
131 |
:type 'integer) |
|
132 |
|
|
133 |
(defcustom org-edit-src-persistent-message t |
|
134 |
"Non-nil means show persistent exit help message while editing src examples. |
|
135 |
The message is shown in the header-line, which will be created in the |
|
136 |
first line of the window showing the editing buffer." |
|
137 |
:group 'org-edit-structure |
|
138 |
:type 'boolean) |
|
139 |
|
|
140 |
(defcustom org-src-ask-before-returning-to-edit-buffer t |
|
141 |
"Non-nil means ask before switching to an existing edit buffer. |
|
142 |
If nil, when `org-edit-src-code' is used on a block that already |
|
143 |
has an active edit buffer, it will switch to that edit buffer |
|
144 |
immediately; otherwise it will ask whether you want to return to |
|
145 |
the existing edit buffer." |
|
146 |
:group 'org-edit-structure |
|
147 |
:version "24.4" |
|
148 |
:package-version '(Org . "8.0") |
|
149 |
:type 'boolean) |
|
150 |
|
|
151 |
(defcustom org-src-window-setup 'reorganize-frame |
|
152 |
"How the source code edit buffer should be displayed. |
|
153 |
Possible values for this option are: |
|
154 |
|
|
155 |
current-window Show edit buffer in the current window, keeping all other |
|
156 |
windows. |
|
157 |
other-window Use `switch-to-buffer-other-window' to display edit buffer. |
|
158 |
reorganize-frame Show only two windows on the current frame, the current |
|
159 |
window and the edit buffer. When exiting the edit buffer, |
|
160 |
return to one window. |
|
161 |
other-frame Use `switch-to-buffer-other-frame' to display edit buffer. |
|
162 |
Also, when exiting the edit buffer, kill that frame." |
|
163 |
:group 'org-edit-structure |
|
164 |
:type '(choice |
|
165 |
(const current-window) |
|
166 |
(const other-frame) |
|
167 |
(const other-window) |
|
168 |
(const reorganize-frame))) |
|
169 |
|
|
170 |
(defvar org-src-mode-hook nil |
|
171 |
"Hook run after Org switched a source code snippet to its Emacs mode. |
|
172 |
\\<org-mode-map> |
|
173 |
This hook will run: |
|
174 |
- when editing a source code snippet with `\\[org-edit-special]' |
|
175 |
- when formatting a source code snippet for export with htmlize. |
|
176 |
|
|
177 |
You may want to use this hook for example to turn off `outline-minor-mode' |
|
178 |
or similar things which you want to have when editing a source code file, |
|
179 |
but which mess up the display of a snippet in Org exported files.") |
|
180 |
|
|
181 |
(defcustom org-src-lang-modes |
|
182 |
'(("ocaml" . tuareg) ("elisp" . emacs-lisp) ("ditaa" . artist) |
|
183 |
("asymptote" . asy) ("dot" . fundamental) ("sqlite" . sql) |
|
184 |
("calc" . fundamental) ("C" . c) ("cpp" . c++) ("C++" . c++) |
|
185 |
("screen" . shell-script) ("shell" . sh) ("bash" . sh)) |
|
186 |
"Alist mapping languages to their major mode. |
|
187 |
The key is the language name, the value is the string that should |
|
188 |
be inserted as the name of the major mode. For many languages this is |
|
189 |
simple, but for language where this is not the case, this variable |
|
190 |
provides a way to simplify things on the user side. |
|
191 |
For example, there is no ocaml-mode in Emacs, but the mode to use is |
|
192 |
`tuareg-mode'." |
|
193 |
:group 'org-edit-structure |
|
194 |
:type '(repeat |
|
195 |
(cons |
|
196 |
(string "Language name") |
|
197 |
(symbol "Major mode")))) |
|
198 |
|
|
199 |
(defcustom org-src-block-faces nil |
|
200 |
"Alist of faces to be used for source-block. |
|
201 |
Each element is a cell of the format |
|
202 |
|
|
203 |
(\"language\" FACE) |
|
204 |
|
|
205 |
Where FACE is either a defined face or an anonymous face. |
|
206 |
|
|
207 |
For instance, the following value would color the background of |
|
208 |
emacs-lisp source blocks and python source blocks in purple and |
|
209 |
green, respectability. |
|
210 |
|
|
211 |
\\='((\"emacs-lisp\" (:background \"#EEE2FF\")) |
|
212 |
(\"python\" (:background \"#e5ffb8\")))" |
|
213 |
:group 'org-edit-structure |
|
214 |
:type '(repeat (list (string :tag "language") |
|
215 |
(choice |
|
216 |
(face :tag "Face") |
|
217 |
(sexp :tag "Anonymous face")))) |
|
218 |
:version "26.1" |
|
219 |
:package-version '(Org . "9.0")) |
|
220 |
|
|
221 |
(defcustom org-src-tab-acts-natively nil |
|
222 |
"If non-nil, the effect of TAB in a code block is as if it were |
|
223 |
issued in the language major mode buffer." |
|
224 |
:type 'boolean |
|
225 |
:version "24.1" |
|
226 |
:group 'org-babel) |
|
227 |
|
|
228 |
|
|
229 |
|
|
230 |
;;; Internal functions and variables |
|
231 |
|
|
232 |
(defvar-local org-src--allow-write-back t) |
|
233 |
(put 'org-src--allow-write-back 'permanent-local t) |
|
234 |
|
|
235 |
(defvar-local org-src--auto-save-timer nil) |
|
236 |
(put 'org-src--auto-save-timer 'permanent-local t) |
|
237 |
|
|
238 |
(defvar-local org-src--babel-info nil) |
|
239 |
(put 'org-src--babel-info 'permanent-local t) |
|
240 |
|
|
241 |
(defvar-local org-src--beg-marker nil) |
|
242 |
(put 'org-src--beg-marker 'permanent-local t) |
|
243 |
|
|
244 |
(defvar-local org-src--block-indentation nil) |
|
245 |
(put 'org-src--block-indentation 'permanent-local t) |
|
246 |
|
|
247 |
(defvar-local org-src--end-marker nil) |
|
248 |
(put 'org-src--end-marker 'permanent-local t) |
|
249 |
|
|
250 |
(defvar-local org-src--from-org-mode nil) |
|
251 |
(put 'org-src--from-org-mode 'permanent-local t) |
|
252 |
|
|
253 |
(defvar-local org-src--overlay nil) |
|
254 |
(put 'org-src--overlay 'permanent-local t) |
|
255 |
|
|
256 |
(defvar-local org-src--preserve-indentation nil) |
|
257 |
(put 'org-src--preserve-indentation 'permanent-local t) |
|
258 |
|
|
259 |
(defvar-local org-src--remote nil) |
|
260 |
(put 'org-src--remote 'permanent-local t) |
|
261 |
|
|
262 |
(defvar-local org-src--saved-temp-window-config nil) |
|
263 |
(put 'org-src--saved-temp-window-config 'permanent-local t) |
|
264 |
|
|
265 |
(defvar-local org-src--source-type nil |
|
266 |
"Type of element being edited, as a symbol.") |
|
267 |
(put 'org-src--source-type 'permanent-local t) |
|
268 |
|
|
269 |
(defvar-local org-src--tab-width nil |
|
270 |
"Contains `tab-width' value from Org source buffer. |
|
271 |
However, if `indent-tabs-mode' is nil in that buffer, its value |
|
272 |
is 0.") |
|
273 |
(put 'org-src--tab-width 'permanent-local t) |
|
274 |
|
|
275 |
(defun org-src--construct-edit-buffer-name (org-buffer-name lang) |
|
276 |
"Construct the buffer name for a source editing buffer." |
|
277 |
(concat "*Org Src " org-buffer-name "[ " lang " ]*")) |
|
278 |
|
|
279 |
(defun org-src--edit-buffer (beg end) |
|
280 |
"Return buffer editing area between BEG and END. |
|
281 |
Return nil if there is no such buffer." |
|
282 |
(catch 'exit |
|
283 |
(dolist (b (buffer-list)) |
|
284 |
(with-current-buffer b |
|
285 |
(and (org-src-edit-buffer-p) |
|
286 |
(= beg org-src--beg-marker) |
|
287 |
(eq (marker-buffer beg) (marker-buffer org-src--beg-marker)) |
|
288 |
(= end org-src--end-marker) |
|
289 |
(eq (marker-buffer end) (marker-buffer org-src--end-marker)) |
|
290 |
(throw 'exit b)))))) |
|
291 |
|
|
292 |
(defun org-src--source-buffer () |
|
293 |
"Return source buffer edited by current buffer." |
|
294 |
(unless (org-src-edit-buffer-p) (error "Not in a source buffer")) |
|
295 |
(or (marker-buffer org-src--beg-marker) |
|
296 |
(error "No source buffer available for current editing session"))) |
|
297 |
|
|
298 |
(defun org-src--get-lang-mode (lang) |
|
299 |
"Return major mode that should be used for LANG. |
|
300 |
LANG is a string, and the returned major mode is a symbol." |
|
301 |
(intern |
|
302 |
(concat |
|
303 |
(let ((l (or (cdr (assoc lang org-src-lang-modes)) lang))) |
|
304 |
(if (symbolp l) (symbol-name l) l)) |
|
305 |
"-mode"))) |
|
306 |
|
|
307 |
(defun org-src--coordinates (pos beg end) |
|
308 |
"Return coordinates of POS relatively to BEG and END. |
|
309 |
POS, BEG and END are buffer positions. Return value is either |
|
310 |
a cons cell (LINE . COLUMN) or symbol `end'. See also |
|
311 |
`org-src--goto-coordinates'." |
|
312 |
(if (>= pos end) 'end |
|
313 |
(org-with-wide-buffer |
|
314 |
(goto-char (max beg pos)) |
|
315 |
(cons (count-lines beg (line-beginning-position)) |
|
316 |
;; Column is relative to the end of line to avoid problems of |
|
317 |
;; comma escaping or colons appended in front of the line. |
|
318 |
(- (current-column) |
|
319 |
(progn (end-of-line) (current-column))))))) |
|
320 |
|
|
321 |
(defun org-src--goto-coordinates (coord beg end) |
|
322 |
"Move to coordinates COORD relatively to BEG and END. |
|
323 |
COORD are coordinates, as returned by `org-src--coordinates', |
|
324 |
which see. BEG and END are buffer positions." |
|
325 |
(goto-char |
|
326 |
(if (eq coord 'end) (max (1- end) beg) |
|
327 |
;; If BEG happens to be located outside of the narrowed part of |
|
328 |
;; the buffer, widen it first. |
|
329 |
(org-with-wide-buffer |
|
330 |
(goto-char beg) |
|
331 |
(forward-line (car coord)) |
|
332 |
(end-of-line) |
|
333 |
(org-move-to-column (max (+ (current-column) (cdr coord)) 0)) |
|
334 |
(point))))) |
|
335 |
|
|
336 |
(defun org-src--contents-area (datum) |
|
337 |
"Return contents boundaries of DATUM. |
|
338 |
DATUM is an element or object. Return a list (BEG END CONTENTS) |
|
339 |
where BEG and END are buffer positions and CONTENTS is a string." |
|
340 |
(let ((type (org-element-type datum))) |
|
341 |
(org-with-wide-buffer |
|
342 |
(cond |
|
343 |
((eq type 'footnote-definition) |
|
344 |
(let* ((beg (progn |
|
345 |
(goto-char (org-element-property :post-affiliated datum)) |
|
346 |
(search-forward "]"))) |
|
347 |
(end (or (org-element-property :contents-end datum) beg))) |
|
348 |
(list beg end (buffer-substring-no-properties beg end)))) |
|
349 |
((eq type 'inline-src-block) |
|
350 |
(let ((beg (progn (goto-char (org-element-property :begin datum)) |
|
351 |
(search-forward "{" (line-end-position) t))) |
|
352 |
(end (progn (goto-char (org-element-property :end datum)) |
|
353 |
(search-backward "}" (line-beginning-position) t)))) |
|
354 |
(list beg end (buffer-substring-no-properties beg end)))) |
|
355 |
((org-element-property :contents-begin datum) |
|
356 |
(let ((beg (org-element-property :contents-begin datum)) |
|
357 |
(end (org-element-property :contents-end datum))) |
|
358 |
(list beg end (buffer-substring-no-properties beg end)))) |
|
359 |
((memq type '(example-block export-block src-block)) |
|
360 |
(list (progn (goto-char (org-element-property :post-affiliated datum)) |
|
361 |
(line-beginning-position 2)) |
|
362 |
(progn (goto-char (org-element-property :end datum)) |
|
363 |
(skip-chars-backward " \r\t\n") |
|
364 |
(line-beginning-position 1)) |
|
365 |
(org-element-property :value datum))) |
|
366 |
((memq type '(fixed-width latex-environment table)) |
|
367 |
(let ((beg (org-element-property :post-affiliated datum)) |
|
368 |
(end (progn (goto-char (org-element-property :end datum)) |
|
369 |
(skip-chars-backward " \r\t\n") |
|
370 |
(line-beginning-position 2)))) |
|
371 |
(list beg |
|
372 |
end |
|
373 |
(if (eq type 'fixed-width) (org-element-property :value datum) |
|
374 |
(buffer-substring-no-properties beg end))))) |
|
375 |
(t (error "Unsupported element or object: %s" type)))))) |
|
376 |
|
|
377 |
(defun org-src--make-source-overlay (beg end edit-buffer) |
|
378 |
"Create overlay between BEG and END positions and return it. |
|
379 |
EDIT-BUFFER is the buffer currently editing area between BEG and |
|
380 |
END." |
|
381 |
(let ((overlay (make-overlay beg end))) |
|
382 |
(overlay-put overlay 'face 'secondary-selection) |
|
383 |
(overlay-put overlay 'edit-buffer edit-buffer) |
|
384 |
(overlay-put overlay 'help-echo |
|
385 |
"Click with mouse-1 to switch to buffer editing this segment") |
|
386 |
(overlay-put overlay 'face 'secondary-selection) |
|
387 |
(overlay-put overlay 'keymap |
|
388 |
(let ((map (make-sparse-keymap))) |
|
389 |
(define-key map [mouse-1] 'org-edit-src-continue) |
|
390 |
map)) |
|
391 |
(let ((read-only |
|
392 |
(list |
|
393 |
(lambda (&rest _) |
|
394 |
(user-error |
|
395 |
"Cannot modify an area being edited in a dedicated buffer"))))) |
|
396 |
(overlay-put overlay 'modification-hooks read-only) |
|
397 |
(overlay-put overlay 'insert-in-front-hooks read-only) |
|
398 |
(overlay-put overlay 'insert-behind-hooks read-only)) |
|
399 |
overlay)) |
|
400 |
|
|
401 |
(defun org-src--remove-overlay () |
|
402 |
"Remove overlay from current source buffer." |
|
403 |
(when (overlayp org-src--overlay) (delete-overlay org-src--overlay))) |
|
404 |
|
|
405 |
(defun org-src--on-datum-p (datum) |
|
406 |
"Non-nil when point is on DATUM. |
|
407 |
DATUM is an element or an object. Consider blank lines or white |
|
408 |
spaces after it as being outside." |
|
409 |
(and (>= (point) (org-element-property :begin datum)) |
|
410 |
(<= (point) |
|
411 |
(org-with-wide-buffer |
|
412 |
(goto-char (org-element-property :end datum)) |
|
413 |
(skip-chars-backward " \r\t\n") |
|
414 |
(if (eq (org-element-class datum) 'element) |
|
415 |
(line-end-position) |
|
416 |
(point)))))) |
|
417 |
|
|
418 |
(defun org-src--contents-for-write-back () |
|
419 |
"Return buffer contents in a format appropriate for write back. |
|
420 |
Assume point is in the corresponding edit buffer." |
|
421 |
(let ((indentation-offset |
|
422 |
(if org-src--preserve-indentation 0 |
|
423 |
(+ (or org-src--block-indentation 0) |
|
424 |
(if (memq org-src--source-type '(example-block src-block)) |
|
425 |
org-edit-src-content-indentation |
|
426 |
0)))) |
|
427 |
(use-tabs? (and (> org-src--tab-width 0) t)) |
|
428 |
(source-tab-width org-src--tab-width) |
|
429 |
(contents (org-with-wide-buffer (buffer-string))) |
|
430 |
(write-back org-src--allow-write-back)) |
|
431 |
(with-temp-buffer |
|
432 |
;; Reproduce indentation parameters from source buffer. |
|
433 |
(setq-local indent-tabs-mode use-tabs?) |
|
434 |
(when (> source-tab-width 0) (setq-local tab-width source-tab-width)) |
|
435 |
;; Apply WRITE-BACK function on edit buffer contents. |
|
436 |
(insert (org-no-properties contents)) |
|
437 |
(goto-char (point-min)) |
|
438 |
(when (functionp write-back) (save-excursion (funcall write-back))) |
|
439 |
;; Add INDENTATION-OFFSET to every non-empty line in buffer, |
|
440 |
;; unless indentation is meant to be preserved. |
|
441 |
(when (> indentation-offset 0) |
|
442 |
(while (not (eobp)) |
|
443 |
(skip-chars-forward " \t") |
|
444 |
(unless (eolp) ;ignore blank lines |
|
445 |
(let ((i (current-column))) |
|
446 |
(delete-region (line-beginning-position) (point)) |
|
447 |
(indent-to (+ i indentation-offset)))) |
|
448 |
(forward-line))) |
|
449 |
(buffer-string)))) |
|
450 |
|
|
451 |
(defun org-src--edit-element |
|
452 |
(datum name &optional initialize write-back contents remote) |
|
453 |
"Edit DATUM contents in a dedicated buffer NAME. |
|
454 |
|
|
455 |
INITIALIZE is a function to call upon creating the buffer. |
|
456 |
|
|
457 |
When WRITE-BACK is non-nil, assume contents will replace original |
|
458 |
region. Moreover, if it is a function, apply it in the edit |
|
459 |
buffer, from point min, before returning the contents. |
|
460 |
|
|
461 |
When CONTENTS is non-nil, display them in the edit buffer. |
|
462 |
Otherwise, show DATUM contents as specified by |
|
463 |
`org-src--contents-area'. |
|
464 |
|
|
465 |
When REMOTE is non-nil, do not try to preserve point or mark when |
|
466 |
moving from the edit area to the source. |
|
467 |
|
|
468 |
Leave point in edit buffer." |
|
469 |
(setq org-src--saved-temp-window-config (current-window-configuration)) |
|
470 |
(let* ((area (org-src--contents-area datum)) |
|
471 |
(beg (copy-marker (nth 0 area))) |
|
472 |
(end (copy-marker (nth 1 area) t)) |
|
473 |
(old-edit-buffer (org-src--edit-buffer beg end)) |
|
474 |
(contents (or contents (nth 2 area)))) |
|
475 |
(if (and old-edit-buffer |
|
476 |
(or (not org-src-ask-before-returning-to-edit-buffer) |
|
477 |
(y-or-n-p "Return to existing edit buffer ([n] will revert changes)? "))) |
|
478 |
;; Move to existing buffer. |
|
479 |
(org-src-switch-to-buffer old-edit-buffer 'return) |
|
480 |
;; Discard old edit buffer. |
|
481 |
(when old-edit-buffer |
|
482 |
(with-current-buffer old-edit-buffer (org-src--remove-overlay)) |
|
483 |
(kill-buffer old-edit-buffer)) |
|
484 |
(let* ((org-mode-p (derived-mode-p 'org-mode)) |
|
485 |
(source-tab-width (if indent-tabs-mode tab-width 0)) |
|
486 |
(type (org-element-type datum)) |
|
487 |
(ind (org-with-wide-buffer |
|
488 |
(goto-char (org-element-property :begin datum)) |
|
489 |
(org-get-indentation))) |
|
490 |
(preserve-ind |
|
491 |
(and (memq type '(example-block src-block)) |
|
492 |
(or (org-element-property :preserve-indent datum) |
|
493 |
org-src-preserve-indentation))) |
|
494 |
;; Store relative positions of mark (if any) and point |
|
495 |
;; within the edited area. |
|
496 |
(point-coordinates (and (not remote) |
|
497 |
(org-src--coordinates (point) beg end))) |
|
498 |
(mark-coordinates (and (not remote) |
|
499 |
(org-region-active-p) |
|
500 |
(let ((m (mark))) |
|
501 |
(and (>= m beg) (>= end m) |
|
502 |
(org-src--coordinates m beg end))))) |
|
503 |
;; Generate a new edit buffer. |
|
504 |
(buffer (generate-new-buffer name)) |
|
505 |
;; Add an overlay on top of source. |
|
506 |
(overlay (org-src--make-source-overlay beg end buffer))) |
|
507 |
;; Switch to edit buffer. |
|
508 |
(org-src-switch-to-buffer buffer 'edit) |
|
509 |
;; Insert contents. |
|
510 |
(insert contents) |
|
511 |
(remove-text-properties (point-min) (point-max) |
|
512 |
'(display nil invisible nil intangible nil)) |
|
513 |
(unless preserve-ind (org-do-remove-indentation)) |
|
514 |
(set-buffer-modified-p nil) |
|
515 |
(setq buffer-file-name nil) |
|
516 |
;; Initialize buffer. |
|
517 |
(when (functionp initialize) |
|
518 |
(let ((org-inhibit-startup t)) |
|
519 |
(condition-case e |
|
520 |
(funcall initialize) |
|
521 |
(error (message "Initialization fails with: %S" |
|
522 |
(error-message-string e)))))) |
|
523 |
;; Transmit buffer-local variables for exit function. It must |
|
524 |
;; be done after initializing major mode, as this operation |
|
525 |
;; may reset them otherwise. |
|
526 |
(setq org-src--tab-width source-tab-width) |
|
527 |
(setq org-src--from-org-mode org-mode-p) |
|
528 |
(setq org-src--beg-marker beg) |
|
529 |
(setq org-src--end-marker end) |
|
530 |
(setq org-src--remote remote) |
|
531 |
(setq org-src--source-type type) |
|
532 |
(setq org-src--block-indentation ind) |
|
533 |
(setq org-src--preserve-indentation preserve-ind) |
|
534 |
(setq org-src--overlay overlay) |
|
535 |
(setq org-src--allow-write-back write-back) |
|
536 |
;; Start minor mode. |
|
537 |
(org-src-mode) |
|
538 |
;; Move mark and point in edit buffer to the corresponding |
|
539 |
;; location. |
|
540 |
(if remote |
|
541 |
(progn |
|
542 |
;; Put point at first non read-only character after |
|
543 |
;; leading blank. |
|
544 |
(goto-char |
|
545 |
(or (text-property-any (point-min) (point-max) 'read-only nil) |
|
546 |
(point-max))) |
|
547 |
(skip-chars-forward " \r\t\n")) |
|
548 |
;; Set mark and point. |
|
549 |
(when mark-coordinates |
|
550 |
(org-src--goto-coordinates mark-coordinates (point-min) (point-max)) |
|
551 |
(push-mark (point) 'no-message t) |
|
552 |
(setq deactivate-mark nil)) |
|
553 |
(org-src--goto-coordinates |
|
554 |
point-coordinates (point-min) (point-max))))))) |
|
555 |
|
|
556 |
|
|
557 |
|
|
558 |
;;; Fontification of source blocks |
|
559 |
|
|
560 |
(defun org-src-font-lock-fontify-block (lang start end) |
|
561 |
"Fontify code block. |
|
562 |
This function is called by emacs automatic fontification, as long |
|
563 |
as `org-src-fontify-natively' is non-nil." |
|
564 |
(let ((lang-mode (org-src--get-lang-mode lang))) |
|
565 |
(when (fboundp lang-mode) |
|
566 |
(let ((string (buffer-substring-no-properties start end)) |
|
567 |
(modified (buffer-modified-p)) |
|
568 |
(org-buffer (current-buffer))) |
|
569 |
(remove-text-properties start end '(face nil)) |
|
570 |
(with-current-buffer |
|
571 |
(get-buffer-create |
|
572 |
(format " *org-src-fontification:%s*" lang-mode)) |
|
573 |
(let ((inhibit-modification-hooks nil)) |
|
574 |
(erase-buffer) |
|
575 |
;; Add string and a final space to ensure property change. |
|
576 |
(insert string " ")) |
|
577 |
(unless (eq major-mode lang-mode) (funcall lang-mode)) |
|
578 |
(org-font-lock-ensure) |
|
579 |
(let ((pos (point-min)) next) |
|
580 |
(while (setq next (next-property-change pos)) |
|
581 |
;; Handle additional properties from font-lock, so as to |
|
582 |
;; preserve, e.g., composition. |
|
583 |
(dolist (prop (cons 'face font-lock-extra-managed-props)) |
|
584 |
(let ((new-prop (get-text-property pos prop))) |
|
585 |
(put-text-property |
|
586 |
(+ start (1- pos)) (1- (+ start next)) prop new-prop |
|
587 |
org-buffer))) |
|
588 |
(setq pos next)))) |
|
589 |
;; Add Org faces. |
|
590 |
(let ((src-face (nth 1 (assoc-string lang org-src-block-faces t)))) |
|
591 |
(when (or (facep src-face) (listp src-face)) |
|
592 |
(font-lock-append-text-property start end 'face src-face)) |
|
593 |
(font-lock-append-text-property start end 'face 'org-block)) |
|
594 |
(add-text-properties |
|
595 |
start end |
|
596 |
'(font-lock-fontified t fontified t font-lock-multiline t)) |
|
597 |
(set-buffer-modified-p modified))))) |
|
598 |
|
|
599 |
|
|
600 |
;;; Escape contents |
|
601 |
|
|
602 |
(defun org-escape-code-in-region (beg end) |
|
603 |
"Escape lines between BEG and END. |
|
604 |
Escaping happens when a line starts with \"*\", \"#+\", \",*\" or |
|
605 |
\",#+\" by appending a comma to it." |
|
606 |
(interactive "r") |
|
607 |
(save-excursion |
|
608 |
(goto-char end) |
|
609 |
(while (re-search-backward "^[ \t]*\\(,*\\(?:\\*\\|#\\+\\)\\)" beg t) |
|
610 |
(save-excursion (replace-match ",\\1" nil nil nil 1))))) |
|
611 |
|
|
612 |
(defun org-escape-code-in-string (s) |
|
613 |
"Escape lines in string S. |
|
614 |
Escaping happens when a line starts with \"*\", \"#+\", \",*\" or |
|
615 |
\",#+\" by appending a comma to it." |
|
616 |
(replace-regexp-in-string "^[ \t]*\\(,*\\(?:\\*\\|#\\+\\)\\)" ",\\1" |
|
617 |
s nil nil 1)) |
|
618 |
|
|
619 |
(defun org-unescape-code-in-region (beg end) |
|
620 |
"Un-escape lines between BEG and END. |
|
621 |
Un-escaping happens by removing the first comma on lines starting |
|
622 |
with \",*\", \",#+\", \",,*\" and \",,#+\"." |
|
623 |
(interactive "r") |
|
624 |
(save-excursion |
|
625 |
(goto-char end) |
|
626 |
(while (re-search-backward "^[ \t]*,*\\(,\\)\\(?:\\*\\|#\\+\\)" beg t) |
|
627 |
(save-excursion (replace-match "" nil nil nil 1))))) |
|
628 |
|
|
629 |
(defun org-unescape-code-in-string (s) |
|
630 |
"Un-escape lines in string S. |
|
631 |
Un-escaping happens by removing the first comma on lines starting |
|
632 |
with \",*\", \",#+\", \",,*\" and \",,#+\"." |
|
633 |
(replace-regexp-in-string |
|
634 |
"^[ \t]*,*\\(,\\)\\(?:\\*\\|#\\+\\)" "" s nil nil 1)) |
|
635 |
|
|
636 |
|
|
637 |
|
|
638 |
;;; Org src minor mode |
|
639 |
|
|
640 |
(defvar org-src-mode-map |
|
641 |
(let ((map (make-sparse-keymap))) |
|
642 |
(define-key map "\C-c'" 'org-edit-src-exit) |
|
643 |
(define-key map "\C-c\C-k" 'org-edit-src-abort) |
|
644 |
(define-key map "\C-x\C-s" 'org-edit-src-save) |
|
645 |
map)) |
|
646 |
|
|
647 |
(define-minor-mode org-src-mode |
|
648 |
"Minor mode for language major mode buffers generated by Org. |
|
649 |
\\<org-mode-map> |
|
650 |
This minor mode is turned on in two situations: |
|
651 |
- when editing a source code snippet with `\\[org-edit-special]' |
|
652 |
- when formatting a source code snippet for export with htmlize. |
|
653 |
|
|
654 |
\\{org-src-mode-map} |
|
655 |
|
|
656 |
See also `org-src-mode-hook'." |
|
657 |
nil " OrgSrc" nil |
|
658 |
(when org-edit-src-persistent-message |
|
659 |
(setq-local |
|
660 |
header-line-format |
|
661 |
(substitute-command-keys |
|
662 |
(if org-src--allow-write-back |
|
663 |
"Edit, then exit with `\\[org-edit-src-exit]' or abort with \ |
|
664 |
`\\[org-edit-src-abort]'" |
|
665 |
"Exit with `\\[org-edit-src-exit]' or abort with \ |
|
666 |
`\\[org-edit-src-abort]'")))) |
|
667 |
;; Possibly activate various auto-save features (for the edit buffer |
|
668 |
;; or the source buffer). |
|
669 |
(when org-edit-src-turn-on-auto-save |
|
670 |
(setq buffer-auto-save-file-name |
|
671 |
(concat (make-temp-name "org-src-") |
|
672 |
(format-time-string "-%Y-%d-%m") |
|
673 |
".txt"))) |
|
674 |
(unless (or org-src--auto-save-timer (zerop org-edit-src-auto-save-idle-delay)) |
|
675 |
(setq org-src--auto-save-timer |
|
676 |
(run-with-idle-timer |
|
677 |
org-edit-src-auto-save-idle-delay t |
|
678 |
(lambda () |
|
679 |
(save-excursion |
|
680 |
(let (edit-flag) |
|
681 |
(dolist (b (buffer-list)) |
|
682 |
(with-current-buffer b |
|
683 |
(when (org-src-edit-buffer-p) |
|
684 |
(unless edit-flag (setq edit-flag t)) |
|
685 |
(when (buffer-modified-p) (org-edit-src-save))))) |
|
686 |
(unless edit-flag |
|
687 |
(cancel-timer org-src--auto-save-timer) |
|
688 |
(setq org-src--auto-save-timer nil))))))))) |
|
689 |
|
|
690 |
(defun org-src-mode-configure-edit-buffer () |
|
691 |
(when (bound-and-true-p org-src--from-org-mode) |
|
692 |
(add-hook 'kill-buffer-hook #'org-src--remove-overlay nil 'local) |
|
693 |
(if (bound-and-true-p org-src--allow-write-back) |
|
694 |
(progn |
|
695 |
(setq buffer-offer-save t) |
|
696 |
(setq buffer-file-name |
|
697 |
(concat (buffer-file-name (marker-buffer org-src--beg-marker)) |
|
698 |
"[" (buffer-name) "]")) |
|
699 |
(setq-local write-contents-functions '(org-edit-src-save))) |
|
700 |
(setq buffer-read-only t)))) |
|
701 |
|
|
702 |
(add-hook 'org-src-mode-hook #'org-src-mode-configure-edit-buffer) |
|
703 |
|
|
704 |
|
|
705 |
|
|
706 |
;;; Babel related functions |
|
707 |
|
|
708 |
(defun org-src-associate-babel-session (info) |
|
709 |
"Associate edit buffer with comint session." |
|
710 |
(interactive) |
|
711 |
(let ((session (cdr (assq :session (nth 2 info))))) |
|
712 |
(and session (not (string= session "none")) |
|
713 |
(org-babel-comint-buffer-livep session) |
|
714 |
(let ((f (intern (format "org-babel-%s-associate-session" |
|
715 |
(nth 0 info))))) |
|
716 |
(and (fboundp f) (funcall f session)))))) |
|
717 |
|
|
718 |
(defun org-src-babel-configure-edit-buffer () |
|
719 |
(when org-src--babel-info |
|
720 |
(org-src-associate-babel-session org-src--babel-info))) |
|
721 |
|
|
722 |
(add-hook 'org-src-mode-hook #'org-src-babel-configure-edit-buffer) |
|
723 |
|
|
724 |
|
|
725 |
;;; Public API |
|
726 |
|
|
727 |
(defmacro org-src-do-at-code-block (&rest body) |
|
728 |
"Execute BODY from an edit buffer in the Org mode buffer." |
|
729 |
(declare (debug (body))) |
|
730 |
`(let ((beg-marker org-src--beg-marker)) |
|
731 |
(when beg-marker |
|
732 |
(with-current-buffer (marker-buffer beg-marker) |
|
733 |
(goto-char beg-marker) |
|
734 |
,@body)))) |
|
735 |
|
|
736 |
(defun org-src-do-key-sequence-at-code-block (&optional key) |
|
737 |
"Execute key sequence at code block in the source Org buffer. |
|
738 |
The command bound to KEY in the Org-babel key map is executed |
|
739 |
remotely with point temporarily at the start of the code block in |
|
740 |
the Org buffer. |
|
741 |
|
|
742 |
This command is not bound to a key by default, to avoid conflicts |
|
743 |
with language major mode bindings. To bind it to C-c @ in all |
|
744 |
language major modes, you could use |
|
745 |
|
|
746 |
(add-hook \\='org-src-mode-hook |
|
747 |
(lambda () (define-key org-src-mode-map \"\\C-c@\" |
|
748 |
\\='org-src-do-key-sequence-at-code-block))) |
|
749 |
|
|
750 |
In that case, for example, C-c @ t issued in code edit buffers |
|
751 |
would tangle the current Org code block, C-c @ e would execute |
|
752 |
the block and C-c @ h would display the other available |
|
753 |
Org-babel commands." |
|
754 |
(interactive "kOrg-babel key: ") |
|
755 |
(if (equal key (kbd "C-g")) (keyboard-quit) |
|
756 |
(org-edit-src-save) |
|
757 |
(org-src-do-at-code-block |
|
758 |
(call-interactively (lookup-key org-babel-map key))))) |
|
759 |
|
|
760 |
(defun org-src-edit-buffer-p (&optional buffer) |
|
761 |
"Non-nil when current buffer is a source editing buffer. |
|
762 |
If BUFFER is non-nil, test it instead." |
|
763 |
(let ((buffer (org-base-buffer (or buffer (current-buffer))))) |
|
764 |
(and (buffer-live-p buffer) |
|
765 |
(local-variable-p 'org-src--beg-marker buffer) |
|
766 |
(local-variable-p 'org-src--end-marker buffer)))) |
|
767 |
|
|
768 |
(defun org-src-switch-to-buffer (buffer context) |
|
769 |
(pcase org-src-window-setup |
|
770 |
(`current-window (pop-to-buffer-same-window buffer)) |
|
771 |
(`other-window |
|
772 |
(switch-to-buffer-other-window buffer)) |
|
773 |
(`other-frame |
|
774 |
(pcase context |
|
775 |
(`exit |
|
776 |
(let ((frame (selected-frame))) |
|
777 |
(switch-to-buffer-other-frame buffer) |
|
778 |
(delete-frame frame))) |
|
779 |
(`save |
|
780 |
(kill-buffer (current-buffer)) |
|
781 |
(pop-to-buffer-same-window buffer)) |
|
782 |
(_ (switch-to-buffer-other-frame buffer)))) |
|
783 |
(`reorganize-frame |
|
784 |
(when (eq context 'edit) (delete-other-windows)) |
|
785 |
(org-switch-to-buffer-other-window buffer) |
|
786 |
(when (eq context 'exit) (delete-other-windows))) |
|
787 |
(`switch-invisibly (set-buffer buffer)) |
|
788 |
(_ |
|
789 |
(message "Invalid value %s for `org-src-window-setup'" |
|
790 |
org-src-window-setup) |
|
791 |
(pop-to-buffer-same-window buffer)))) |
|
792 |
|
|
793 |
(defun org-src-coderef-format (&optional element) |
|
794 |
"Return format string for block at point. |
|
795 |
|
|
796 |
When optional argument ELEMENT is provided, use that block. |
|
797 |
Otherwise, assume point is either at a source block, at an |
|
798 |
example block. |
|
799 |
|
|
800 |
If point is in an edit buffer, retrieve format string associated |
|
801 |
to the remote source block." |
|
802 |
(cond |
|
803 |
((and element (org-element-property :label-fmt element))) |
|
804 |
((org-src-edit-buffer-p) (org-src-do-at-code-block (org-src-coderef-format))) |
|
805 |
((org-element-property :label-fmt (org-element-at-point))) |
|
806 |
(t org-coderef-label-format))) |
|
807 |
|
|
808 |
(defun org-src-coderef-regexp (fmt &optional label) |
|
809 |
"Return regexp matching a coderef format string FMT. |
|
810 |
|
|
811 |
When optional argument LABEL is non-nil, match coderef for that |
|
812 |
label only. |
|
813 |
|
|
814 |
Match group 1 contains the full coderef string with surrounding |
|
815 |
white spaces. Match group 2 contains the same string without any |
|
816 |
surrounding space. Match group 3 contains the label. |
|
817 |
|
|
818 |
A coderef format regexp can only match at the end of a line." |
|
819 |
(format "\\([ \t]*\\(%s\\)[ \t]*\\)$" |
|
820 |
(replace-regexp-in-string |
|
821 |
"%s" |
|
822 |
(if label (regexp-quote label) "\\([-a-zA-Z0-9_][-a-zA-Z0-9_ ]*\\)") |
|
823 |
(regexp-quote fmt) |
|
824 |
nil t))) |
|
825 |
|
|
826 |
(defun org-edit-footnote-reference () |
|
827 |
"Edit definition of footnote reference at point." |
|
828 |
(interactive) |
|
829 |
(let* ((context (org-element-context)) |
|
830 |
(label (org-element-property :label context))) |
|
831 |
(unless (and (eq (org-element-type context) 'footnote-reference) |
|
832 |
(org-src--on-datum-p context)) |
|
833 |
(user-error "Not on a footnote reference")) |
|
834 |
(unless label (user-error "Cannot edit remotely anonymous footnotes")) |
|
835 |
(let* ((definition (org-with-wide-buffer |
|
836 |
(org-footnote-goto-definition label) |
|
837 |
(backward-char) |
|
838 |
(org-element-context))) |
|
839 |
(inline? (eq 'footnote-reference (org-element-type definition))) |
|
840 |
(contents |
|
841 |
(org-with-wide-buffer |
|
842 |
(buffer-substring-no-properties |
|
843 |
(or (org-element-property :post-affiliated definition) |
|
844 |
(org-element-property :begin definition)) |
|
845 |
(cond |
|
846 |
(inline? (1+ (org-element-property :contents-end definition))) |
|
847 |
((org-element-property :contents-end definition)) |
|
848 |
(t (goto-char (org-element-property :post-affiliated definition)) |
|
849 |
(line-end-position))))))) |
|
850 |
(add-text-properties |
|
851 |
0 |
|
852 |
(progn (string-match (if inline? "\\`\\[fn:.*?:" "\\`.*?\\]") contents) |
|
853 |
(match-end 0)) |
|
854 |
'(read-only "Cannot edit footnote label" front-sticky t rear-nonsticky t) |
|
855 |
contents) |
|
856 |
(when inline? |
|
857 |
(let ((l (length contents))) |
|
858 |
(add-text-properties |
|
859 |
(1- l) l |
|
860 |
'(read-only "Cannot edit past footnote reference" |
|
861 |
front-sticky nil rear-nonsticky nil) |
|
862 |
contents))) |
|
863 |
(org-src--edit-element |
|
864 |
definition |
|
865 |
(format "*Edit footnote [%s]*" label) |
|
866 |
(let ((source (current-buffer))) |
|
867 |
(lambda () |
|
868 |
(org-mode) |
|
869 |
(org-clone-local-variables source))) |
|
870 |
(lambda () |
|
871 |
(if (not inline?) (delete-region (point) (search-forward "]")) |
|
872 |
(delete-region (point) (search-forward ":" nil t 2)) |
|
873 |
(delete-region (1- (point-max)) (point-max)) |
|
874 |
(when (re-search-forward "\n[ \t]*\n" nil t) |
|
875 |
(user-error "Inline definitions cannot contain blank lines")) |
|
876 |
;; If footnote reference belongs to a table, make sure to |
|
877 |
;; remove any newline characters in order to preserve |
|
878 |
;; table's structure. |
|
879 |
(when (org-element-lineage definition '(table-cell)) |
|
880 |
(while (search-forward "\n" nil t) (replace-match ""))))) |
|
881 |
contents |
|
882 |
'remote)) |
|
883 |
;; Report success. |
|
884 |
t)) |
|
885 |
|
|
886 |
(defun org-edit-table.el () |
|
887 |
"Edit \"table.el\" table at point. |
|
888 |
\\<org-src-mode-map> |
|
889 |
A new buffer is created and the table is copied into it. Then |
|
890 |
the table is recognized with `table-recognize'. When done |
|
891 |
editing, exit with `\\[org-edit-src-exit]'. The edited text will \ |
|
892 |
then replace |
|
893 |
the area in the Org mode buffer. |
|
894 |
|
|
895 |
Throw an error when not at such a table." |
|
896 |
(interactive) |
|
897 |
(let ((element (org-element-at-point))) |
|
898 |
(unless (and (eq (org-element-type element) 'table) |
|
899 |
(eq (org-element-property :type element) 'table.el) |
|
900 |
(org-src--on-datum-p element)) |
|
901 |
(user-error "Not in a table.el table")) |
|
902 |
(org-src--edit-element |
|
903 |
element |
|
904 |
(org-src--construct-edit-buffer-name (buffer-name) "Table") |
|
905 |
#'text-mode t) |
|
906 |
(when (bound-and-true-p flyspell-mode) (flyspell-mode -1)) |
|
907 |
(table-recognize) |
|
908 |
t)) |
|
909 |
|
|
910 |
(defun org-edit-latex-environment () |
|
911 |
"Edit LaTeX environment at point. |
|
912 |
\\<org-src-mode-map> |
|
913 |
The LaTeX environment is copied into a new buffer. Major mode is |
|
914 |
set to the one associated to \"latex\" in `org-src-lang-modes', |
|
915 |
or to `latex-mode' if there is none. |
|
916 |
|
|
917 |
When done, exit with `\\[org-edit-src-exit]'. The edited text \ |
|
918 |
will then replace |
|
919 |
the LaTeX environment in the Org mode buffer." |
|
920 |
(interactive) |
|
921 |
(let ((element (org-element-at-point))) |
|
922 |
(unless (and (eq (org-element-type element) 'latex-environment) |
|
923 |
(org-src--on-datum-p element)) |
|
924 |
(user-error "Not in a LaTeX environment")) |
|
925 |
(org-src--edit-element |
|
926 |
element |
|
927 |
(org-src--construct-edit-buffer-name (buffer-name) "LaTeX environment") |
|
928 |
(org-src--get-lang-mode "latex") |
|
929 |
t) |
|
930 |
t)) |
|
931 |
|
|
932 |
(defun org-edit-export-block () |
|
933 |
"Edit export block at point. |
|
934 |
\\<org-src-mode-map> |
|
935 |
A new buffer is created and the block is copied into it, and the |
|
936 |
buffer is switched into an appropriate major mode. See also |
|
937 |
`org-src-lang-modes'. |
|
938 |
|
|
939 |
When done, exit with `\\[org-edit-src-exit]'. The edited text \ |
|
940 |
will then replace |
|
941 |
the area in the Org mode buffer. |
|
942 |
|
|
943 |
Throw an error when not at an export block." |
|
944 |
(interactive) |
|
945 |
(let ((element (org-element-at-point))) |
|
946 |
(unless (and (eq (org-element-type element) 'export-block) |
|
947 |
(org-src--on-datum-p element)) |
|
948 |
(user-error "Not in an export block")) |
|
949 |
(let* ((type (downcase (or (org-element-property :type element) |
|
950 |
;; Missing export-block type. Fallback |
|
951 |
;; to default mode. |
|
952 |
"fundamental"))) |
|
953 |
(mode (org-src--get-lang-mode type))) |
|
954 |
(unless (functionp mode) (error "No such language mode: %s" mode)) |
|
955 |
(org-src--edit-element |
|
956 |
element |
|
957 |
(org-src--construct-edit-buffer-name (buffer-name) type) |
|
958 |
mode |
|
959 |
(lambda () (org-escape-code-in-region (point-min) (point-max))))) |
|
960 |
t)) |
|
961 |
|
|
962 |
(defun org-edit-src-code (&optional code edit-buffer-name) |
|
963 |
"Edit the source or example block at point. |
|
964 |
\\<org-src-mode-map> |
|
965 |
The code is copied to a separate buffer and the appropriate mode |
|
966 |
is turned on. When done, exit with `\\[org-edit-src-exit]'. This \ |
|
967 |
will remove the |
|
968 |
original code in the Org buffer, and replace it with the edited |
|
969 |
version. See `org-src-window-setup' to configure the display of |
|
970 |
windows containing the Org buffer and the code buffer. |
|
971 |
|
|
972 |
When optional argument CODE is a string, edit it in a dedicated |
|
973 |
buffer instead. |
|
974 |
|
|
975 |
When optional argument EDIT-BUFFER-NAME is non-nil, use it as the |
|
976 |
name of the sub-editing buffer." |
|
977 |
(interactive) |
|
978 |
(let* ((element (org-element-at-point)) |
|
979 |
(type (org-element-type element))) |
|
980 |
(unless (and (memq type '(example-block src-block)) |
|
981 |
(org-src--on-datum-p element)) |
|
982 |
(user-error "Not in a source or example block")) |
|
983 |
(let* ((lang |
|
984 |
(if (eq type 'src-block) (org-element-property :language element) |
|
985 |
"example")) |
|
986 |
(lang-f (and (eq type 'src-block) (org-src--get-lang-mode lang))) |
|
987 |
(babel-info (and (eq type 'src-block) |
|
988 |
(org-babel-get-src-block-info 'light))) |
|
989 |
deactivate-mark) |
|
990 |
(when (and (eq type 'src-block) (not (functionp lang-f))) |
|
991 |
(error "No such language mode: %s" lang-f)) |
|
992 |
(org-src--edit-element |
|
993 |
element |
|
994 |
(or edit-buffer-name |
|
995 |
(org-src--construct-edit-buffer-name (buffer-name) lang)) |
|
996 |
lang-f |
|
997 |
(and (null code) |
|
998 |
(lambda () (org-escape-code-in-region (point-min) (point-max)))) |
|
999 |
(and code (org-unescape-code-in-string code))) |
|
1000 |
;; Finalize buffer. |
|
1001 |
(setq-local org-coderef-label-format |
|
1002 |
(or (org-element-property :label-fmt element) |
|
1003 |
org-coderef-label-format)) |
|
1004 |
(when (eq type 'src-block) |
|
1005 |
(setq org-src--babel-info babel-info) |
|
1006 |
(let ((edit-prep-func (intern (concat "org-babel-edit-prep:" lang)))) |
|
1007 |
(when (fboundp edit-prep-func) |
|
1008 |
(funcall edit-prep-func babel-info)))) |
|
1009 |
t))) |
|
1010 |
|
|
1011 |
(defun org-edit-inline-src-code () |
|
1012 |
"Edit inline source code at point." |
|
1013 |
(interactive) |
|
1014 |
(let ((context (org-element-context))) |
|
1015 |
(unless (and (eq (org-element-type context) 'inline-src-block) |
|
1016 |
(org-src--on-datum-p context)) |
|
1017 |
(user-error "Not on inline source code")) |
|
1018 |
(let* ((lang (org-element-property :language context)) |
|
1019 |
(lang-f (org-src--get-lang-mode lang)) |
|
1020 |
(babel-info (org-babel-get-src-block-info 'light)) |
|
1021 |
deactivate-mark) |
|
1022 |
(unless (functionp lang-f) (error "No such language mode: %s" lang-f)) |
|
1023 |
(org-src--edit-element |
|
1024 |
context |
|
1025 |
(org-src--construct-edit-buffer-name (buffer-name) lang) |
|
1026 |
lang-f |
|
1027 |
(lambda () |
|
1028 |
;; Inline src blocks are limited to one line. |
|
1029 |
(while (re-search-forward "\n[ \t]*" nil t) (replace-match " ")) |
|
1030 |
;; Trim contents. |
|
1031 |
(goto-char (point-min)) |
|
1032 |
(skip-chars-forward " \t") |
|
1033 |
(delete-region (point-min) (point)) |
|
1034 |
(goto-char (point-max)) |
|
1035 |
(skip-chars-backward " \t") |
|
1036 |
(delete-region (point) (point-max)))) |
|
1037 |
;; Finalize buffer. |
|
1038 |
(setq org-src--babel-info babel-info) |
|
1039 |
(setq org-src--preserve-indentation t) |
|
1040 |
(let ((edit-prep-func (intern (concat "org-babel-edit-prep:" lang)))) |
|
1041 |
(when (fboundp edit-prep-func) (funcall edit-prep-func babel-info))) |
|
1042 |
;; Return success. |
|
1043 |
t))) |
|
1044 |
|
|
1045 |
(defun org-edit-fixed-width-region () |
|
1046 |
"Edit the fixed-width ASCII drawing at point. |
|
1047 |
\\<org-src-mode-map> |
|
1048 |
This must be a region where each line starts with a colon |
|
1049 |
followed by a space or a newline character. |
|
1050 |
|
|
1051 |
A new buffer is created and the fixed-width region is copied into |
|
1052 |
it, and the buffer is switched into the major mode defined in |
|
1053 |
`org-edit-fixed-width-region-mode', which see. |
|
1054 |
|
|
1055 |
When done, exit with `\\[org-edit-src-exit]'. The edited text \ |
|
1056 |
will then replace |
|
1057 |
the area in the Org mode buffer." |
|
1058 |
(interactive) |
|
1059 |
(let ((element (org-element-at-point))) |
|
1060 |
(unless (and (eq (org-element-type element) 'fixed-width) |
|
1061 |
(org-src--on-datum-p element)) |
|
1062 |
(user-error "Not in a fixed-width area")) |
|
1063 |
(org-src--edit-element |
|
1064 |
element |
|
1065 |
(org-src--construct-edit-buffer-name (buffer-name) "Fixed Width") |
|
1066 |
org-edit-fixed-width-region-mode |
|
1067 |
(lambda () (while (not (eobp)) (insert ": ") (forward-line)))) |
|
1068 |
;; Return success. |
|
1069 |
t)) |
|
1070 |
|
|
1071 |
(defun org-edit-src-abort () |
|
1072 |
"Abort editing of the src code and return to the Org buffer." |
|
1073 |
(interactive) |
|
1074 |
(let (org-src--allow-write-back) (org-edit-src-exit))) |
|
1075 |
|
|
1076 |
(defun org-edit-src-continue (e) |
|
1077 |
"Unconditionally return to buffer editing area under point. |
|
1078 |
Throw an error if there is no such buffer." |
|
1079 |
(interactive "e") |
|
1080 |
(mouse-set-point e) |
|
1081 |
(let ((buf (get-char-property (point) 'edit-buffer))) |
|
1082 |
(if buf (org-src-switch-to-buffer buf 'continue) |
|
1083 |
(user-error "No sub-editing buffer for area at point")))) |
|
1084 |
|
|
1085 |
(defun org-edit-src-save () |
|
1086 |
"Save parent buffer with current state source-code buffer." |
|
1087 |
(interactive) |
|
1088 |
(unless (org-src-edit-buffer-p) (user-error "Not in a sub-editing buffer")) |
|
1089 |
(set-buffer-modified-p nil) |
|
1090 |
(let ((edited-code (org-src--contents-for-write-back)) |
|
1091 |
(beg org-src--beg-marker) |
|
1092 |
(end org-src--end-marker) |
|
1093 |
(overlay org-src--overlay)) |
|
1094 |
(with-current-buffer (org-src--source-buffer) |
|
1095 |
(undo-boundary) |
|
1096 |
(goto-char beg) |
|
1097 |
;; Temporarily disable read-only features of OVERLAY in order to |
|
1098 |
;; insert new contents. |
|
1099 |
(delete-overlay overlay) |
|
1100 |
(delete-region beg end) |
|
1101 |
(let ((expecting-bol (bolp))) |
|
1102 |
(insert edited-code) |
|
1103 |
(when (and expecting-bol (not (bolp))) (insert "\n"))) |
|
1104 |
(save-buffer) |
|
1105 |
(move-overlay overlay beg (point)))) |
|
1106 |
;; `write-contents-functions' requires the function to return |
|
1107 |
;; a non-nil value so that other functions are not called. |
|
1108 |
t) |
|
1109 |
|
|
1110 |
(defun org-edit-src-exit () |
|
1111 |
"Kill current sub-editing buffer and return to source buffer." |
|
1112 |
(interactive) |
|
1113 |
(unless (org-src-edit-buffer-p) (error "Not in a sub-editing buffer")) |
|
1114 |
(let* ((beg org-src--beg-marker) |
|
1115 |
(end org-src--end-marker) |
|
1116 |
(write-back org-src--allow-write-back) |
|
1117 |
(remote org-src--remote) |
|
1118 |
(coordinates (and (not remote) |
|
1119 |
(org-src--coordinates (point) 1 (point-max)))) |
|
1120 |
(code (and write-back (org-src--contents-for-write-back)))) |
|
1121 |
(set-buffer-modified-p nil) |
|
1122 |
;; Switch to source buffer. Kill sub-editing buffer. |
|
1123 |
(let ((edit-buffer (current-buffer)) |
|
1124 |
(source-buffer (marker-buffer beg))) |
|
1125 |
(unless source-buffer (error "Source buffer disappeared. Aborting")) |
|
1126 |
(org-src-switch-to-buffer source-buffer 'exit) |
|
1127 |
(kill-buffer edit-buffer)) |
|
1128 |
;; Insert modified code. Ensure it ends with a newline character. |
|
1129 |
(org-with-wide-buffer |
|
1130 |
(when (and write-back (not (equal (buffer-substring beg end) code))) |
|
1131 |
(undo-boundary) |
|
1132 |
(goto-char beg) |
|
1133 |
(delete-region beg end) |
|
1134 |
(let ((expecting-bol (bolp))) |
|
1135 |
(insert code) |
|
1136 |
(when (and expecting-bol (not (bolp))) (insert "\n"))))) |
|
1137 |
;; If we are to return to source buffer, put point at an |
|
1138 |
;; appropriate location. In particular, if block is hidden, move |
|
1139 |
;; to the beginning of the block opening line. |
|
1140 |
(unless remote |
|
1141 |
(goto-char beg) |
|
1142 |
(cond |
|
1143 |
;; Block is hidden; move at start of block. |
|
1144 |
((cl-some (lambda (o) (eq (overlay-get o 'invisible) 'org-hide-block)) |
|
1145 |
(overlays-at (point))) |
|
1146 |
(beginning-of-line 0)) |
|
1147 |
(write-back (org-src--goto-coordinates coordinates beg end)))) |
|
1148 |
;; Clean up left-over markers and restore window configuration. |
|
1149 |
(set-marker beg nil) |
|
1150 |
(set-marker end nil) |
|
1151 |
(when org-src--saved-temp-window-config |
|
1152 |
(set-window-configuration org-src--saved-temp-window-config) |
|
1153 |
(setq org-src--saved-temp-window-config nil)))) |
|
1154 |
|
|
1155 |
|
|
1156 |
(provide 'org-src) |
|
1157 |
|
|
1158 |
;;; org-src.el ends here |