]> git.donarmstrong.com Git - lilypond.git/blob - lilypond-mode.el
*** empty log message ***
[lilypond.git] / lilypond-mode.el
1 ;;;
2 ;;; lilypond-mode.el --- Major mode for editing GNU LilyPond music scores
3 ;;;
4 ;;; source file of the GNU LilyPond music typesetter
5 ;;;  
6 ;;; (c)  1999--2003 Jan Nieuwenhuizen <janneke@gnu.org>
7 ;;; 
8 ;;; Changed 2001--2003 Heikki Junes <heikki.junes@hut.fi>
9 ;;;    * Add PS-compilation, PS-viewing and MIDI-play (29th Aug 2001)
10 ;;;    * Keyboard shortcuts (12th Sep 2001)
11 ;;;    * Inserting tags, inspired on sgml-mode (11th Oct 2001)
12 ;;;    * Autocompletion & Info (23rd Nov 2002)
13 ;;;
14 ;;; Changed 2002 Carlos Betancourt <carlos.betancourt@chello.be>
15 ;;;    * Added spanish-note-replacements
16
17 ;;; Inspired on auctex
18
19 ;;; Look lilypond-init.el or Documentation/topdocs/INSTALL.texi
20 ;;; for installing instructions.
21
22 (require 'easymenu)
23 (require 'compile)
24
25 (defconst LilyPond-version "1.7.18"
26   "`LilyPond-mode' version number.")
27
28 (defconst LilyPond-help-address "bug-lilypond@gnu.org"
29   "Address accepting submission of bug reports.")
30
31 (defvar LilyPond-mode-hook nil
32   "*Hook called by `LilyPond-mode'.")
33
34 ;; FIXME: find ``\score'' in buffers / make settable?
35 (defun LilyPond-master-file ()
36   ;; duh
37   (buffer-file-name))
38
39 (defvar LilyPond-kick-xdvi nil
40   "If true, no simultaneous xdvi's are started, but reload signal is sent.")
41
42 (defvar LilyPond-command-history nil
43   "Command history list.")
44         
45 (defvar LilyPond-regexp-alist
46   '(("\\([a-zA-Z]?:?[^:( \t\n]+\\)[:( \t]+\\([0-9]+\\)[:) \t]" 1 2))
47   "Regexp used to match LilyPond errors.  See `compilation-error-regexp-alist'.")
48
49 (defcustom LilyPond-include-path ".:/tmp"
50   "* LilyPond include path."
51   :type 'string
52   :group 'LilyPond)
53
54 (defun LilyPond-words-filename ()
55   "The file containing LilyPond \keywords \Identifiers and ReservedWords.
56 Finds file lilypond-words from load-path."
57   (let ((fn nil)
58         (lp load-path)
59         (words-file "lilypond.words"))
60     (while (and (> (length lp) 0) (not fn))
61       (setq fn (concat (car lp) "/" words-file))
62       (if (not (file-readable-p fn)) 
63           (progn (setq fn nil) (setq lp (cdr lp)))))
64     (if (not fn)
65         (progn (message "Warning: `lilypond.words' not found in `load-path'. See `lilypond-init.el'.")
66                (sit-for 5 0)))
67     fn))
68
69 (defun LilyPond-add-dictionary-word (x)
70   "Contains all words: \keywords \Identifiers and ReservedWords."
71   (nconc '(("" . 1)) x))
72
73 ;; creates dictionary if empty
74 (if (and (eq (length (LilyPond-add-dictionary-word ())) 1)
75          (not (eq (LilyPond-words-filename) nil)))
76     (progn
77       (setq b (find-file-noselect (LilyPond-words-filename) t t))
78       (setq m (set-marker (make-marker) 1 (get-buffer b)))
79       (setq i 1)
80       (while (> (buffer-size b) (marker-position m))
81         (setq i (+ i 1))
82         (setq copy (copy-alist (list (eval (symbol-name (read m))))))
83         (setcdr copy i)
84         (LilyPond-add-dictionary-word (list copy)))
85       (kill-buffer b)))
86
87 (defconst LilyPond-keywords 
88   (let ((wordlist ())
89         (co (all-completions "" (LilyPond-add-dictionary-word ()))))
90     (progn
91       (while (> (length co) 0)
92         (if (> (length (car co)) 1)
93             (if (and (string-equal "\\" (substring (car co) 0 1))
94                      (string-equal (downcase (car co)) (car co)))
95                 (add-to-list 'wordlist (car co))))
96         (setq co (cdr co)))
97       (if (eq (length wordlist) 0)
98           (setq wordlist '("\\score"))) ; add \keywords to lilypond.words
99       wordlist))
100   "LilyPond \\keywords")
101
102 (defconst LilyPond-identifiers 
103   (let ((wordlist ())
104         (co (all-completions "" (LilyPond-add-dictionary-word ()))))
105     (progn
106       (while (> (length co) 0)
107         (if (> (length (car co)) 1)
108             (if (and (string-equal "\\" (substring (car co) 0 1))
109                      (not (string-equal (downcase (car co)) (car co))))
110                 (add-to-list 'wordlist (car co))))
111         (setq co (cdr co)))
112       (if (eq (length wordlist) 0)
113           (setq wordlist '("\\voiceOne"))) ; add \Identifiers to lilypond.words
114       wordlist))
115   "LilyPond \\Identifiers")
116
117 (defconst LilyPond-reserved-words 
118   (let ((wordlist ())
119         (co (all-completions "" (LilyPond-add-dictionary-word ()))))
120     (progn
121       (while (> (length co) 0)
122         (if (> (length (car co)) 0)
123             (if (not (string-equal "\\" (substring (car co) 0 1)))
124                 (add-to-list 'wordlist (car co))))
125         (setq co (cdr co)))
126       (if (eq (length wordlist) 0)
127           (setq wordlist '("Staff"))) ; add ReservedWords to lilypond.words
128       wordlist))
129   "LilyPond ReservedWords")
130
131 (defun LilyPond-check-files (derived originals extensions)
132   "Check that DERIVED is newer than any of the ORIGINALS.
133 Try each original with each member of EXTENSIONS, in all directories
134 in LilyPond-include-path."
135   (let ((found nil)
136         (regexp (concat "\\`\\("
137                         (mapconcat (function (lambda (dir)
138                                       (regexp-quote (expand-file-name dir))))
139                                    LilyPond-include-path "\\|")
140                         "\\).*\\("
141                         (mapconcat 'regexp-quote originals "\\|")
142                         "\\)\\.\\("
143                         (mapconcat 'regexp-quote extensions "\\|")
144                         "\\)\\'"))
145         (buffers (buffer-list)))
146     (while buffers
147       (let* ((buffer (car buffers))
148              (name (buffer-file-name buffer)))
149         (setq buffers (cdr buffers))
150         (if (and name (string-match regexp name))
151             (progn
152               (and (buffer-modified-p buffer)
153                    (or (not LilyPond-save-query)
154                        (y-or-n-p (concat "Save file "
155                                          (buffer-file-name buffer)
156                                          "? ")))
157                    (save-excursion (set-buffer buffer) (save-buffer)))
158               (if (file-newer-than-file-p name derived)
159                   (setq found t))))))
160     found))
161
162 (defun LilyPond-running ()
163   "Check the currently running LilyPond compiling jobs."
164   (interactive)
165   (let ((process-names (list "lilypond" "tex" "2dvi" "2ps" "2midi" 
166                              "book" "latex"))
167         (running nil))
168     (while (setq process-name (pop process-names))
169       (setq process (get-process process-name))
170       (if (and process 
171                (eq (process-status process) 'run))
172           (push process-name running)))
173     running)) ; return the running jobs
174
175 (defun LilyPond-midi-running ()
176   "Check the currently running Midi processes."
177   (interactive)
178   (let ((process-names (list "midi" "midiall"))
179         (running nil))
180     (while (setq process-name (pop process-names))
181       (setq process (get-process process-name))
182       (if (and process 
183                (eq (process-status process) 'run))
184           (push process-name running)))
185     running)) ; return the running jobs
186
187 (defun LilyPond-kill-jobs ()
188   "Kill the currently running LilyPond compiling jobs."
189   (interactive)
190   (let ((process-names (LilyPond-running))
191         (killed nil))
192     (while (setq process-name (pop process-names))
193       (quit-process (get-process process-name) t)
194       (push process-name killed))
195     killed)) ; return the killed jobs
196
197 (defun LilyPond-kill-midi ()
198   "Kill the currently running midi processes."
199   (let ((process-names (LilyPond-midi-running))
200         (killed nil))
201     (while (setq process-name (pop process-names))
202       (quit-process (get-process process-name) t)
203       (push process-name killed))
204     killed)) ; return the killed jobs
205
206 ;; URG, should only run LilyPond-compile for LilyPond
207 ;; not for tex,xdvi (ly2dvi?)
208 (defun LilyPond-compile-file (command name)
209   ;; We maybe should know what we run here (Lily, ly2dvi, tex)
210   ;; and adjust our error-matching regex ?
211   (compile-internal command "No more errors" name ))
212
213 ;; do we still need this, now that we're using compile-internal?
214 (defun LilyPond-save-buffer ()
215   "Save buffer and set default command for compiling."
216   (interactive)
217   (if (buffer-modified-p)
218       (progn (save-buffer)
219              (setq LilyPond-command-default "LilyPond"))))
220
221 ;;; return (dir base ext)
222 (defun split-file-name (name)
223   (let* ((i (string-match "[^/]*$" name))
224          (dir (if (> i 0) (substring name 0 i) "./"))
225          (file (substring name i (length name)))
226          (i (string-match "[^.]*$" file)))
227     (if (and
228          (> i 0)
229          (< i (length file)))
230         (list dir (substring file 0 (- i 1)) (substring file i (length file)))
231       (list dir file ""))))
232
233
234 ;; Should check whether in command-alist?
235 (defcustom LilyPond-command-default "LilyPond"
236   "Default command. Must identify a member of LilyPond-command-alist."
237
238   :group 'LilyPond
239   :type 'string)
240 ;;;(make-variable-buffer-local 'LilyPond-command-last)
241
242 (defvar LilyPond-command-current 'LilyPond-command-master)
243 ;;;(make-variable-buffer-local 'LilyPond-command-master)
244
245
246 ;; If non-nil, LilyPond-command-query will return the value of this
247 ;; variable instead of quering the user. 
248 (defvar LilyPond-command-force nil)
249
250 (defcustom LilyPond-xdvi-command "xdvi"
251   "Command used to display DVI files."
252
253   :group 'LilyPond
254   :type 'string)
255
256 (defcustom LilyPond-gv-command "gv -watch"
257   "Command used to display PS files."
258
259   :group 'LilyPond
260   :type 'string)
261
262 (defcustom LilyPond-midi-command "timidity"
263   "Command used to play MIDI files."
264
265   :group 'LilyPond
266   :type 'string)
267
268 (defcustom LilyPond-all-midi-command "timidity -ia"
269   "Command used to play MIDI files."
270
271   :group 'LilyPond
272   :type 'string)
273
274 (defun LilyPond-command-current-midi ()
275   "Play midi corresponding to the current document."
276   (interactive)
277   (LilyPond-command (LilyPond-command-menu "Midi") 'LilyPond-master-file))
278
279 (defun LilyPond-command-all-midi ()
280   "Play midi corresponding to the current document."
281   (interactive)
282   (LilyPond-command (LilyPond-command-menu "MidiAll") 'LilyPond-master-file))
283
284 (defun count-rexp (start end rexp)
285   "Print number of found regular expressions in the region."
286   (interactive "r")
287   (save-excursion
288     (save-restriction
289       (narrow-to-region start end)
290       (goto-char (point-min))
291       (count-matches rexp))))
292
293 (defun count-midi-words ()
294   "Check number of midi-scores before the curser."
295   (interactive)
296   (count-rexp (point-min) (point-max) "\\\\midi"))
297  
298 (defun count-midi-words-backwards ()
299   "Check number of midi-scores before the curser."
300   (interactive)
301   (count-rexp (point-min) (point) "\\\\midi"))
302  
303 (defun LilyPond-string-current-midi ()
304   "Check the midi file of the following midi-score in the current document."
305   (interactive)
306   (let ((fnameprefix (substring (LilyPond-master-file) 0 -3)) ; suppose ".ly"
307         (allcount (string-to-number (substring (count-midi-words) 0 -12)))
308         (count (string-to-number (substring (count-midi-words-backwards) 0 -12))))
309     (concat  fnameprefix
310              (if (and (> allcount 1) (> count 0)) ; not first score
311                  (if (eq count allcount)          ; last score
312                      (concat "-" (number-to-string (+ count -1)))
313                    (concat "-" (number-to-string count))))
314              ".midi")))
315
316 (defun LilyPond-string-all-midi ()
317   "Return the midi files of the current document in ascending order."
318   (interactive)
319   (let ((fnameprefix (substring (LilyPond-master-file) 0 -3))
320         (allcount (string-to-number (substring (count-midi-words) 0 -12))))
321     (concat (if (> allcount 0)  ; at least one midi-score
322                 (concat fnameprefix ".midi "))
323             (if (> allcount 1)  ; more than one midi-score
324                 (concat fnameprefix "-[1-9].midi "))
325             (if (> allcount 9)  ; etc.
326                 (concat fnameprefix "-[1-9][0-9].midi"))
327             (if (> allcount 99) ; not first score
328                 (concat fnameprefix "-[1-9][0-9][0-9].midi")))))
329
330 ;; This is the major configuration variable.
331 (defcustom LilyPond-command-alist
332   ;; Should expand this to include possible keyboard shortcuts which
333   ;; could then be mapped to define-key and menu.
334   `(
335     ("LilyPond" . ("lilypond %s" . "LaTeX"))
336     ("TeX" . ("tex '\\nonstopmode\\input %t'" . "View"))
337
338     ("2Dvi" . ("ly2dvi %s" . "View"))
339     ("2PS" . ("ly2dvi -P %s" . "ViewPS"))
340     ("2Midi" . ("lilypond -m %s" . "View"))
341
342     ("Book" . ("lilypond-book %x" . "LaTeX"))
343     ("LaTeX" . ("latex '\\nonstopmode\\input %l'" . "View"))
344
345     ;; point-n-click (arg: exits upop USR1)
346     ("SmartView" . ("xdvi %d" . "LilyPond"))
347
348     ;; refreshes when kicked USR1
349     ("View" . (,(concat LilyPond-xdvi-command " %d") . "LilyPond"))
350     ("ViewPS" . (,(concat LilyPond-gv-command " %p") . "LilyPond"))
351
352     ;; The following are refreshed in LilyPond-command:
353     ;; - current-midi depends on cursor position and
354     ("Midi" . (,(concat LilyPond-midi-command " " (LilyPond-string-current-midi)) . "LilyPond" )) ; 
355     ;; - all-midi depends on number of midi-score.
356     ("MidiAll" . (,(concat LilyPond-all-midi-command " " (LilyPond-string-all-midi)) . "LilyPond"))
357     )
358
359   "AList of commands to execute on the current document.
360
361 The key is the name of the command as it will be presented to the
362 user, the value is a cons of the command string handed to the shell
363 after being expanded, and the next command to be executed upon
364 success.  The expansion is done using the information found in
365 LilyPond-expand-list.
366 "
367   :group 'LilyPond
368   :type '(repeat (cons :tag "Command Item"
369                        (string :tag "Key")
370                        (cons :tag "How"
371                         (string :tag "Command")
372                         (string :tag "Next Key")))))
373
374 ;; drop this?
375 (defcustom LilyPond-file-extensions '(".ly" ".sly" ".fly")
376   "*File extensions used by manually generated TeX files."
377   :group 'LilyPond
378   :type '(repeat (string :format "%v")))
379
380
381 (defcustom LilyPond-expand-alist 
382   '(
383     ("%s" . ".ly")
384     ("%t" . ".tex")
385     ("%d" . ".dvi")
386     ("%p" . ".ps")
387     ("%l" . ".tex")
388     ("%x" . ".tely")
389     ("%m" . ".midi")
390     )
391     
392   "Alist of expansion strings for LilyPond command names."
393   :group 'LilyPond
394   :type '(repeat (cons :tag "Alist item"
395                   (string :tag "Symbol")
396                   (string :tag "Expansion")))) 
397
398
399 (defcustom LilyPond-command-Show "View"
400   "*The default command to show (view or print) a LilyPond file.
401 Must be the car of an entry in `LilyPond-command-alist'."
402   :group 'LilyPond
403   :type 'string)
404   (make-variable-buffer-local 'LilyPond-command-Show)
405
406 (defcustom LilyPond-command-Print "Print"
407   "The name of the Print entry in LilyPond-command-Print."
408   :group 'LilyPond
409   :type 'string)
410
411 (defun xLilyPond-compile-sentinel (process msg)
412   (if (and process
413            (= 0 (process-exit-status process)))
414       (setq LilyPond-command-default
415               (cddr (assoc LilyPond-command-default LilyPond-command-alist)))))
416
417 ;; FIXME: shouldn't do this for stray View/xdvi
418 (defun LilyPond-compile-sentinel (buffer msg)
419   (if (string-match "^finished" msg)
420       (setq LilyPond-command-default
421             (cddr (assoc LilyPond-command-default LilyPond-command-alist)))))
422
423 ;;(make-variable-buffer-local 'compilation-finish-function)
424 (setq compilation-finish-function 'LilyPond-compile-sentinel)
425
426 (defun LilyPond-command-query (name)
427   "Query the user for what LilyPond command to use."
428   (let* ((default (cond ((if (string-equal name "emacs-lily")
429                              (LilyPond-check-files (concat name ".tex")
430                                                    (list name)
431                                                    LilyPond-file-extensions)
432                            (if (verify-visited-file-modtime (current-buffer))
433                                (if (buffer-modified-p)
434                                    (if (y-or-n-p "Save buffer before next command? ")
435                                        (LilyPond-save-buffer)))
436                              (if (y-or-n-p "The command will be invoked to an already saved buffer. Revert it? ")
437                                  (revert-buffer t t)))
438                            ;;"LilyPond"
439                            LilyPond-command-default))
440                         (t LilyPond-command-default)))
441
442          (completion-ignore-case t)
443          
444          (answer (or LilyPond-command-force
445                      (completing-read
446                       (concat "Command: (default " default ") ")
447                       LilyPond-command-alist nil t nil 'LilyPond-command-history))))
448
449     ;; If the answer is "LilyPond" it will not be expanded to "LilyPond"
450     (let ((answer (car-safe (assoc answer LilyPond-command-alist))))
451       (if (and answer
452                (not (string-equal answer "")))
453           answer
454         default))))
455
456 (defun LilyPond-command-master ()
457   "Run command on the current document."
458   (interactive)
459   (LilyPond-command (LilyPond-command-query (LilyPond-master-file))
460                     'LilyPond-master-file))
461
462 (defun LilyPond-command-lilypond ()
463   "Run lilypond for the current document."
464   (interactive)
465   (LilyPond-command (LilyPond-command-menu "LilyPond") 'LilyPond-master-file)
466 )
467
468 (defun LilyPond-command-formatdvi ()
469   "Format the dvi output of the current document."
470   (interactive)
471   (LilyPond-command (LilyPond-command-menu "2Dvi") 'LilyPond-master-file)
472 )
473
474 (defun LilyPond-command-formatps ()
475   "Format the ps output of the current document."
476   (interactive)
477   (LilyPond-command (LilyPond-command-menu "2PS") 'LilyPond-master-file)
478 )
479
480 (defun LilyPond-command-formatmidi ()
481   "Format the midi output of the current document."
482   (interactive)
483   (LilyPond-command (LilyPond-command-menu "2Midi") 'LilyPond-master-file)
484 )
485
486 (defun LilyPond-command-smartview ()
487   "View the dvi output of current document."
488   (interactive)
489   (LilyPond-command (LilyPond-command-menu "SmartView") 'LilyPond-master-file)
490 )
491
492 (defun LilyPond-command-view ()
493   "View the dvi output of current document."
494   (interactive)
495   (LilyPond-command (LilyPond-command-menu "View") 'LilyPond-master-file)
496 )
497
498 (defun LilyPond-command-viewps ()
499   "View the ps output of current document."
500   (interactive)
501   (LilyPond-command (LilyPond-command-menu "ViewPS") 'LilyPond-master-file)
502 )
503
504 ;;; Refer to syntax-table (in lilypond-font-lock.el) for comment characters.
505 (defun LilyPond-un-comment-region (start end level)
506   "Remove up to LEVEL comment characters from each line in the region."
507   (interactive "*r\np") 
508   (comment-region start end (- level)))
509
510 ;; FIXME, this is broken
511 (defun LilyPond-region-file (begin end)
512   (let (
513         ;; (dir "/tmp/")
514         ;; urg
515         (dir "./")
516         (base "emacs-lily")
517         ;; Hmm
518         (ext (if (string-match "^[\\]score" (buffer-substring begin end))
519                  ".ly"
520                (if (< 50 (abs (- begin end)))
521                    ".fly"
522                  ".sly"))))
523     (concat dir base ext)))
524
525 (defun LilyPond-command-region (begin end)
526   "Run LilyPond on the current region."
527   (interactive "r")
528   (write-region begin end (LilyPond-region-file begin end) nil 'nomsg)
529   (LilyPond-command (LilyPond-command-query
530                      (LilyPond-region-file begin end))
531                     '(lambda () (LilyPond-region-file begin end))))
532
533 (defun LilyPond-command-buffer ()
534   "Run LilyPond on buffer."
535   (interactive)
536   (LilyPond-command-region (point-min) (point-max)))
537
538 (defun LilyPond-command-expand (string file)
539   (let ((case-fold-search nil))
540     (if (string-match "%" string)
541         (let* ((b (match-beginning 0))
542                (e (+ b 2))
543                (l (split-file-name file))
544                (dir (car l))
545                (base (cadr l)))
546           (LilyPond-command-expand
547            (concat (substring string 0 b)
548                    dir
549                    base
550                    (let ((entry (assoc (substring string b e)
551                                        LilyPond-expand-alist)))
552                      (if entry (cdr entry) ""))
553                    (substring string e))
554            file))
555       string)))
556
557 (defun LilyPond-shell-process (name buffer command)
558   (let ((old (current-buffer)))
559     (switch-to-buffer-other-window buffer)
560     ;; If we empty the buffer don't see messages scroll by.
561     ;; (erase-buffer)
562     
563     (start-process-shell-command name buffer command)
564     (switch-to-buffer-other-window old)))
565   
566
567 (defun LilyPond-command (name file)
568   "Run command NAME on the file you get by calling FILE.
569
570 FILE is a function return a file name.  It has one optional argument,
571 the extension to use on the file.
572
573 Use the information in LilyPond-command-alist to determine how to run the
574 command."
575   
576   (let ((entry (assoc name LilyPond-command-alist)))
577     (if entry
578         (let ((command (LilyPond-command-expand (cadr entry)
579                                                 (apply file nil)))
580               (jobs nil)
581               (job-string "no jobs"))
582           (if (member name (list "View" "ViewPS"))
583               ;; is USR1 a right signal for viewps?
584               (let ((buffer-xdvi (get-buffer-create (concat "*" name "*"))))
585                 (if LilyPond-kick-xdvi
586                   (let ((process-xdvi (get-buffer-process buffer-xdvi)))
587                     (if process-xdvi
588                         (signal-process (process-id process-xdvi) 'SIGUSR1)
589                       (LilyPond-shell-process name buffer-xdvi command)))
590                   (LilyPond-shell-process name buffer-xdvi command)))
591             (progn
592               (if (string-equal name "Midi")
593                   (progn
594                     (setq command (concat LilyPond-midi-command " " (LilyPond-string-current-midi)))
595                     (if (LilyPond-kill-midi)
596                         (setq job-string nil)))) ; either stop or start playing
597               (if (string-equal name "MidiAll")
598                   (progn
599                     (setq command (concat LilyPond-all-midi-command " " (LilyPond-string-all-midi)))
600                     (LilyPond-kill-midi))) ; stop and start playing
601               (if (and (member name (list "Midi" "MidiAll")) job-string)
602                   (if (file-newer-than-file-p
603                        (LilyPond-master-file)
604                        (concat (substring (LilyPond-master-file) 0 -3) ".midi"))
605                       (if (y-or-n-p "Midi older than source. Reformat midi?")
606                           (progn
607                             (LilyPond-command-formatmidi)
608                             (while (LilyPond-running)
609                               (message "Starts playing midi once it is built.")
610                               (sit-for 0 100))))))
611               (if (member name (list "LilyPond" "TeX" "2Midi" "2PS" "2Dvi" 
612                                      "Book" "LaTeX"))
613                   (if (setq jobs (LilyPond-running))
614                       (progn
615                         (setq job-string "Process") ; could also suggest compiling after process has ended
616                         (while jobs
617                           (setq job-string (concat job-string " \"" (pop jobs) "\"")))
618                         (setq job-string (concat job-string " is already running; kill it to proceed "))
619                         (if (y-or-n-p job-string)
620                             (progn
621                               (setq job-string "no jobs")
622                               (LilyPond-kill-jobs)
623                               (while (LilyPond-running)
624                                 (sit-for 0 100)))
625                           (setq job-string nil)))))
626
627               (setq LilyPond-command-default name)
628               (if (string-equal job-string "no jobs")
629                   (LilyPond-compile-file command name))))))))
630           
631 ;; XEmacs stuff
632 ;; Sadly we need this for a macro in Emacs 19.
633 (eval-when-compile
634   ;; Imenu isn't used in XEmacs, so just ignore load errors.
635   (condition-case ()
636       (require 'imenu)
637     (error nil)))
638
639
640 ;;; Keymap
641
642 (defvar LilyPond-mode-map ()
643   "Keymap used in `LilyPond-mode' buffers.")
644
645 ;; Note:  if you make changes to the map, you must do
646 ;;    M-x set-variable LilyPond-mode-map nil
647 ;;    M-x eval-buffer
648 ;;    M-x LilyPond-mode
649 ;; to let the changest take effect
650
651 (if LilyPond-mode-map
652     ()
653   (setq LilyPond-mode-map (make-sparse-keymap))
654   ;; Put keys to Lilypond-command-alist and fetch them from there somehow.
655   (define-key LilyPond-mode-map "\C-c\C-l" 'LilyPond-command-lilypond)
656   (define-key LilyPond-mode-map "\C-c\C-r" 'LilyPond-command-region)
657   (define-key LilyPond-mode-map "\C-c\C-b" 'LilyPond-command-buffer)
658   (define-key LilyPond-mode-map "\C-c\C-k" 'LilyPond-kill-jobs)
659   (define-key LilyPond-mode-map "\C-c\C-c" 'LilyPond-command-master)
660   (define-key LilyPond-mode-map "\C-cm" 'LilyPond-command-formatmidi)
661   (define-key LilyPond-mode-map "\C-c\C-d" 'LilyPond-command-formatdvi)
662   (define-key LilyPond-mode-map "\C-c\C-f" 'LilyPond-command-formatps)
663   (define-key LilyPond-mode-map "\C-c\C-s" 'LilyPond-command-smartview)
664   (define-key LilyPond-mode-map "\C-c\C-v" 'LilyPond-command-view)
665   (define-key LilyPond-mode-map "\C-c\C-p" 'LilyPond-command-viewps)
666   (define-key LilyPond-mode-map [(control c) return] 'LilyPond-command-current-midi)
667   (define-key LilyPond-mode-map [(control c) (control return)] 'LilyPond-command-all-midi)
668   (define-key LilyPond-mode-map "\C-x\C-s" 'LilyPond-save-buffer)
669   (define-key LilyPond-mode-map "\C-cf" 'font-lock-fontify-buffer)
670   (define-key LilyPond-mode-map "\C-ci" 'LilyPond-quick-note-insert)
671   (define-key LilyPond-mode-map "\C-cn" 'LilyPond-insert-tag-notes)
672   (define-key LilyPond-mode-map "\C-cs" 'LilyPond-insert-tag-score)
673   (define-key LilyPond-mode-map "\C-c:" 'LilyPond-un-comment-region)
674   (define-key LilyPond-mode-map "\C-c;" 'comment-region)
675   (define-key LilyPond-mode-map ")" 'LilyPond-electric-close-paren)
676   (define-key LilyPond-mode-map ">" 'LilyPond-electric-close-paren)
677   (define-key LilyPond-mode-map "}" 'LilyPond-electric-close-paren)
678   (define-key LilyPond-mode-map [iso-lefttab] 'LilyPond-autocompletion) ; Emacs
679   (define-key LilyPond-mode-map [iso-left-tab] 'LilyPond-autocompletion) ; XEmacs
680   (define-key LilyPond-mode-map "\C-c\t" 'LilyPond-info-index-search)
681   )
682
683 ;;; Menu Support
684
685 (defun LilyPond-quick-note-insert()
686   "Insert notes with fewer key strokes by using a simple keyboard piano."
687   (interactive)
688   (setq dutch-notes
689        '(("k" "a") ("l" "b") ("a" "c") ("s" "d")
690          ("d" "e") ("f" "f") ("j" "g") ("r" "r")))
691   (setq dutch-note-ends '("eses" "es" "" "is" "isis"))
692   (setq dutch-note-replacements '("" ""))
693   (setq finnish-note-replacements
694        '(("eeses" "eses") ("ees" "es") ("aeses" "asas") ("aes" "as") ("b" "h")
695          ("beses" "heses") ("bes" "b") ("bis" "his") ("bisis" "hisis")))
696                              ; add more translations of the note names
697   (setq spanish-note-replacements
698        '(("c" "do") ("d" "re") ("e" "mi") ("f" "fa") ("g" "sol") ("a" "la") ("b
699 " "si")
700       ("cis" "dos") ("cisis" "doss") ("ces" "dob") ("ceses" "dobb")
701       ("dis" "res") ("disis" "ress") ("des" "reb") ("deses" "rebb")
702       ("eis" "mis") ("eisis" "miss") ("ees" "mib") ("eeses" "mibb")
703       ("fis" "fas") ("fisis" "fass") ("fes" "fab") ("feses" "fabb")
704       ("gis" "sols") ("gisis" "solss") ("ges" "solb") ("geses" "solbb")
705       ("ais" "las") ("aisis" "lass") ("aes" "lab") ("aeses" "labb")
706       ("bis" "sis") ("bisis" "siss") ("bes" "sib") ("beses" "sibb")))
707   (setq other-keys "()<>~}")
708   (setq accid 0) (setq octav 0) (setq durat "") (setq dots 0)
709
710   (message "Press h for help.") (sit-for 0 750)
711
712   (setq note-replacements dutch-note-replacements)
713   (setq xkey 0) (setq xinitpoint (point))
714   (< xinitpoint (point))
715   (while (not (= xkey 27)) ; esc to quit
716     (message (format " a[]s[]d f[]j[]k[]l r with %4s%-4s%3s%-4s ie ,' 12345678 . 0 (<~>)/}b\\b\\n Esc"
717                      (nth (+ accid 2) dutch-note-ends)
718                      (make-string (abs octav) (if (> octav 0) ?' ?,))
719                      durat
720                      (if (string= durat "") "" (make-string dots ?.))))
721     (setq xkey (read-char-exclusive))
722     (setq x (char-to-string xkey))
723     (setq note (cdr (assoc x dutch-notes)))
724     (cond
725      ((string= x "q") (progn (setq xkey 27))) ; quit
726      ((= xkey 13) (progn (insert "\n") (LilyPond-indent-line))) ; return
727      ((or (= xkey 127) (string= x "b")) ; backspace is a char only in Emacs
728       (progn (narrow-to-region xinitpoint (point))
729              (backward-kill-word 1)
730              (widen)))
731      ((and (string< x "9") (string< "0" x))
732       (progn (setq durat (int-to-string (expt 2 (- (string-to-int x) 1))))
733              (setq dots 0)))
734      ((string= x " ") (insert " "))
735      ((string= x "/") (progn (insert "\\times ")
736                              (message "Insert a number for the denominator (\"x/\")")
737                              (while (not (and (string< x "9") (string< "0" x)))
738                                (setq x (char-to-string (read-char-exclusive))))
739                              (insert (format "%s/" x)) (setq x "/")
740                              (message "Insert a number for the numerator (\"/y\")")
741                              (while (not (and (string< x "9") (string< "0" x)))
742                                (setq x (char-to-string (read-char-exclusive))))
743                              (insert (format "%s { " x))))
744      ((string= x "0") (progn (setq accid 0) (setq octav 0)
745                              (setq durat "") (setq dots 0)))
746      ((string= x "i") (setq accid (if (= accid 2) 0 (max (+ accid 1) 1))))
747      ((string= x "e") (setq accid (if (= accid -2) 0 (min (+ accid -1) -1))))
748      ((string= x "'") (setq octav (if (= octav 4) 0 (max (+ octav 1) 1))))
749      ((string= x ",") (setq octav (if (= octav -4) 0 (min (+ octav -1) -1))))
750      ((string= x ".") (setq dots (if (= dots 4) 0 (+ dots 1))))
751      ((not (null (member x (split-string other-keys ""))))
752       (insert (format "%s " x)))
753      ((not (null note))
754       (progn
755         (setq note
756               (format "%s%s" (car note) (if (string= "r" (car note)) ""
757                                           (nth (+ accid 2) dutch-note-ends))))
758         (setq notetwo (car (cdr (assoc note note-replacements))))
759         (if (not (null notetwo)) (setq note notetwo))
760         (insert
761          (format "%s%s%s%s "
762                  note
763                  (if (string= "r" note) ""
764                      (make-string (abs octav) (if (> octav 0) ?' ?,)))
765                  durat
766                  (if (string= durat "") "" (make-string dots ?.))))
767         (setq accid 0) (setq octav 0) (setq durat "") (setq dots 0)))
768      ((string= x "t") (progn (setq note-replacements dutch-note-replacements)
769                              (message "Selected Dutch notes")
770                              (sit-for 0 750)))
771      ((string= x "n") (progn (setq note-replacements finnish-note-replacements)
772                              (message "Selected Finnish/Deutsch notes")
773                              (sit-for 0 750)))
774                               ; add more translations of the note names
775      ((string= x "p") (progn (setq note-replacements spanish-note-replacements)
776                              (message "Selected Spanish notes")
777                              (sit-for 0 750)))
778      ((string= x "h")
779       (progn (message "Insert notes with fewer key strokes. For example \"i,5.f\" produces \"fis,32. \".") (sit-for 5 0)
780              (message "Add also \"a ~ a\"-ties, \"a ( ) b\"-slurs and \"< a b >\"-chords.") (sit-for 5 0)
781              (message "There are Du(t)ch, Fin(n)ish/Deutsch (hit 'n') and S(p)anish note names.") (sit-for 5 0)
782              (message "(B)ackspace deletes last note, Ret starts a new indented line and Esc (q)uits.") (sit-for 5 0)
783              (message "Insert note triplets \"\\times 2/3 { a b } \" by typing \"/23ab}\".") (sit-for 5 0)
784              (message "This mode is experimental. Use normal mode to add further details.") (sit-for 5 0)))))
785   (message "Normal editing mode."))
786
787 (defun LilyPond-pre-word-search ()
788   "Fetch the alphabetic characters and \\ in front of the cursor."
789   (interactive)
790   (let ((pre "")
791         (prelen 0)
792         (ch (char-before (- (point) 0))))
793     (while (and ch (or (and (>= ch 65) (<= ch 90))  ; not bolp, A-Z
794                        (and (>= ch 97) (<= ch 122)) ; a-z
795                        (= ch 92)))                  ; \\
796       (setq pre (concat (char-to-string ch) pre))
797       (setq prelen (+ prelen 1))
798       (setq ch (char-before (- (point) prelen))))
799     pre))
800
801 (defun LilyPond-post-word-search ()
802   "Fetch the alphabetic characters behind the cursor."
803   (interactive)
804   (let ((post "")
805         (postlen 0)
806         (ch (char-after (+ (point) 0))))
807     (while (and ch (or (and (>= ch 65) (<= ch 90))    ; not eolp, A-Z
808                        (and (>= ch 97) (<= ch 122)))) ; a-z
809       (setq post (concat post (char-to-string ch)))
810       (setq postlen (+ postlen 1))
811       (setq ch (char-after (+ (point) postlen))))
812     post))
813
814 (defun LilyPond-autocompletion ()
815   "Show completions in mini-buffer for the given word."
816   (interactive)
817   (let ((pre (LilyPond-pre-word-search))
818         (post (LilyPond-post-word-search))
819         (compsstr ""))
820     ;; insert try-completion and show all-completions
821     (if (> (length pre) 0)
822         (progn
823           (setq trycomp (try-completion pre (LilyPond-add-dictionary-word ())))
824           (if (char-or-string-p trycomp)
825               (if (string-equal (concat pre post) trycomp)
826                   (goto-char (+ (point) (length post)))
827                 (progn
828                   (delete-region (point) (+ (point) (length post)))
829                   (insert (substring trycomp (length pre) nil))))
830             (progn
831               (delete-region (point) (+ (point) (length post)))
832               (font-lock-fontify-buffer))) ; only inserting fontifies
833         
834         (setq complist (all-completions pre (LilyPond-add-dictionary-word ())))
835         (while (> (length complist) 0)
836           (setq compsstr (concat compsstr "\"" (car complist) "\" "))
837           (setq complist (cdr complist)))
838         (message compsstr) 
839         (sit-for 0 100)))))
840
841 (defun LilyPond-info ()
842   "Launch Info for lilypond."
843   (interactive)
844   (info "lilypond"))
845   
846 (defun LilyPond-music-glossary-info ()
847   "Launch Info for music-glossary."
848   (interactive)
849   (info "music-glossary"))
850
851 (defun LilyPond-internals-info ()
852   "Launch Info for lilypond-internals."
853   (interactive)
854   (info "lilypond-internals"))
855   
856 (defun LilyPond-info-index-search ()
857   "Inside Emacs, launch `info lilypond --index-search word-under-cursor'"
858   (interactive)
859   (let ((str (concat (LilyPond-pre-word-search) (LilyPond-post-word-search))))
860     (if (and (> (length str) 0) 
861              (string-equal (substring str 0 1) "\\"))
862         (setq str (substring str 1 nil)))
863     (LilyPond-info)
864     (Info-index str)))
865
866 (defun LilyPond-insert-string (pre)
867   "Insert text to the buffer."
868   (interactive)
869   (insert pre)
870   (length pre))
871
872 (defun LilyPond-insert-between (text pre post)
873   "Insert text to the buffer if non-empty string is given."
874   (interactive)
875   (let ((str (read-string text)))
876     (if (string-equal str "")
877         0
878       (progn (setq pre_str_post (concat pre str post))
879              (insert pre_str_post)
880              (length pre_str_post)))))
881
882 (defun LilyPond-insert-tag-notes ()
883   "LilyPond notes tag."
884   (interactive)
885   (setq begin (if (and transient-mark-mode mark-active) 
886                   (mark-marker) (point-marker)))
887   (setq end (point-marker))
888   (goto-char begin)
889   (setq l1 (LilyPond-insert-string "\\notes "))
890   (setq l2 (LilyPond-insert-between "Relative (e.g. c'): " "\\relative " " "))
891   (if (eq l2 0)
892       (setq l2 (LilyPond-insert-between "Transpose (e.g. c c'): " "\\transpose " " ")))
893   (setq l3 (LilyPond-insert-string "{ "))
894   (goto-char (+ end l1 l2 l3))
895   (LilyPond-insert-string " }")
896   (goto-char (+ end l1 l2 l3)))
897
898 (defun LilyPond-insert-tag-score ()
899   "LilyPond score tag."
900   (interactive)
901   (setq begin (if (and transient-mark-mode mark-active) 
902                   (mark-marker) (point-marker)))
903   (setq end (point-marker))
904   (goto-char begin)
905   (setq l1 (LilyPond-insert-string "\\score {\n    ")) ; keep track of lengths
906   (goto-char (+ end l1))
907   (LilyPond-insert-string "\n    \\paper { }\n")
908   (setq l2 (if (y-or-n-p "Insert \"\\header\" field? ")
909                (+ (LilyPond-insert-string "    \\header {")
910                   (LilyPond-insert-between "Title: " "\n      title = \"" "\"")
911                   (LilyPond-insert-between "Subtitle: " "\n      subtitle = \"" "\"")
912                   (LilyPond-insert-between "Piece: " "\n      piece = \"" "\"")
913                   (LilyPond-insert-between "Opus: "  "\n      opus = \"" "\"")
914                   (LilyPond-insert-string "\n    }\n"))
915              0))
916   (setq l3 (if (y-or-n-p "Insert \"\\midi\" field? ")
917                (+ (LilyPond-insert-string "    \\midi {")
918                   (LilyPond-insert-between "Tempo: " " \\tempo (e.g. 4 = 60)" "")
919                   (LilyPond-insert-string " }\n"))
920              0))
921   (setq l4 (LilyPond-insert-string "}\n"))
922   (goto-char (+ end l1)))
923
924 (defun LilyPond-command-menu-entry (entry)
925   ;; Return LilyPond-command-alist ENTRY as a menu item.
926   (let ((name (car entry)))
927     (cond ((and (string-equal name LilyPond-command-Print)
928                 LilyPond-printer-list)
929            (let ((command LilyPond-print-command)
930                  (lookup 1))
931              (append (list LilyPond-command-Print)
932                      (mapcar 'LilyPond-command-menu-printer-entry
933                              LilyPond-printer-list))))
934           (t
935            (vector name (list 'LilyPond-command-menu name) t)))))
936
937
938 (easy-menu-define LilyPond-command-menu
939   LilyPond-mode-map
940   "Menu used in LilyPond mode."
941   (append '("Command")
942           '(("Command on"
943              [ "Master File" LilyPond-command-select-master
944                :keys "C-c C-c" :style radio
945                :selected (eq LilyPond-command-current 'LilyPond-command-master) ]
946              [ "Buffer" LilyPond-command-select-buffer
947                :keys "C-c C-b" :style radio
948                :selected (eq LilyPond-command-current 'LilyPond-command-buffer) ]
949              [ "Region" LilyPond-command-select-region
950                :keys "C-c C-r" :style radio
951                :selected (eq LilyPond-command-current 'LilyPond-command-region) ]))
952 ;;;       (let ((file 'LilyPond-command-on-current))
953 ;;;         (mapcar 'LilyPond-command-menu-entry LilyPond-command-alist))
954 ;;; Some kind of mapping which includes :keys might be more elegant
955 ;;; Put keys to Lilypond-command-alist and fetch them from there somehow.
956           '([ "LilyPond" LilyPond-command-lilypond t])
957           '([ "TeX" (LilyPond-command (LilyPond-command-menu "TeX") 'LilyPond-master-file) ])
958           '([ "2Dvi" LilyPond-command-formatdvi t])
959           '([ "2PS" LilyPond-command-formatps t])
960           '([ "2Midi" LilyPond-command-formatmidi t])
961           '([ "Book" (LilyPond-command (LilyPond-command-menu "Book") 'LilyPond-master-file) ])
962           '([ "LaTeX" (LilyPond-command (LilyPond-command-menu "LaTeX") 'LilyPond-master-file) ])
963           '([ "Kill jobs" LilyPond-kill-jobs t])
964           '("-----")
965           '([ "SmartView" LilyPond-command-smartview t])
966           '([ "View" LilyPond-command-view t])
967           '([ "ViewPS" LilyPond-command-viewps t])
968           '("-----")
969           '([ "Midi (toggle)" LilyPond-command-current-midi t])
970           '([ "Midi all" LilyPond-command-all-midi t])
971           ))
972
973 (easy-menu-define LilyPond-mode-menu
974   LilyPond-mode-map
975   "Menu used in LilyPond mode."
976   (append '("LilyPond")
977           '(("Insert"
978              [ "\\notes..."  LilyPond-insert-tag-notes t]
979              [ "\\score..."  LilyPond-insert-tag-score t]
980              ["Quick Notes"  LilyPond-quick-note-insert t]
981              ["Autocompletion"   LilyPond-autocompletion t]
982              ))
983           '(("Miscellaneous"
984              ["Uncomment Region" LilyPond-un-comment-region t]
985              ["Comment Region" comment-region t]
986              ["Refontify buffer" font-lock-fontify-buffer t]
987              ))
988           '(("Info"
989              ["LilyPond" LilyPond-info t]
990              ["LilyPond index-search" LilyPond-info-index-search t]
991              ["Music Glossary" LilyPond-music-glossary-info t]
992              ["LilyPond internals" LilyPond-internals-info t]
993              ))
994           ))
995
996 (defconst LilyPond-imenu-generic-re "^\\([a-zA-Z]+\\) *="
997   "Regexp matching Identifier definitions.")
998
999 (defvar LilyPond-imenu-generic-expression
1000   (list (list nil LilyPond-imenu-generic-re 1))
1001   "Expression for imenu")
1002
1003 (defun LilyPond-command-select-master ()
1004   (interactive)
1005   (message "Next command will be on the master file")
1006   (setq LilyPond-command-current 'LilyPond-command-master))
1007
1008 (defun LilyPond-command-select-buffer ()
1009   (interactive)
1010   (message "Next command will be on the buffer")
1011   (setq LilyPond-command-current 'LilyPond-command-buffer))
1012
1013 (defun LilyPond-command-select-region ()
1014   (interactive)
1015   (message "Next command will be on the region")
1016   (setq LilyPond-command-current 'LilyPond-command-region))
1017
1018 (defun LilyPond-command-menu (name)
1019   ;; Execute LilyPond-command-alist NAME from a menu.
1020   (let ((LilyPond-command-force name))
1021     (funcall LilyPond-command-current)))
1022
1023 (defun LilyPond-mode ()
1024   "Major mode for editing LilyPond music files.
1025
1026 This mode knows about LilyPond keywords and line comments, not about
1027 indentation or block comments.  It features easy compilation, error
1028 finding and viewing of a LilyPond source buffer or region.
1029
1030 COMMANDS
1031 \\{LilyPond-mode-map}
1032 VARIABLES
1033
1034 LilyPond-command-alist\t\talist from name to command
1035 LilyPond-xdvi-command\t\tcommand to display dvi files -- bit superfluous"
1036   (interactive)
1037   ;; set up local variables
1038   (kill-all-local-variables)
1039
1040   (make-local-variable 'font-lock-defaults)
1041   (setq font-lock-defaults '(LilyPond-font-lock-keywords))
1042
1043   ;; string and comments are fontified explicitly
1044   (make-local-variable 'font-lock-keywords-only)
1045   (setq font-lock-keywords-only t)
1046
1047   ;; Multi-line font-locking needs Emacs 21.1 or newer.
1048   ;; For older versions there is hotkey "C-c f".
1049   (make-local-variable 'font-lock-multiline) 
1050   (setq font-lock-multiline t) 
1051
1052   (make-local-variable 'paragraph-separate)
1053   (setq paragraph-separate "^[ \t]*$")
1054
1055   (make-local-variable 'paragraph-start)
1056   (setq paragraph-start "^[ \t]*$")
1057
1058   (make-local-variable 'comment-start)
1059   (setq comment-start "%")
1060
1061   (make-local-variable 'comment-start-skip)
1062   (setq comment-start-skip "%{? *")
1063
1064   (make-local-variable 'comment-end)
1065   (setq comment-end "")
1066
1067   (make-local-variable 'block-comment-start)
1068   (setq block-comment-start "%{")
1069
1070   (make-local-variable 'block-comment-end)  
1071   (setq block-comment-end   "%}")
1072
1073   (make-local-variable 'indent-line-function)
1074   (setq indent-line-function 'LilyPond-indent-line)
1075
1076   (set-syntax-table LilyPond-mode-syntax-table)
1077   (setq major-mode 'LilyPond-mode)
1078   (setq mode-name "LilyPond")
1079   (setq local-abbrev-table LilyPond-mode-abbrev-table)
1080   (use-local-map LilyPond-mode-map)
1081
1082   ;; In Emacs blink-...-on-screen needs to be declared (, not in XEmacs).
1083   (make-local-variable 'blink-matching-paren-on-screen)
1084   (setq blink-matching-paren-on-screen t)
1085
1086   (make-local-variable 'imenu-generic-expression)
1087   (setq imenu-generic-expression LilyPond-imenu-generic-expression)
1088   (imenu-add-to-menubar "Index")
1089
1090   ;; In XEmacs one needs to use 'easy-menu-add' (, not in Emacs).
1091   (easy-menu-add LilyPond-mode-menu)
1092   (easy-menu-add LilyPond-command-menu)
1093
1094   ;; run the mode hook. LilyPond-mode-hook use is deprecated
1095   (run-hooks 'LilyPond-mode-hook))
1096
1097 (defun LilyPond-version ()
1098   "Echo the current version of `LilyPond-mode' in the minibuffer."
1099   (interactive)
1100   (message "Using `LilyPond-mode' version %s" LilyPond-version))
1101
1102 (load-library "lilypond-font-lock")
1103 (load-library "lilypond-indent")
1104
1105 (provide 'lilypond-mode)
1106 ;;; lilypond-mode.el ends here
1107