* etc/publicsuffix.txt: Update from source.
[bpt/emacs.git] / lisp / org / ob-lilypond.el
CommitLineData
3ab2c837
BG
1;;; ob-lilypond.el --- org-babel functions for lilypond evaluation
2
ba318903 3;; Copyright (C) 2010-2014 Free Software Foundation, Inc.
3ab2c837
BG
4
5;; Author: Martyn Jago
6;; Keywords: babel language, literate programming
153ae947 7;; Homepage: http://orgmode.org/worg/org-contrib/babel/languages/ob-doc-lilypond.html
3ab2c837
BG
8
9;; This file is part of GNU Emacs.
10
11;; GNU Emacs is free software: you can redistribute it and/or modify
12;; it under the terms of the GNU General Public License as published by
13;; the Free Software Foundation, either version 3 of the License, or
14;; (at your option) any later version.
15
16;; GNU Emacs is distributed in the hope that it will be useful,
17;; but WITHOUT ANY WARRANTY; without even the implied warranty of
18;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19;; GNU General Public License for more details.
20
21;; You should have received a copy of the GNU General Public License
22;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
23
24;;; Commentary:
25
8223b1d2
BG
26;; Installation, ob-lilypond documentation, and examples are available at
27;; http://orgmode.org/worg/org-contrib/babel/languages/ob-doc-lilypond.html
28;;
29;; Lilypond documentation can be found at
30;; http://lilypond.org/manuals.html
3ab2c837
BG
31
32;;; Code:
33(require 'ob)
153ae947 34(require 'outline)
3ab2c837
BG
35(defalias 'lilypond-mode 'LilyPond-mode)
36
3ab2c837
BG
37(add-to-list 'org-babel-tangle-lang-exts '("LilyPond" . "ly"))
38
39(defvar org-babel-default-header-args:lilypond '()
8223b1d2
BG
40 "Default header arguments for lilypond code blocks.
41NOTE: The arguments are determined at lilypond compile time.
42See (ly-set-header-args)")
3ab2c837 43
3ab2c837
BG
44(defvar ly-compile-post-tangle t
45 "Following the org-babel-tangle (C-c C-v t) command,
46ly-compile-post-tangle determines whether ob-lilypond should
47automatically attempt to compile the resultant tangled file.
48If the value is nil, no automated compilation takes place.
49Default value is t")
50
51(defvar ly-display-pdf-post-tangle t
52 "Following a successful LilyPond compilation
53ly-display-pdf-post-tangle determines whether to automate the
8223b1d2
BG
54drawing / redrawing of the resultant pdf. If the value is nil,
55the pdf is not automatically redrawn. Default value is t")
3ab2c837
BG
56
57(defvar ly-play-midi-post-tangle t
58 "Following a successful LilyPond compilation
59ly-play-midi-post-tangle determines whether to automate the
8223b1d2
BG
60playing of the resultant midi file. If the value is nil,
61the midi file is not automatically played. Default value is t")
3ab2c837
BG
62
63(defvar ly-OSX-ly-path
64 "/Applications/lilypond.app/Contents/Resources/bin/lilypond")
65(defvar ly-OSX-pdf-path "open")
66(defvar ly-OSX-midi-path "open")
67
68(defvar ly-nix-ly-path "/usr/bin/lilypond")
69(defvar ly-nix-pdf-path "evince")
70(defvar ly-nix-midi-path "timidity")
71
b46a6a83
PE
72(defvar ly-w32-ly-path "lilypond")
73(defvar ly-w32-pdf-path "")
74(defvar ly-w32-midi-path "")
3ab2c837
BG
75
76(defvar ly-gen-png nil
8223b1d2 77 "Image generation (png) can be turned on by default by setting
3ab2c837
BG
78LY-GEN-PNG to t")
79
80(defvar ly-gen-svg nil
8223b1d2 81 "Image generation (SVG) can be turned on by default by setting
3ab2c837
BG
82LY-GEN-SVG to t")
83
84(defvar ly-gen-html nil
8223b1d2 85 "HTML generation can be turned on by default by setting
3ab2c837
BG
86LY-GEN-HTML to t")
87
8223b1d2
BG
88(defvar ly-gen-pdf nil
89 "PDF generation can be turned on by default by setting
90LY-GEN-PDF to t")
91
3ab2c837 92(defvar ly-use-eps nil
8223b1d2 93 "You can force the compiler to use the EPS backend by setting
3ab2c837
BG
94LY-USE-EPS to t")
95
96(defvar ly-arrange-mode nil
97 "Arrange mode is turned on by setting LY-ARRANGE-MODE
8223b1d2 98to t. In Arrange mode the following settings are altered
3ab2c837
BG
99from default...
100:tangle yes, :noweb yes
101:results silent :comments yes.
102In addition lilypond block execution causes tangling of all lilypond
103blocks")
104
105(defun org-babel-expand-body:lilypond (body params)
106 "Expand BODY according to PARAMS, return the expanded body."
3ab2c837
BG
107 (let ((vars (mapcar #'cdr (org-babel-get-header params :var))))
108 (mapc
109 (lambda (pair)
110 (let ((name (symbol-name (car pair)))
111 (value (cdr pair)))
112 (setq body
113 (replace-regexp-in-string
114 (concat "\$" (regexp-quote name))
115 (if (stringp value) value (format "%S" value))
116 body))))
117 vars)
118 body))
14e1337f 119
3ab2c837
BG
120(defun org-babel-execute:lilypond (body params)
121 "This function is called by `org-babel-execute-src-block'.
122Depending on whether we are in arrange mode either:
1231. Attempt to execute lilypond block according to header settings
124 (This is the default basic mode)
1252. Tangle all lilypond blocks and process the result (arrange mode)"
3ab2c837
BG
126 (ly-set-header-args ly-arrange-mode)
127 (if ly-arrange-mode
128 (ly-tangle)
129 (ly-process-basic body params)))
130
131(defun ly-tangle ()
132 "ob-lilypond specific tangle, attempts to invoke
8223b1d2 133=ly-execute-tangled-ly= if tangle is successful. Also passes
3ab2c837 134specific arguments to =org-babel-tangle="
3ab2c837
BG
135 (interactive)
136 (if (org-babel-tangle nil "yes" "lilypond")
137 (ly-execute-tangled-ly) nil))
138
139(defun ly-process-basic (body params)
8223b1d2 140 "Execute a lilypond block in basic mode."
3ab2c837
BG
141 (let* ((result-params (cdr (assoc :result-params params)))
142 (out-file (cdr (assoc :file params)))
143 (cmdline (or (cdr (assoc :cmdline params))
144 ""))
145 (in-file (org-babel-temp-file "lilypond-")))
146
147 (with-temp-file in-file
148 (insert (org-babel-expand-body:generic body params)))
3ab2c837
BG
149 (org-babel-eval
150 (concat
151 (ly-determine-ly-path)
152 " -dbackend=eps "
153 "-dno-gs-load-fonts "
154 "-dinclude-eps-fonts "
271672fa
BG
155 (or (cdr (assoc (file-name-extension out-file)
156 '(("pdf" . "--pdf ")
157 ("ps" . "--ps ")
158 ("png" . "--png "))))
159 "--png ")
3ab2c837
BG
160 "--output="
161 (file-name-sans-extension out-file)
162 " "
163 cmdline
8223b1d2 164 in-file) "")) nil)
3ab2c837
BG
165
166(defun org-babel-prep-session:lilypond (session params)
167 "Return an error because LilyPond exporter does not support sessions."
3ab2c837
BG
168 (error "Sorry, LilyPond does not currently support sessions!"))
169
170(defun ly-execute-tangled-ly ()
171 "Compile result of block tangle with lilypond.
172If error in compilation, attempt to mark the error in lilypond org file"
3ab2c837
BG
173 (when ly-compile-post-tangle
174 (let ((ly-tangled-file (ly-switch-extension
175 (buffer-file-name) ".lilypond"))
176 (ly-temp-file (ly-switch-extension
177 (buffer-file-name) ".ly")))
178 (if (file-exists-p ly-tangled-file)
14e1337f 179 (progn
3ab2c837
BG
180 (when (file-exists-p ly-temp-file)
181 (delete-file ly-temp-file))
182 (rename-file ly-tangled-file
183 ly-temp-file))
184 (error "Error: Tangle Failed!") t)
185 (switch-to-buffer-other-window "*lilypond*")
186 (erase-buffer)
187 (ly-compile-lilyfile ly-temp-file)
188 (goto-char (point-min))
189 (if (not (ly-check-for-compile-error ly-temp-file))
190 (progn
191 (other-window -1)
192 (ly-attempt-to-open-pdf ly-temp-file)
193 (ly-attempt-to-play-midi ly-temp-file))
194 (error "Error in Compilation!")))) nil)
195
196(defun ly-compile-lilyfile (file-name &optional test)
197 "Compile lilypond file and check for compile errors
198FILE-NAME is full path to lilypond (.ly) file"
3ab2c837
BG
199 (message "Compiling LilyPond...")
200 (let ((arg-1 (ly-determine-ly-path)) ;program
201 (arg-2 nil) ;infile
202 (arg-3 "*lilypond*") ;buffer
8223b1d2
BG
203 (arg-4 t) ;display
204 (arg-5 (if ly-gen-png "--png" "")) ;&rest...
205 (arg-6 (if ly-gen-html "--html" ""))
206 (arg-7 (if ly-gen-pdf "--pdf" ""))
207 (arg-8 (if ly-use-eps "-dbackend=eps" ""))
208 (arg-9 (if ly-gen-svg "-dbackend=svg" ""))
209 (arg-10 (concat "--output=" (file-name-sans-extension file-name)))
210 (arg-11 file-name))
3ab2c837 211 (if test
8223b1d2
BG
212 `(,arg-1 ,arg-2 ,arg-3 ,arg-4 ,arg-5 ,arg-6
213 ,arg-7 ,arg-8 ,arg-9 ,arg-10 ,arg-11)
3ab2c837 214 (call-process
8223b1d2
BG
215 arg-1 arg-2 arg-3 arg-4 arg-5 arg-6
216 arg-7 arg-8 arg-9 arg-10 arg-11))))
3ab2c837
BG
217
218(defun ly-check-for-compile-error (file-name &optional test)
219 "Check for compile error.
220This is performed by parsing the *lilypond* buffer
221containing the output message from the compilation.
222FILE-NAME is full path to lilypond file.
223If TEST is t just return nil if no error found, and pass
224nil as file-name since it is unused in this context"
225 (let ((is-error (search-forward "error:" nil t)))
226 (if (not test)
227 (if (not is-error)
228 nil
229 (ly-process-compile-error file-name))
230 is-error)))
231
232(defun ly-process-compile-error (file-name)
233 "Process the compilation error that has occurred.
234FILE-NAME is full path to lilypond file"
3ab2c837
BG
235 (let ((line-num (ly-parse-line-num)))
236 (let ((error-lines (ly-parse-error-line file-name line-num)))
237 (ly-mark-error-line file-name error-lines)
238 (error "Error: Compilation Failed!"))))
239
240(defun ly-mark-error-line (file-name line)
241 "Mark the erroneous lines in the lilypond org buffer.
242FILE-NAME is full path to lilypond file.
243LINE is the erroneous line"
3ab2c837
BG
244 (switch-to-buffer-other-window
245 (concat (file-name-nondirectory
246 (ly-switch-extension file-name ".org"))))
247 (let ((temp (point)))
248 (goto-char (point-min))
249 (setq case-fold-search nil)
250 (if (search-forward line nil t)
251 (progn
252 (show-all)
253 (set-mark (point))
254 (goto-char (- (point) (length line))))
255 (goto-char temp))))
14e1337f 256
3ab2c837
BG
257(defun ly-parse-line-num (&optional buffer)
258 "Extract error line number."
3ab2c837
BG
259 (when buffer
260 (set-buffer buffer))
261 (let ((start
262 (and (search-backward ":" nil t)
263 (search-backward ":" nil t)
264 (search-backward ":" nil t)
265 (search-backward ":" nil t)))
266 (num nil))
267 (if start
268 (progn
269 (forward-char)
270 (let ((num (buffer-substring
271 (+ 1 start)
272 (- (search-forward ":" nil t) 1))))
273 (setq num (string-to-number num))
274 (if (numberp num)
275 num
276 nil)))
277 nil)))
278
279(defun ly-parse-error-line (file-name lineNo)
280 "Extract the erroneous line from the tangled .ly file
281FILE-NAME is full path to lilypond file.
282LINENO is the number of the erroneous line"
3ab2c837
BG
283 (with-temp-buffer
284 (insert-file-contents (ly-switch-extension file-name ".ly")
285 nil nil nil t)
286 (if (> lineNo 0)
287 (progn
288 (goto-char (point-min))
289 (forward-line (- lineNo 1))
290 (buffer-substring (point) (point-at-eol)))
291 nil)))
14e1337f 292
3ab2c837
BG
293(defun ly-attempt-to-open-pdf (file-name &optional test)
294 "Attempt to display the generated pdf file
295FILE-NAME is full path to lilypond file
296If TEST is non-nil, the shell command is returned and is not run"
3ab2c837
BG
297 (when ly-display-pdf-post-tangle
298 (let ((pdf-file (ly-switch-extension file-name ".pdf")))
299 (if (file-exists-p pdf-file)
300 (let ((cmd-string
301 (concat (ly-determine-pdf-path) " " pdf-file)))
302 (if test
303 cmd-string
8223b1d2
BG
304 (start-process
305 "\"Audition pdf\""
306 "*lilypond*"
307 (ly-determine-pdf-path)
308 pdf-file)))
309 (message "No pdf file generated so can't display!")))))
3ab2c837
BG
310
311(defun ly-attempt-to-play-midi (file-name &optional test)
312 "Attempt to play the generated MIDI file
313FILE-NAME is full path to lilypond file
314If TEST is non-nil, the shell command is returned and is not run"
3ab2c837
BG
315 (when ly-play-midi-post-tangle
316 (let ((midi-file (ly-switch-extension file-name ".midi")))
317 (if (file-exists-p midi-file)
318 (let ((cmd-string
319 (concat (ly-determine-midi-path) " " midi-file)))
320 (if test
321 cmd-string
8223b1d2
BG
322 (start-process
323 "\"Audition midi\""
324 "*lilypond*"
325 (ly-determine-midi-path)
326 midi-file)))
3ab2c837
BG
327 (message "No midi file generated so can't play!")))))
328
329(defun ly-determine-ly-path (&optional test)
330 "Return correct path to ly binary depending on OS
331If TEST is non-nil, it contains a simulation of the OS for test purposes"
3ab2c837
BG
332 (let ((sys-type
333 (or test system-type)))
334 (cond ((string= sys-type "darwin")
335 ly-OSX-ly-path)
b46a6a83
PE
336 ((string= sys-type "windows-nt")
337 ly-w32-ly-path)
3ab2c837
BG
338 (t ly-nix-ly-path))))
339
340(defun ly-determine-pdf-path (&optional test)
341 "Return correct path to pdf viewer depending on OS
342If TEST is non-nil, it contains a simulation of the OS for test purposes"
3ab2c837
BG
343 (let ((sys-type
344 (or test system-type)))
345 (cond ((string= sys-type "darwin")
346 ly-OSX-pdf-path)
b46a6a83
PE
347 ((string= sys-type "windows-nt")
348 ly-w32-pdf-path)
3ab2c837
BG
349 (t ly-nix-pdf-path))))
350
351(defun ly-determine-midi-path (&optional test)
352 "Return correct path to midi player depending on OS
353If TEST is non-nil, it contains a simulation of the OS for test purposes"
3ab2c837
BG
354 (let ((sys-type
355 (or test test system-type)))
356 (cond ((string= sys-type "darwin")
357 ly-OSX-midi-path)
b46a6a83
PE
358 ((string= sys-type "windows-nt")
359 ly-w32-midi-path)
3ab2c837 360 (t ly-nix-midi-path))))
14e1337f 361
3ab2c837 362(defun ly-toggle-midi-play ()
8223b1d2 363 "Toggle whether midi will be played following a successful compilation."
3ab2c837
BG
364 (interactive)
365 (setq ly-play-midi-post-tangle
366 (not ly-play-midi-post-tangle))
367 (message (concat "Post-Tangle MIDI play has been "
368 (if ly-play-midi-post-tangle
369 "ENABLED." "DISABLED."))))
370
371(defun ly-toggle-pdf-display ()
8223b1d2 372 "Toggle whether pdf will be displayed following a successful compilation."
3ab2c837
BG
373 (interactive)
374 (setq ly-display-pdf-post-tangle
375 (not ly-display-pdf-post-tangle))
376 (message (concat "Post-Tangle PDF display has been "
377 (if ly-display-pdf-post-tangle
378 "ENABLED." "DISABLED."))))
379
380(defun ly-toggle-png-generation ()
8223b1d2 381 "Toggle whether png image will be generated by compilation."
3ab2c837 382 (interactive)
8223b1d2 383 (setq ly-gen-png (not ly-gen-png))
3ab2c837
BG
384 (message (concat "PNG image generation has been "
385 (if ly-gen-png "ENABLED." "DISABLED."))))
386
387(defun ly-toggle-html-generation ()
8223b1d2 388 "Toggle whether html will be generated by compilation."
3ab2c837 389 (interactive)
8223b1d2 390 (setq ly-gen-html (not ly-gen-html))
3ab2c837
BG
391 (message (concat "HTML generation has been "
392 (if ly-gen-html "ENABLED." "DISABLED."))))
393
8223b1d2
BG
394(defun ly-toggle-pdf-generation ()
395 "Toggle whether pdf will be generated by compilation."
396 (interactive)
397 (setq ly-gen-pdf (not ly-gen-pdf))
398 (message (concat "PDF generation has been "
399 (if ly-gen-pdf "ENABLED." "DISABLED."))))
3ab2c837 400
8223b1d2
BG
401(defun ly-toggle-arrange-mode ()
402 "Toggle whether in Arrange mode or Basic mode."
3ab2c837
BG
403 (interactive)
404 (setq ly-arrange-mode
405 (not ly-arrange-mode))
406 (message (concat "Arrange mode has been "
407 (if ly-arrange-mode "ENABLED." "DISABLED."))))
408
153ae947 409(defun ly-switch-extension (file-name ext)
3ab2c837 410 "Utility command to swap current FILE-NAME extension with EXT"
3ab2c837
BG
411 (concat (file-name-sans-extension
412 file-name) ext))
413
414(defun ly-get-header-args (mode)
415 "Default arguments to use when evaluating a lilypond
8223b1d2
BG
416source block. These depend upon whether we are in arrange
417mode i.e. ARRANGE-MODE is t"
3ab2c837
BG
418 (cond (mode
419 '((:tangle . "yes")
420 (:noweb . "yes")
421 (:results . "silent")
8223b1d2 422 (:cache . "yes")
3ab2c837
BG
423 (:comments . "yes")))
424 (t
425 '((:results . "file")
426 (:exports . "results")))))
427
428(defun ly-set-header-args (mode)
429 "Set org-babel-default-header-args:lilypond
430dependent on LY-ARRANGE-MODE"
431 (setq org-babel-default-header-args:lilypond
432 (ly-get-header-args mode)))
433
434(provide 'ob-lilypond)
435
3ab2c837 436;;; ob-lilypond.el ends here