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