]> git.donarmstrong.com Git - lilypond.git/blob - lilypond-mode.el
add reserved words
[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--2001 Jan Nieuwenhuizen <janneke@gnu.org>
7 ;;; 
8 ;;; Changed 2001 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 ;;;
13 ;;; Changed 2002 Carlos Betancourt <carlos.betancourt@chello.be>
14 ;;;    * Added spanish-note-replacements
15
16 ;;; Inspired on auctex
17
18 (load-library "lilypond-font-lock")
19 (load-library "lilypond-indent")
20
21 (require 'easymenu)
22 (require 'compile)
23
24 (defconst LilyPond-version "1.7.8"
25   "`LilyPond-mode' version number.")
26
27 (defconst LilyPond-help-address "bug-lilypond@gnu.org"
28   "Address accepting submission of bug reports.")
29
30 (defvar LilyPond-mode-hook nil
31   "*Hook called by `LilyPond-mode'.")
32
33 (defvar LilyPond-kick-xdvi nil
34   "If true, no simultaneous xdvi's are started, but reload signal is sent.")
35
36 (defvar LilyPond-command-history nil
37   "Command history list.")
38         
39 (defvar LilyPond-regexp-alist
40   '(("\\([a-zA-Z]?:?[^:( \t\n]+\\)[:( \t]+\\([0-9]+\\)[:) \t]" 1 2))
41   "Regexp used to match LilyPond errors.  See `compilation-error-regexp-alist'.")
42
43 (defcustom LilyPond-include-path ".:/tmp"
44   "* LilyPond include path."
45   :type 'string
46   :group 'LilyPond)
47
48
49 (defun LilyPond-check-files (derived originals extensions)
50   "Check that DERIVED is newer than any of the ORIGINALS.
51 Try each original with each member of EXTENSIONS, in all directories
52 in LilyPond-include-path."
53   (let ((found nil)
54         (regexp (concat "\\`\\("
55                         (mapconcat (function (lambda (dir)
56                                       (regexp-quote (expand-file-name dir))))
57                                    LilyPond-include-path "\\|")
58                         "\\).*\\("
59                         (mapconcat 'regexp-quote originals "\\|")
60                         "\\)\\.\\("
61                         (mapconcat 'regexp-quote extensions "\\|")
62                         "\\)\\'"))
63         (buffers (buffer-list)))
64     (while buffers
65       (let* ((buffer (car buffers))
66              (name (buffer-file-name buffer)))
67         (setq buffers (cdr buffers))
68         (if (and name (string-match regexp name))
69             (progn
70               (and (buffer-modified-p buffer)
71                    (or (not LilyPond-save-query)
72                        (y-or-n-p (concat "Save file "
73                                          (buffer-file-name buffer)
74                                          "? ")))
75                    (save-excursion (set-buffer buffer) (save-buffer)))
76               (if (file-newer-than-file-p name derived)
77                   (setq found t))))))
78     found))
79
80 (defun LilyPond-running ()
81   (let ((process (get-process "lilypond")))
82   (and process
83        (eq (process-status process) 'run))))
84
85 (defun Midi-running ()
86   (let ((process (get-process "midi")))
87   (and process
88        (eq (process-status process) 'run))))
89
90 (defun LilyPond-kill-job ()
91   "Kill the currently running LilyPond job."
92   (interactive)
93   ;; What bout TeX, Xdvi?
94   (quit-process (get-process "lilypond") t))
95
96 ;; URG, should only run LilyPond-compile for LilyPond
97 ;; not for tex,xdvi (ly2dvi?)
98 (defun LilyPond-compile-file (command name)
99   ;; We maybe should know what we run here (Lily, ly2dvi, tex)
100   ;; and adjust our error-matching regex ?
101   (compile-internal command "No more errors" name ))
102
103 ;; do we still need this, now that we're using compile-internal?
104 (defun LilyPond-save-buffer ()
105   "Save buffer and set default command for compiling."
106   (interactive)
107   (if (buffer-modified-p)
108       (progn (save-buffer)
109              (setq LilyPond-command-default "LilyPond"))))
110
111 ;;; return (dir base ext)
112 (defun split-file-name (name)
113   (let* ((i (string-match "[^/]*$" name))
114          (dir (if (> i 0) (substring name 0 i) "./"))
115          (file (substring name i (length name)))
116          (i (string-match "[^.]*$" file)))
117     (if (and
118          (> i 0)
119          (< i (length file)))
120         (list dir (substring file 0 (- i 1)) (substring file i (length file)))
121       (list dir file ""))))
122
123
124 ;; Should check whether in command-alist?
125 (defcustom LilyPond-command-default "LilyPond"
126   "Default command. Must identify a member of LilyPond-command-alist."
127
128   :group 'LilyPond
129   :type 'string)
130 ;;;(make-variable-buffer-local 'LilyPond-command-last)
131
132 (defvar LilyPond-command-current 'LilyPond-command-master)
133 ;;;(make-variable-buffer-local 'LilyPond-command-master)
134
135
136 ;; If non-nil, LilyPond-command-query will return the value of this
137 ;; variable instead of quering the user. 
138 (defvar LilyPond-command-force nil)
139
140 (defcustom LilyPond-xdvi-command "xdvi"
141   "Command used to display DVI files."
142
143   :group 'LilyPond
144   :type 'string)
145
146 (defcustom LilyPond-gv-command "gv -watch"
147   "Command used to display PS files."
148
149   :group 'LilyPond
150   :type 'string)
151
152 (defcustom LilyPond-midi-command "timidity"
153   "Command used to play MIDI files."
154
155   :group 'LilyPond
156   :type 'string)
157
158 (defcustom LilyPond-midi-all-command "timidity -ig"
159   "Command used to play MIDI files."
160
161   :group 'LilyPond
162   :type 'string)
163
164 ;; This is the major configuration variable.
165 (defcustom LilyPond-command-alist
166   `(
167     ("LilyPond" . ("lilypond %s" . "LaTeX"))
168     ("TeX" . ("tex '\\nonstopmode\\input %t'" . "View"))
169
170     ("2Dvi" . ("ly2dvi %s" . "View"))
171     ("2PS" . ("ly2dvi -P %s" . "View"))
172     ("2Midi" . ("lilypond -m %s" . "View"))
173
174     ("Book" . ("lilypond-book %x" . "LaTeX"))
175     ("LaTeX" . ("latex '\\nonstopmode\\input %l'" . "View"))
176
177     ;; point-n-click (arg: exits upop USR1)
178     ("SmartView" . ("xdvi %d" . "LilyPond"))
179     
180     ;; refreshes when kicked USR1
181     ("View" . (,(concat LilyPond-xdvi-command " %d") . "LilyPond"))
182
183     ("ViewPS" . (,(concat LilyPond-gv-command " %p") . "LilyPond"))
184
185     ("Midi" . (,(concat LilyPond-midi-command " %m") . "LilyPond"))
186     )
187
188   "AList of commands to execute on the current document.
189
190 The key is the name of the command as it will be presented to the
191 user, the value is a cons of the command string handed to the shell
192 after being expanded, and the next command to be executed upon
193 success.  The expansion is done using the information found in
194 LilyPond-expand-list.
195 "
196   :group 'LilyPond
197   :type '(repeat (cons :tag "Command Item"
198                        (string :tag "Key")
199                        (cons :tag "How"
200                         (string :tag "Command")
201                         (string :tag "Next Key")))))
202
203 ;; drop this?
204 (defcustom LilyPond-file-extensions '(".ly" ".sly" ".fly")
205   "*File extensions used by manually generated TeX files."
206   :group 'LilyPond
207   :type '(repeat (string :format "%v")))
208
209
210 (defcustom LilyPond-expand-alist 
211   '(
212     ("%s" . ".ly")
213     ("%t" . ".tex")
214     ("%d" . ".dvi")
215     ("%p" . ".ps")
216     ("%l" . ".latex")
217     ("%x" . ".tely")
218     ("%m" . ".midi")
219     )
220     
221   "Alist of expansion strings for LilyPond command names."
222   :group 'LilyPond
223   :type '(repeat (cons :tag "Alist item"
224                   (string :tag "Symbol")
225                   (string :tag "Expansion")))) 
226
227
228 (defcustom LilyPond-command-Show "View"
229   "*The default command to show (view or print) a LilyPond file.
230 Must be the car of an entry in `LilyPond-command-alist'."
231   :group 'LilyPond
232   :type 'string)
233   (make-variable-buffer-local 'LilyPond-command-Show)
234
235 (defcustom LilyPond-command-Print "Print"
236   "The name of the Print entry in LilyPond-command-Print."
237   :group 'LilyPond
238   :type 'string)
239
240 (defun xLilyPond-compile-sentinel (process msg)
241   (if (and process
242            (= 0 (process-exit-status process)))
243       (setq LilyPond-command-default
244               (cddr (assoc LilyPond-command-default LilyPond-command-alist)))))
245
246 ;; FIXME: shouldn't do this for stray View/xdvi
247 (defun LilyPond-compile-sentinel (buffer msg)
248   (if (string-match "^finished" msg)
249       (setq LilyPond-command-default
250             (cddr (assoc LilyPond-command-default LilyPond-command-alist)))))
251
252 ;;(make-variable-buffer-local 'compilation-finish-function)
253 (setq compilation-finish-function 'LilyPond-compile-sentinel)
254
255 (defun LilyPond-command-query (name)
256   "Query the user for what LilyPond command to use."
257   (let* ((default (cond ((if (string-equal name "emacs-lily")
258                              (LilyPond-check-files (concat name ".tex")
259                                                    (list name)
260                                                    LilyPond-file-extensions)
261                            (if (buffer-modified-p) 
262                                (if (y-or-n-p "Save buffer before next command? ")
263                                    (LilyPond-save-buffer)))
264                            ;;"LilyPond"
265                            LilyPond-command-default))
266                         (t LilyPond-command-default)))
267
268          (completion-ignore-case t)
269          
270          (answer (or LilyPond-command-force
271                      (completing-read
272                       (concat "Command: (default " default ") ")
273                       LilyPond-command-alist nil t nil 'LilyPond-command-history))))
274
275     ;; If the answer is "LilyPond" it will not be expanded to "LilyPond"
276     (let ((answer (car-safe (assoc answer LilyPond-command-alist))))
277       (if (and answer
278                (not (string-equal answer "")))
279           answer
280         default))))
281
282
283 ;; FIXME: find ``\score'' in buffers / make settable?
284 (defun LilyPond-master-file ()
285   ;; duh
286   (buffer-file-name))
287
288 (defun LilyPond-command-master ()
289   "Run command on the current document."
290   (interactive)
291   (LilyPond-command (LilyPond-command-query (LilyPond-master-file))
292                     'LilyPond-master-file))
293
294 (defun LilyPond-command-lilypond ()
295   "Run lilypond for the current document."
296   (interactive)
297   (LilyPond-command (LilyPond-command-menu "LilyPond") 'LilyPond-master-file)
298 )
299
300 (defun LilyPond-command-formatdvi ()
301   "Format the dvi output of the current document."
302   (interactive)
303   (LilyPond-command (LilyPond-command-menu "2Dvi") 'LilyPond-master-file)
304 )
305
306 (defun LilyPond-command-formatps ()
307   "Format the ps output of the current document."
308   (interactive)
309   (LilyPond-command (LilyPond-command-menu "2PS") 'LilyPond-master-file)
310 )
311
312 (defun LilyPond-command-smartview ()
313   "View the dvi output of current document."
314   (interactive)
315   (LilyPond-command (LilyPond-command-menu "SmartView") 'LilyPond-master-file)
316 )
317
318 (defun LilyPond-command-view ()
319   "View the dvi output of current document."
320   (interactive)
321   (LilyPond-command (LilyPond-command-menu "View") 'LilyPond-master-file)
322 )
323
324 (defun LilyPond-command-viewps ()
325   "View the ps output of current document."
326   (interactive)
327   (LilyPond-command (LilyPond-command-menu "ViewPS") 'LilyPond-master-file)
328 )
329
330 (defun LilyPond-command-midi ()
331   "Play midi corresponding to the current document."
332   (interactive)
333   (LilyPond-command (LilyPond-command-menu "Midi") 'LilyPond-master-file)
334 )
335
336 (defun count-rexp (start end rexp)
337   "Print number of found regular expressions in the region."
338   (interactive "r")
339   (save-excursion
340     (save-restriction
341       (narrow-to-region start end)
342       (goto-char (point-min))
343       (count-matches rexp))))
344
345 (defun count-midi-words ()
346   "Print number of scores before the curser."
347   (interactive)
348   (count-rexp (point-min) (point-max) "\\\\midi"))
349  
350 (defun count-midi-words-backwards ()
351   "Print number of scores before the curser."
352   (interactive)
353   (count-rexp (point-min) (point) "\\\\midi"))
354  
355 (defun LilyPond-command-next-midi ()
356   "Play next midi score of the current document."
357   (interactive)
358   (if (Midi-running)
359       (quit-process (get-process "midi") t)
360     (LilyPond-compile-file 
361      (let ((fname (LilyPond-master-file))
362            (allcount (string-to-number (substring (count-midi-words) 0 -12)))
363            (count (string-to-number (substring (count-midi-words-backwards) 0 -12))))
364        (concat  LilyPond-midi-command " "
365                 (substring fname 0 -3) ; suppose ".ly"
366                 (if (and (> allcount 1) (> count 0)) ; not first score
367                     (if (eq count allcount)          ; last score
368                         (concat "-" (number-to-string (+ count -1)))
369                       (concat "-" (number-to-string count))))
370                 ".midi"))
371      "Midi")))
372
373 (defun LilyPond-command-all-midi ()
374   "Play next midi score of the current document."
375   (interactive)
376   (if (Midi-running)
377       (quit-process (get-process "midi") t)
378     (LilyPond-compile-file 
379      (let ((fname (LilyPond-master-file))
380            (allcount (string-to-number (substring (count-midi-words) 0 -12))))
381        (concat  LilyPond-midi-all-command " "
382                 (if (> allcount 0) ; at least one midi-score
383                     (concat (substring fname 0 -3) ".midi "))
384                 (if (> allcount 1) ; more than one midi-score
385                     (concat (substring fname 0 -3) "-?.midi "))
386                 (if (> allcount 9) ; etc.
387                     (concat (substring fname 0 -3) "-??.midi"))
388                 (if (> allcount 99) ; not first score
389                     (concat (substring fname 0 -3) "-???.midi"))))
390      "Midi")))
391
392 (defun LilyPond-un-comment-region (start end level)
393   "Remove up to LEVEL comment characters from each line in the region."
394   (interactive "*r\np") 
395   (comment-region start end (- level)))
396
397 ;; FIXME, this is broken
398 (defun LilyPond-region-file (begin end)
399   (let (
400         ;; (dir "/tmp/")
401         ;; urg
402         (dir "./")
403         (base "emacs-lily")
404         ;; Hmm
405         (ext (if (string-match "^[\\]score" (buffer-substring begin end))
406                  ".ly"
407                (if (< 50 (abs (- begin end)))
408                    ".fly"
409                  ".sly"))))
410     (concat dir base ext)))
411
412 (defun LilyPond-command-region (begin end)
413   "Run LilyPond on the current region."
414   (interactive "r")
415   (write-region begin end (LilyPond-region-file begin end) nil 'nomsg)
416   (LilyPond-command (LilyPond-command-query
417                      (LilyPond-region-file begin end))
418                     '(lambda () (LilyPond-region-file begin end))))
419
420 (defun LilyPond-command-buffer ()
421   "Run LilyPond on buffer."
422   (interactive)
423   (LilyPond-command-region (point-min) (point-max)))
424
425 (defun LilyPond-command-expand (string file)
426   (let ((case-fold-search nil))
427     (if (string-match "%" string)
428         (let* ((b (match-beginning 0))
429                (e (+ b 2))
430                (l (split-file-name file))
431                (dir (car l))
432                (base (cadr l)))
433           (LilyPond-command-expand
434            (concat (substring string 0 b)
435                    dir
436                    base
437                    (let ((entry (assoc (substring string b e)
438                                        LilyPond-expand-alist)))
439                      (if entry (cdr entry) ""))
440                    (substring string e))
441            file))
442       string)))
443
444 (defun LilyPond-shell-process (name buffer command)
445   (let ((old (current-buffer)))
446     (switch-to-buffer-other-window buffer)
447     ;; If we empty the buffer don't see messages scroll by.
448     ;; (erase-buffer)
449     
450     (start-process-shell-command name buffer command)
451     (switch-to-buffer-other-window old)))
452   
453
454 (defun LilyPond-command (name file)
455   "Run command NAME on the file you get by calling FILE.
456
457 FILE is a function return a file name.  It has one optional argument,
458 the extension to use on the file.
459
460 Use the information in LilyPond-command-alist to determine how to run the
461 command."
462   
463   (let ((entry (assoc name LilyPond-command-alist)))
464     (if entry
465         (let ((command (LilyPond-command-expand (cadr entry)
466                                                 (apply file nil))))
467           (if (string-equal name "View")
468               (let ((buffer-xdvi (get-buffer-create "*view*")))
469                 (if LilyPond-kick-xdvi
470                   (let ((process-xdvi (get-buffer-process buffer-xdvi)))
471                     (if process-xdvi
472                         (signal-process (process-id process-xdvi) 'SIGUSR1)
473                       (LilyPond-shell-process name buffer-xdvi command)))
474                   (LilyPond-shell-process name buffer-xdvi command)))
475             (progn
476               (setq LilyPond-command-default name)
477               (LilyPond-compile-file command name)))))))
478           
479 ;; XEmacs stuff
480 ;; Sadly we need this for a macro in Emacs 19.
481 (eval-when-compile
482   ;; Imenu isn't used in XEmacs, so just ignore load errors.
483   (condition-case ()
484       (require 'imenu)
485     (error nil)))
486
487
488 ;;; Keymap
489
490 (defvar LilyPond-mode-map ()
491   "Keymap used in `LilyPond-mode' buffers.")
492
493 ;; Note:  if you make changes to the map, you must do
494 ;;    M-x set-variable LilyPond-mode-map nil
495 ;;    M-x eval-buffer
496 ;;    M-x LilyPond-mode
497 ;; to let the changest take effect
498
499 (if LilyPond-mode-map
500     ()
501   (setq LilyPond-mode-map (make-sparse-keymap))
502   (define-key LilyPond-mode-map "\C-c\C-l" 'LilyPond-command-lilypond)
503   (define-key LilyPond-mode-map "\C-c\C-r" 'LilyPond-command-region)
504   (define-key LilyPond-mode-map "\C-c\C-b" 'LilyPond-command-buffer)
505   (define-key LilyPond-mode-map "\C-c\C-k" 'LilyPond-kill-job)
506   (define-key LilyPond-mode-map "\C-c\C-c" 'LilyPond-command-master)
507   (define-key LilyPond-mode-map "\C-c\C-d" 'LilyPond-command-formatdvi)
508   (define-key LilyPond-mode-map "\C-c\C-f" 'LilyPond-command-formatps)
509   (define-key LilyPond-mode-map "\C-c\C-s" 'LilyPond-command-smartview)
510   (define-key LilyPond-mode-map "\C-c\C-v" 'LilyPond-command-view)
511   (define-key LilyPond-mode-map "\C-c\C-p" 'LilyPond-command-viewps)
512   (define-key LilyPond-mode-map "\C-c\C-m" 'LilyPond-command-next-midi)
513   (define-key LilyPond-mode-map "\C-x\C-s" 'LilyPond-save-buffer)
514   (define-key LilyPond-mode-map "\C-cf" 'font-lock-fontify-buffer)
515   (define-key LilyPond-mode-map "\C-ci" 'LilyPond-quick-note-insert)
516   (define-key LilyPond-mode-map "\C-cn" 'LilyPond-insert-tag-notes)
517   (define-key LilyPond-mode-map "\C-cs" 'LilyPond-insert-tag-score)
518   (define-key LilyPond-mode-map "\C-c:" 'LilyPond-un-comment-region)
519   (define-key LilyPond-mode-map "\C-c;" 'comment-region)
520   (define-key LilyPond-mode-map ")" 'LilyPond-electric-close-paren)
521   (define-key LilyPond-mode-map ">" 'LilyPond-electric-close-paren)
522   (define-key LilyPond-mode-map "}" 'LilyPond-electric-close-paren)
523   )
524
525 ;;; Menu Support
526
527 (defun LilyPond-quick-note-insert()
528   "Insert notes with fewer key strokes by using a simple keyboard piano."
529   (interactive)
530   (setq dutch-notes
531         '(("k" "a") ("l" "b") ("a" "c") ("s" "d") 
532           ("d" "e") ("f" "f") ("j" "g") ("r" "r")))
533   (setq dutch-note-ends '("eses" "es" "" "is" "isis"))
534   (setq dutch-note-replacements '("" ""))
535   (setq finnish-note-replacements
536         '(("eeses" "eses") ("ees" "es") ("aeses" "asas") ("aes" "as") ("b" "h")
537           ("beses" "heses") ("bes" "b") ("bis" "his") ("bisis" "hisis")))
538                               ; add more translations of the note names
539   (setq spanish-note-replacements
540         '(("c" "do") ("d" "re") ("e" "mi") ("f" "fa") ("g" "sol") ("a" "la") ("b" "si")
541       ("cis" "dos") ("cisis" "doss") ("ces" "dob") ("ceses" "dobb")
542       ("dis" "res") ("disis" "ress") ("des" "reb") ("deses" "rebb")
543       ("eis" "mis") ("eisis" "miss") ("ees" "mib") ("eeses" "mibb")
544       ("fis" "fas") ("fisis" "fass") ("fes" "fab") ("feses" "fabb")
545       ("gis" "sols") ("gisis" "solss") ("ges" "solb") ("geses" "solbb")
546       ("ais" "las") ("aisis" "lass") ("aes" "lab") ("aeses" "labb")
547       ("bis" "sis") ("bisis" "siss") ("bes" "sib") ("beses" "sibb")))
548   (setq other-keys "()<>~}")
549   (setq accid 0) (setq octav 0) (setq durat "") (setq dots 0)
550
551   (message "Press h for help.") (sit-for 0 750 1)
552
553   (setq note-replacements dutch-note-replacements)
554   (while (not (= 27 ; esc to quit
555     (setq x (read-char-exclusive 
556              (format " | a[_]s[_]d | f[_]j[_]k[_]l | r with ie ,' 12345678 . 0 (<~>)/}\\b\\n Esc \n | c | d | e | f | g | a | %s | r with %s%s%s%s"
557                      (if (string= (car(cdr(assoc "b" note-replacements))) "h")
558                          "h" "b")
559                      (nth (+ accid 2) dutch-note-ends)
560                      (make-string (abs octav) (if (> octav 0) ?' ?,)) 
561                      durat 
562                      (if (string= durat "") "" (make-string dots ?.)))))))
563 ;    (insert (number-to-string x)) ; test numbers for characters
564     (setq note (cdr (assoc (char-to-string x) dutch-notes)))
565     (cond
566      ((= x 127) (backward-kill-word 1)) ; backspace
567      ((= x 13) (progn (insert "\n") (LilyPond-indent-line)))) ; return
568     (setq x (char-to-string x))
569     (cond
570      ((and (string< x "9") (string< "0" x))
571       (progn (setq durat (int-to-string (expt 2 (- (string-to-int x) 1))))
572              (setq dots 0)))
573      ((string= x " ") (insert " "))
574      ((string= x "/") (progn (insert "\\times ")
575                              (while (not (and (string< x "9") (string< "0" x)))
576                                (setq x (char-to-string (read-char-exclusive "Insert a number for the denominator (\"x/\")"))))
577                              (insert (format "%s/" x)) (setq x "/")
578                              (while (not (and (string< x "9") (string< "0" x)))
579                                (setq x (char-to-string (read-char-exclusive "Insert a number for the numerator (\"/y\")"))))
580                              (insert (format "%s { " x))))
581      ((string= x "0") (progn (setq accid 0) (setq octav 0) 
582                              (setq durat "") (setq dots 0)))
583      ((string= x "i") (setq accid (if (= accid 2) 0 (max (+ accid 1) 1))))
584      ((string= x "e") (setq accid (if (= accid -2) 0 (min (+ accid -1) -1))))
585      ((string= x "'") (setq octav (if (= octav 4) 0 (max (+ octav 1) 1))))
586      ((string= x ",") (setq octav (if (= octav -4) 0 (min (+ octav -1) -1))))
587      ((string= x ".") (setq dots (if (= dots 4) 0 (+ dots 1))))
588      ((not (null (member x (split-string other-keys ""))))
589       (insert (format "%s " x)))
590      ((not (null note))
591       (progn
592         (setq note 
593               (format "%s%s" (car note) (if (string= "r" (car note)) "" 
594                                           (nth (+ accid 2) dutch-note-ends))))
595         (setq notetwo (car (cdr (assoc note note-replacements))))
596         (if (not (null notetwo)) (setq note notetwo))
597         (insert
598          (format "%s%s%s%s " 
599                  note
600                  (if (string= "r" note) ""
601                      (make-string (abs octav) (if (> octav 0) ?' ?,)))
602                  durat
603                  (if (string= durat "") "" (make-string dots ?.))))
604         (setq accid 0) (setq octav 0) (setq durat "") (setq dots 0)))
605      ((string= x "t") (progn (setq note-replacements dutch-note-replacements)
606                              (message "Selected Dutch notes") 
607                              (sit-for 0 750 1))) ; t
608      ((string= x "n") (progn (setq note-replacements finnish-note-replacements)
609                              (message "Selected Finnish/Deutsch notes") 
610                              (sit-for 0 750 1))) ; n
611                               ; add more translations of the note names
612      ((string= x "p") (progn (setq note-replacements spanish-note-replacements)
613                              (message "Selected Spanish notes") 
614                              (sit-for 0 750 1))) ; p
615      ((string= x "h") 
616       (progn (message "Insert notes with fewer key strokes. For example \"i,5.f\" produces \"fis,32. \".") (sit-for 5 0 1) 
617              (message "Add also \"a ~ a\"-ties, \"a ( ) b\"-slurs and \"< a b >\"-chords.") (sit-for 5 0 1) 
618              (message "Note names are in Du(t)ch by default. Hit 'n' for Fin(n)ish/Deutsch note names. Hit 'p' for S(p)anish note names") (sit-for 5 0 1) 
619              (message "Backspace deletes last note, return starts a new indented line and Esc quits.") (sit-for 5 0 1) 
620              (message "Insert note triplets \"\\times 2/3 { a b } \" by typing \"/23ab}\".") (sit-for 5 0 1) 
621              (message "Remember to add all other details as well.") (sit-for 5 0 1)))
622     )))
623
624 (define-skeleton LilyPond-insert-tag-notes
625   "LilyPond notes tag."
626   nil
627 ;  (if (bolp) nil ?\n)
628   "\\notes"
629   (if (y-or-n-p "Set \"\\relative\" attribute? ")
630       (concat " \\relative " (skeleton-read "Relative: " "" str)))
631   " { " _ " }")
632
633 (define-skeleton LilyPond-insert-tag-score
634   "LilyPond score tag."
635   nil
636   (if (bolp) nil ?\n)
637   "\\score {\n"
638   "   " _ "\n"
639   "   \\paper {  }\n"
640   (if (y-or-n-p "Insert \"\\header\" field? ")
641       (concat "   \\header {\n      " 
642               (skeleton-read "Piece: " "piece = " str) "\n"
643               (if (y-or-n-p "Insert \"opus\" field? ")
644                   (concat "      " (skeleton-read "Opus: " "opus = " str) "\n"))
645               "   }\n"))
646   (if (y-or-n-p "Insert \"\\midi\" field? ")
647       (concat "   \\midi { " 
648               (skeleton-read "Midi: " "\\tempo 4 = " str)  
649               " }\n"))
650   "}\n")
651
652 (defun LilyPond-command-menu-entry (entry)
653   ;; Return LilyPond-command-alist ENTRY as a menu item.
654   (let ((name (car entry)))
655     (cond ((and (string-equal name LilyPond-command-Print)
656                 LilyPond-printer-list)
657            (let ((command LilyPond-print-command)
658                  (lookup 1))
659              (append (list LilyPond-command-Print)
660                      (mapcar 'LilyPond-command-menu-printer-entry
661                              LilyPond-printer-list))))
662           (t
663            (vector name (list 'LilyPond-command-menu name) t)))))
664
665
666 (easy-menu-define LilyPond-command-menu
667     LilyPond-mode-map
668     "Menu used in LilyPond mode."
669   (append '("Command")
670           '(("Command on"
671              [ "Master File" LilyPond-command-select-master
672                :keys "C-c C-c" :style radio
673                :selected (eq LilyPond-command-current 'LilyPond-command-master) ]
674              [ "Buffer" LilyPond-command-select-buffer
675                :keys "C-c C-b" :style radio
676                :selected (eq LilyPond-command-current 'LilyPond-command-buffer) ]
677              [ "Region" LilyPond-command-select-region
678                :keys "C-c C-r" :style radio
679                :selected (eq LilyPond-command-current 'LilyPond-command-region) ]))
680 ;         (let ((file 'LilyPond-command-on-current))
681 ;           (mapcar 'LilyPond-command-menu-entry LilyPond-command-alist))
682 ;;; Some kind of mapping which includes :keys might be more elegant
683           '([ "LilyPond" (LilyPond-command (LilyPond-command-menu "LilyPond") 'LilyPond-master-file) :keys "C-c C-l"])
684           '([ "TeX" (LilyPond-command (LilyPond-command-menu "TeX") 'LilyPond-master-file) ])
685           '([ "2Dvi" (LilyPond-command (LilyPond-command-menu "2Dvi") 'LilyPond-master-file) :keys "C-c C-d"])
686           '([ "2PS" (LilyPond-command (LilyPond-command-menu "2PS") 'LilyPond-master-file) :keys "C-c C-f"])
687           '([ "2Midi" (LilyPond-command (LilyPond-command-menu "2Midi") 'LilyPond-master-file)])
688           '([ "Book" (LilyPond-command (LilyPond-command-menu "Book") 'LilyPond-master-file) ])
689           '([ "LaTeX" (LilyPond-command (LilyPond-command-menu "LaTeX") 'LilyPond-master-file) ])
690           '([ "SmartView" (LilyPond-command (LilyPond-command-menu "SmartView") 'LilyPond-master-file) :keys "C-c C-s"])
691           '([ "View" (LilyPond-command (LilyPond-command-menu "View") 'LilyPond-master-file) :keys "C-c C-v"])
692           '([ "ViewPS" (LilyPond-command (LilyPond-command-menu "ViewPS") 'LilyPond-master-file) :keys "C-c C-p"])
693           '([ "Midi (off)" (LilyPond-command-next-midi) :keys "C-c C-m"])
694           '([ "Midi all" (LilyPond-command-all-midi)])
695           ))
696
697 (easy-menu-define LilyPond-mode-menu
698   LilyPond-mode-map
699   "Menu used in LilyPond mode."
700   (append '("LilyPond")
701           '(("Insert"
702              [ "\\notes..."  LilyPond-insert-tag-notes t]
703              [ "\\score..."  LilyPond-insert-tag-score t]
704              ["Quick Notes"  LilyPond-quick-note-insert t]
705              ))
706           '(("Miscellaneous"
707              ["Uncomment Region" LilyPond-un-comment-region t]
708              ["Comment Region" comment-region t]
709              ["Refontify buffer" font-lock-fontify-buffer t]
710              ))
711           ))
712
713 (defconst LilyPond-imenu-generic-re "^\\([a-zA-Z]+\\) *="
714   "Regexp matching Identifier definitions.")
715
716 (defvar LilyPond-imenu-generic-expression
717   (list (list nil LilyPond-imenu-generic-re 1))
718   "Expression for imenu")
719
720 (defun LilyPond-command-select-master ()
721   (interactive)
722   (message "Next command will be on the master file")
723   (setq LilyPond-command-current 'LilyPond-command-master))
724
725 (defun LilyPond-command-select-buffer ()
726   (interactive)
727   (message "Next command will be on the buffer")
728   (setq LilyPond-command-current 'LilyPond-command-buffer))
729
730 (defun LilyPond-command-select-region ()
731   (interactive)
732   (message "Next command will be on the region")
733   (setq LilyPond-command-current 'LilyPond-command-region))
734
735 (defun LilyPond-command-menu (name)
736   ;; Execute LilyPond-command-alist NAME from a menu.
737   (let ((LilyPond-command-force name))
738     (funcall LilyPond-command-current)))
739
740 (defun LilyPond-mode ()
741   "Major mode for editing LilyPond music files.
742
743 This mode knows about LilyPond keywords and line comments, not about
744 indentation or block comments.  It features easy compilation, error
745 finding and viewing of a LilyPond source buffer or region.
746
747 COMMANDS
748 \\{LilyPond-mode-map}
749 VARIABLES
750
751 LilyPond-command-alist\t\talist from name to command
752 LilyPond-xdvi-command\t\tcommand to display dvi files -- bit superfluous"
753   (interactive)
754   ;; set up local variables
755   (kill-all-local-variables)
756
757   (make-local-variable 'font-lock-defaults)
758   (setq font-lock-defaults '(LilyPond-font-lock-keywords))
759
760   ;; string and comments are fontified explicitly
761   (make-local-variable 'font-lock-keywords-only)
762   (setq font-lock-keywords-only t)
763
764   ;; Multi-line font-locking needs Emacs 21.1 or newer.
765   ;; For older versions there is hotkey "C-c f".
766   (make-local-variable 'font-lock-multiline) 
767   (setq font-lock-multiline t) 
768
769   (make-local-variable 'paragraph-separate)
770   (setq paragraph-separate "^[ \t]*$")
771
772   (make-local-variable 'paragraph-start)
773   (setq paragraph-start "^[ \t]*$")
774
775   (make-local-variable 'comment-start)
776   (setq comment-start "%")
777
778   (make-local-variable 'comment-start-skip)
779   (setq comment-start-skip "%{? *")
780
781   (make-local-variable 'comment-end)
782   (setq comment-end "")
783
784   (make-local-variable 'block-comment-start)
785   (setq block-comment-start "%{")
786
787   (make-local-variable 'block-comment-end)  
788   (setq block-comment-end   "%}")
789
790   (make-local-variable 'indent-line-function)
791   (setq indent-line-function 'LilyPond-indent-line)
792
793     (set-syntax-table LilyPond-mode-syntax-table)
794   (setq major-mode 'LilyPond-mode)
795   (setq mode-name "LilyPond")
796   (setq local-abbrev-table LilyPond-mode-abbrev-table)
797   (use-local-map LilyPond-mode-map)
798
799   (make-local-variable 'imenu-generic-expression)
800   (setq imenu-generic-expression LilyPond-imenu-generic-expression)
801   (imenu-add-to-menubar "Index")
802
803     ;; run the mode hook. LilyPond-mode-hook use is deprecated
804   (run-hooks 'LilyPond-mode-hook))
805
806 (defun LilyPond-version ()
807   "Echo the current version of `LilyPond-mode' in the minibuffer."
808   (interactive)
809   (message "Using `LilyPond-mode' version %s" LilyPond-version))
810
811 (provide 'lilypond-mode)
812 ;;; lilypond-mode.el ends here
813