-;;;
-;;; lilypond-mode.el --- Major mode for editing GNU LilyPond music scores
-;;;
-;;; source file of the GNU LilyPond music typesetter
-;;;
-;;; (c) 1999--2004 Jan Nieuwenhuizen <janneke@gnu.org>
-;;;
-;;; Changed 2001--2003 Heikki Junes <heikki.junes@hut.fi>
-;;; * Add PS-compilation, PS-viewing and MIDI-play (29th Aug 2001)
-;;; * Keyboard shortcuts (12th Sep 2001)
-;;; * Inserting tags, inspired on sgml-mode (11th Oct 2001)
-;;; * Autocompletion & Info (23rd Nov 2002)
+;;;;
+;;;; lilypond-mode.el --- Major mode for editing GNU LilyPond music scores
+;;;;
+;;;; source file of the GNU LilyPond music typesetter
+;;;;
+;;;; (c) 1999--2009 Jan Nieuwenhuizen <janneke@gnu.org>
+;;;;
+;;;; Changed 2001--2003 Heikki Junes <heikki.junes@hut.fi>
+;;;; * Add PS-compilation, PS-viewing and MIDI-play (29th Aug 2001)
+;;;; * Keyboard shortcuts (12th Sep 2001)
+;;;; * Inserting tags, inspired on sgml-mode (11th Oct 2001)
+;;;; * Autocompletion & Info (23rd Nov 2002)
;;; Inspired on auctex
(require 'easymenu)
(require 'compile)
-(defconst LilyPond-version "1.9.9"
+(defconst LilyPond-version "2.5.20"
"`LilyPond-mode' version number.")
(defconst LilyPond-help-address "bug-lilypond@gnu.org"
(defvar LilyPond-region-file-prefix "emacs-lily"
"File prefix for commands on buffer or region.")
+(defvar LilyPond-master-file nil
+ "Master file that Lilypond will be run on.")
+
;; FIXME: find ``\score'' in buffers / make settable?
-(defun LilyPond-master-file ()
- ;; duh
- (buffer-file-name))
+(defun LilyPond-get-master-file ()
+ (or LilyPond-master-file
+ (buffer-file-name)))
(defvar LilyPond-kick-xdvi nil
"If true, no simultaneous xdvi's are started, but reload signal is sent.")
Finds file lilypond-words.el from load-path."
(let ((fn nil)
(lp load-path)
- (words-file "lilypond.words.el"))
+ (words-file "lilypond-words.el"))
(while (and (> (length lp) 0) (not fn))
(setq fn (concat (car lp) "/" words-file))
(if (not (file-readable-p fn))
(progn (setq fn nil) (setq lp (cdr lp)))))
(if (not fn)
- (progn (message "Warning: `lilypond.words.el' not found in `load-path'. See `lilypond-init.el'.")
+ (progn (message "Warning: `lilypond-words.el' not found in `load-path'. See `lilypond-init.el'.")
(sit-for 5 0)))
fn))
(defun LilyPond-running ()
"Check the currently running LilyPond compiling jobs."
- (let ((process-names (list "lilypond" "tex" "2dvi" "2ps" "2midi"
+ (let ((process-names (list "lilypond" "tex" "2ps" "2midi"
"book" "latex"))
(running nil))
(while (setq process-name (pop process-names))
(interactive)
(if (buffer-modified-p)
(progn (save-buffer)
- (setq LilyPond-command-default "LilyPond"))))
+ (setq LilyPond-command-next LilyPond-command-default))))
;;; return (dir base ext)
(defun split-file-name (name)
:type 'string)
;;;(make-variable-buffer-local 'LilyPond-command-last)
+(defvar LilyPond-command-next LilyPond-command-default)
+
(defvar LilyPond-command-current 'LilyPond-command-master)
;;;(make-variable-buffer-local 'LilyPond-command-master)
;; variable instead of quering the user.
(defvar LilyPond-command-force nil)
-(defcustom LilyPond-xdvi-command "xdvi"
- "Command used to display DVI files."
-
+(defcustom LilyPond-lilypond-command "lilypond"
+ "Command used to compile LY files."
:group 'LilyPond
:type 'string)
-(defcustom LilyPond-gv-command "gv -watch"
+(defcustom LilyPond-ps-command "gv --watch"
"Command used to display PS files."
:group 'LilyPond
:type 'string)
+(defcustom LilyPond-pdf-command "xpdf"
+ "Command used to display PDF files."
+
+ :group 'LilyPond
+ :type 'string)
+
(defcustom LilyPond-midi-command "timidity"
"Command used to play MIDI files."
(defun LilyPond-command-current-midi ()
"Play midi corresponding to the current document."
(interactive)
- (LilyPond-command (LilyPond-command-menu "Midi") 'LilyPond-master-file))
+ (LilyPond-command (LilyPond-command-menu "Midi") 'LilyPond-get-master-file))
(defun LilyPond-command-all-midi ()
"Play midi corresponding to the current document."
(interactive)
- (LilyPond-command (LilyPond-command-menu "MidiAll") 'LilyPond-master-file))
-
+ (LilyPond-command (LilyPond-command-menu "MidiAll") 'LilyPond-get-master-file))
+
+(defun count-matches-as-number (re)
+ "Count-matches in emacs 22 backwards-incompatibly returns a number"
+ (let ((result (count-matches re)))
+ (if (stringp result)
+ (string-to-number result)
+ result)))
+
(defun count-rexp (start end rexp)
"Print number of found regular expressions in the region."
(interactive "r")
(save-restriction
(narrow-to-region start end)
(goto-char (point-min))
- (count-matches rexp))))
+ (count-matches-as-number rexp))))
(defun count-midi-words ()
"Check number of midi-scores before the curser."
(defun LilyPond-string-current-midi ()
"Check the midi file of the following midi-score in the current document."
(let ((fnameprefix (if (eq LilyPond-command-current 'LilyPond-command-master)
- (substring (LilyPond-master-file) 0 -3); suppose ".ly"
+ (substring (LilyPond-get-master-file) 0 -3); suppose ".ly"
LilyPond-region-file-prefix))
- (allcount (string-to-number (substring (count-midi-words) 0 -12)))
- (count (string-to-number (substring (count-midi-words-backwards) 0 -12))))
+ (allcount (count-midi-words))
+ (count (count-midi-words-backwards)))
(concat fnameprefix
(if (and (> allcount 1) (> count 0)) ; not first score
(if (eq count allcount) ; last score
(defun LilyPond-string-all-midi ()
"Return the midi files of the current document in ascending order."
(let ((fnameprefix (if (eq LilyPond-command-current 'LilyPond-command-master)
- (substring (LilyPond-master-file) 0 -3); suppose ".ly"
+ (substring (LilyPond-get-master-file) 0 -3); suppose ".ly"
LilyPond-region-file-prefix))
- (allcount (string-to-number (substring (count-midi-words) 0 -12))))
+ (allcount (count-midi-words)))
(concat (if (> allcount 0) ; at least one midi-score
(concat fnameprefix ".midi "))
(if (> allcount 1) ; more than one midi-score
;; Should expand this to include possible keyboard shortcuts which
;; could then be mapped to define-key and menu.
`(
- ("LilyPond" . ("lilypond-bin %s" . "LaTeX"))
- ("TeX" . ("tex '\\nonstopmode\\input %t'" . "View"))
-
- ("2Dvi" . ("lilypond %s" . "View"))
- ("2PS" . ("lilypond -P %s" . "ViewPS"))
- ("2Midi" . ("lilypond -m %s" . "View"))
+ ("LilyPond" . (,(concat LilyPond-lilypond-command " %s") "%s" "%l" "View"))
+ ("2PS" . (,(concat LilyPond-lilypond-command " -f ps %s") "%s" "%p" "ViewPS"))
+ ("2Gnome" . (,(concat LilyPond-lilypond-command " -b gnome %s")))
- ("Book" . ("lilypond-book %x" . "LaTeX"))
- ("LaTeX" . ("latex '\\nonstopmode\\input %l'" . "View"))
-
- ;; point-n-click (arg: exits upop USR1)
- ("SmartView" . ("xdvi %d" . "LilyPond"))
+ ("Book" . ("lilypond-book %x" "%x" "%l" "LaTeX"))
+ ("LaTeX" . ("latex '\\nonstopmode\\input %l'" "%l" "%d" "ViewDVI"))
;; refreshes when kicked USR1
- ("View" . (,(concat LilyPond-xdvi-command " %d") . "LilyPond"))
- ("ViewPS" . (,(concat LilyPond-gv-command " %p") . "LilyPond"))
+ ("View" . (,(concat LilyPond-pdf-command " %f")))
+ ("ViewPDF" . (,(concat LilyPond-pdf-command " %f")))
+ ("ViewPS" . (,(concat LilyPond-ps-command " %p")))
;; The following are refreshed in LilyPond-command:
;; - current-midi depends on cursor position and
- ("Midi" . (,(concat LilyPond-midi-command " " (LilyPond-string-current-midi)) . "LilyPond" )) ;
+ ("Midi" . ("")) ;
;; - all-midi depends on number of midi-score.
- ("MidiAll" . (,(concat LilyPond-all-midi-command " " (LilyPond-string-all-midi)) . "LilyPond"))
+ ("MidiAll" . (""))
)
"AList of commands to execute on the current document.
("%s" . ".ly")
("%t" . ".tex")
("%d" . ".dvi")
+ ("%f" . ".pdf")
("%p" . ".ps")
("%l" . ".tex")
("%x" . ".tely")
:group 'LilyPond
:type 'string)
-(defun xLilyPond-compile-sentinel (process msg)
- (if (and process
- (= 0 (process-exit-status process)))
- (setq LilyPond-command-default
- (cddr (assoc LilyPond-command-default LilyPond-command-alist)))))
-
-;; FIXME: shouldn't do this for stray View/xdvi
-(defun LilyPond-compile-sentinel (buffer msg)
- (if (string-match "^finished" msg)
- (setq LilyPond-command-default
- (cddr (assoc LilyPond-command-default LilyPond-command-alist)))))
-
-;;(make-variable-buffer-local 'compilation-finish-function)
-(setq compilation-finish-function 'LilyPond-compile-sentinel)
+(defun LilyPond-find-required-command (command file)
+ "Find the first command in the chain that is needed to run
+ (input file is newer than the output file)"
+ (let* ((entry (cdr (assoc command LilyPond-command-alist)))
+ (next-command (nth 3 entry)))
+ (if (null next-command)
+ command
+ (let* ((src-string (nth 1 entry))
+ (input (LilyPond-command-expand src-string file))
+ (output (LilyPond-command-expand (nth 2 entry) file)))
+ (if (or (file-newer-than-file-p input output)
+ (and (equal "%s" src-string)
+ (not (equal (buffer-name) file))
+ (file-newer-than-file-p (buffer-name)
+ output)))
+ command
+ (LilyPond-find-required-command next-command file))))))
(defun LilyPond-command-query (name)
"Query the user for what LilyPond command to use."
- (let* ((default (cond ((if (string-equal name LilyPond-region-file-prefix)
- (LilyPond-check-files (concat name ".tex")
- (list name)
- (list LilyPond-file-extension))
- (if (verify-visited-file-modtime (current-buffer))
- (if (buffer-modified-p)
- (if (y-or-n-p "Save buffer before next command? ")
- (LilyPond-save-buffer)))
- (if (y-or-n-p "The command will be invoked to an already saved buffer. Revert it? ")
- (revert-buffer t t)))
- ;;"LilyPond"
- LilyPond-command-default))
- (t LilyPond-command-default)))
-
- (completion-ignore-case t)
-
+ (cond ((string-equal name LilyPond-region-file-prefix)
+ (LilyPond-check-files (concat name ".tex")
+ (list name)
+ (list LilyPond-file-extension)))
+ ((verify-visited-file-modtime (current-buffer))
+ (and (buffer-modified-p)
+ (y-or-n-p "Save buffer before next command? ")
+ (LilyPond-save-buffer)))
+ ((y-or-n-p "The command will be invoked to an already saved buffer. Revert it? ")
+ (revert-buffer t t)))
+
+ (let* ((default (LilyPond-find-required-command LilyPond-command-next name))
+ (completion-ignore-case t)
(answer (or LilyPond-command-force
(completing-read
(concat "Command: (default " default ") ")
"Run command on the current document."
(interactive)
(LilyPond-command-select-master)
- (LilyPond-command (LilyPond-command-query (LilyPond-master-file))
- 'LilyPond-master-file))
+ (LilyPond-command (LilyPond-command-query (LilyPond-get-master-file))
+ 'LilyPond-get-master-file))
(defun LilyPond-command-lilypond ()
"Run lilypond for the current document."
(interactive)
- (LilyPond-command (LilyPond-command-menu "LilyPond") 'LilyPond-master-file)
-)
-
-(defun LilyPond-command-formatdvi ()
- "Format the dvi output of the current document."
- (interactive)
- (LilyPond-command (LilyPond-command-menu "2Dvi") 'LilyPond-master-file)
+ (LilyPond-command (LilyPond-command-menu "LilyPond") 'LilyPond-get-master-file)
)
(defun LilyPond-command-formatps ()
"Format the ps output of the current document."
(interactive)
- (LilyPond-command (LilyPond-command-menu "2PS") 'LilyPond-master-file)
+ (LilyPond-command (LilyPond-command-menu "2PS") 'LilyPond-get-master-file)
)
+(defun LilyPond-command-formatgnome ()
+ "Format the gnome output of the current document."
+ (interactive)
+ (LilyPond-command (LilyPond-command-menu "2Gnome") 'LilyPond-get-master-file))
+
(defun LilyPond-command-formatmidi ()
"Format the midi output of the current document."
(interactive)
- (LilyPond-command (LilyPond-command-menu "2Midi") 'LilyPond-master-file)
-)
+ (LilyPond-command (LilyPond-command-menu "2Midi") 'LilyPond-get-master-file))
-(defun LilyPond-command-smartview ()
- "View the dvi output of current document."
+(defun LilyPond-command-view ()
+ "View the output of current document."
(interactive)
- (LilyPond-command (LilyPond-command-menu "SmartView") 'LilyPond-master-file)
-)
+ (LilyPond-command-viewpdf))
-(defun LilyPond-command-view ()
- "View the dvi output of current document."
+(defun LilyPond-command-viewpdf ()
+ "View the ps output of current document."
(interactive)
- (LilyPond-command (LilyPond-command-menu "View") 'LilyPond-master-file)
-)
+ (LilyPond-command (LilyPond-command-menu "ViewPDF") 'LilyPond-get-master-file))
(defun LilyPond-command-viewps ()
"View the ps output of current document."
(interactive)
- (LilyPond-command (LilyPond-command-menu "ViewPS") 'LilyPond-master-file)
-)
+ (LilyPond-command (LilyPond-command-menu "ViewPS") 'LilyPond-get-master-file))
;; FIXME, this is broken
(defun LilyPond-region-file (begin end)
(base (cadr l)))
(LilyPond-command-expand
(concat (substring string 0 b)
- dir
- base
+ (shell-quote-argument (concat dir base))
(let ((entry (assoc (substring string b e)
LilyPond-expand-alist)))
(if entry (cdr entry) ""))
Use the information in LilyPond-command-alist to determine how to run the
command."
-
+
(let ((entry (assoc name LilyPond-command-alist)))
(if entry
(let ((command (LilyPond-command-expand (cadr entry)
(if (member name (list "View" "ViewPS"))
;; is USR1 a right signal for viewps?
(let ((buffer-xdvi (get-buffer-create (concat "*" name "*"))))
+ ;; what if XEDITOR is set to gedit or so, should we steal it?
+ (if (not (getenv "XEDITOR"))
+ (setenv "XEDITOR" "emacsclient --no-wait +%l:%c %f"))
(if LilyPond-kick-xdvi
(let ((process-xdvi (get-buffer-process buffer-xdvi)))
(if process-xdvi
(LilyPond-kill-midi))) ; stop and start playing
(if (and (member name (list "Midi" "MidiAll")) job-string)
(if (file-newer-than-file-p
- (LilyPond-master-file)
- (concat (substring (LilyPond-master-file) 0 -3) ".midi"))
+ (LilyPond-get-master-file)
+ (concat (substring (LilyPond-get-master-file) 0 -3) ".midi"))
(if (y-or-n-p "Midi older than source. Reformat midi?")
(progn
(LilyPond-command-formatmidi)
(while (LilyPond-running)
(message "Starts playing midi once it is built.")
(sit-for 0 100))))))
- (if (member name (list "LilyPond" "TeX" "2Midi" "2PS" "2Dvi"
+ (if (member name (list "LilyPond" "TeX" "2Midi" "2PS"
"Book" "LaTeX"))
(if (setq jobs (LilyPond-running))
(progn
(sit-for 0 100)))
(setq job-string nil)))))
- (setq LilyPond-command-default name)
+ (setq LilyPond-command-next
+ (let* ((entry (assoc name LilyPond-command-alist))
+ (next-command (nth 3 (cdr entry))))
+ (or next-command
+ LilyPond-command-default)))
+
(if (string-equal job-string "no jobs")
(LilyPond-compile-file command name))))))))
(define-key LilyPond-mode-map "\C-c\C-k" 'LilyPond-kill-jobs)
(define-key LilyPond-mode-map "\C-c\C-c" 'LilyPond-command-master)
(define-key LilyPond-mode-map "\C-cm" 'LilyPond-command-formatmidi)
- (define-key LilyPond-mode-map "\C-c\C-d" 'LilyPond-command-formatdvi)
(define-key LilyPond-mode-map "\C-c\C-f" 'LilyPond-command-formatps)
- (define-key LilyPond-mode-map "\C-c\C-s" 'LilyPond-command-smartview)
- (define-key LilyPond-mode-map "\C-c\C-v" 'LilyPond-command-view)
+ (define-key LilyPond-mode-map "\C-c\C-g" 'LilyPond-command-formatgnome)
+ (define-key LilyPond-mode-map "\C-c\C-s" 'LilyPond-command-view)
(define-key LilyPond-mode-map "\C-c\C-p" 'LilyPond-command-viewps)
(define-key LilyPond-mode-map [(control c) return] 'LilyPond-command-current-midi)
(define-key LilyPond-mode-map [(control c) (control return)] 'LilyPond-command-all-midi)
(define-key LilyPond-mode-map "\C-x\C-s" 'LilyPond-save-buffer)
+ (define-key LilyPond-mode-map "\C-cb" 'LilyPond-what-beat)
(define-key LilyPond-mode-map "\C-cf" 'font-lock-fontify-buffer)
(define-key LilyPond-mode-map "\C-ci" 'LilyPond-insert-tag-current)
;; the following will should be overriden by Lilypond Quick Insert Mode
(define-key LilyPond-mode-map ">" 'LilyPond-electric-close-paren)
(define-key LilyPond-mode-map "}" 'LilyPond-electric-close-paren)
(define-key LilyPond-mode-map "]" 'LilyPond-electric-close-paren)
+ (define-key LilyPond-mode-map "|" 'LilyPond-electric-bar)
(if (string-match "XEmacs\\|Lucid" emacs-version)
(define-key LilyPond-mode-map [iso-left-tab] 'LilyPond-autocompletion)
- (define-key LilyPond-mode-map [iso-lefttab] 'LilyPond-autocompletion))
+ (define-key LilyPond-mode-map [(shift iso-lefttab)] 'LilyPond-autocompletion))
(define-key LilyPond-mode-map "\C-c\t" 'LilyPond-info-index-search)
)
;;; Some kind of mapping which includes :keys might be more elegant
;;; Put keys to LilyPond-command-alist and fetch them from there somehow.
'([ "LilyPond" LilyPond-command-lilypond t])
- '([ "TeX" (LilyPond-command (LilyPond-command-menu "TeX") 'LilyPond-master-file) ])
- '([ "2Dvi" LilyPond-command-formatdvi t])
'([ "2PS" LilyPond-command-formatps t])
'([ "2Midi" LilyPond-command-formatmidi t])
- '([ "Book" (LilyPond-command (LilyPond-command-menu "Book") 'LilyPond-master-file) ])
- '([ "LaTeX" (LilyPond-command (LilyPond-command-menu "LaTeX") 'LilyPond-master-file) ])
+ '([ "Book" (LilyPond-command (LilyPond-command-menu "Book") 'LilyPond-get-master-file) ])
+ '([ "LaTeX" (LilyPond-command (LilyPond-command-menu "LaTeX") 'LilyPond-get-master-file) ])
'([ "Kill jobs" LilyPond-kill-jobs t])
'("-----")
- '([ "SmartView" LilyPond-command-smartview t])
'([ "View" LilyPond-command-view t])
'([ "ViewPS" LilyPond-command-viewps t])
'("-----")
\\{LilyPond-mode-map}
VARIABLES
-LilyPond-command-alist\t\talist from name to command
-LilyPond-xdvi-command\t\tcommand to display dvi files -- bit superfluous"
+LilyPond-command-alist\t\talist from name to command"
(interactive)
;; set up local variables
(kill-all-local-variables)
(load-library "lilypond-font-lock")
(load-library "lilypond-indent")
+(load-library "lilypond-what-beat")
+
+(defun LilyPond-guile ()
+ (interactive)
+ (require 'ilisp)
+ (guile "lilyguile" (LilyPond-command-expand (cadr (assoc "LilyPond" LilyPond-command-alist))
+ (funcall 'LilyPond-get-master-file)))
+ (comint-default-send (ilisp-process) "(define-module (*anonymous-ly-0*))")
+ (comint-default-send (ilisp-process) "(set! %load-path (cons \"/usr/share/ilisp/\" %load-path))")
+ (comint-default-send (ilisp-process) "(use-modules (guile-user) (guile-ilisp))")
+ (comint-default-send (ilisp-process) "(newline)"))
(provide 'lilypond-mode)
;;; lilypond-mode.el ends here