+;; Session-handling variables and procedures.
+;;
+;; A "session" corresponds to one .ly file processed on a LilyPond
+;; command line. Every session gets to see a reasonably fresh state
+;; of LilyPond and should work independently from previous files.
+;;
+;; Session management relies on cooperation, namely the user not
+;; trying to change variables and data structures internal to
+;; LilyPond. It is not proof against in-place modification of data
+;; structures (as they are just reinitialized with the original
+;; identities), and it is not proof against tampering with internals.
+;;
+;; As a consequence, session management is not sufficient for
+;; separating multiple independent .ly files in "-dsafe" mode: you
+;; should give each its own LilyPond process when reliable separation
+;; is mandatory.
+;;
+;; For standard tasks and programming practices, multiple sessions in
+;; the same LilyPond job should work reasonably independently and
+;; without "bleed-over" while still loading and compiling the
+;; relevant .scm and .ly files only once.
+;;
+
+(define lilypond-declarations '())
+(define after-session-hook (make-hook))
+
+(define-public (call-after-session thunk)
+ (if (ly:undead? lilypond-declarations)
+ (ly:error (_ "call-after-session used after session start")))
+ (add-hook! after-session-hook thunk #t))
+
+(defmacro-public define-session (name value)
+ "This defines a variable @var{name} with the starting value
+@var{value} that is reinitialized at the start of each session.
+A@tie{}session basically corresponds to one LilyPond file on the
+command line. The value is recorded at the start of the first session
+after loading all initialization files and before loading the user
+file and is reinstated for all of the following sessions. This
+happens just by replacing the value, not by copying structures, so you
+should not destructively modify them. For example, lists defined in
+this manner should be changed within a session only be adding material
+to their front or replacing them altogether, not by modifying parts of
+them. It is an error to call @code{define-session} after the first
+session has started."
+ (define (add-session-variable name value)
+ (if (ly:undead? lilypond-declarations)
+ (ly:error (_ "define-session used after session start")))
+ (let ((var (make-variable value)))
+ (module-add! (current-module) name var)
+ (set! lilypond-declarations (cons var lilypond-declarations))))
+ `(,add-session-variable ',name ,value))
+
+(defmacro-public define-session-public (name value)
+ "Like @code{define-session}, but also exports @var{name}."
+ `(begin
+ (define-session ,name ,value)
+ (export ,name)))
+
+(define (session-terminate)
+ (if (ly:undead? lilypond-declarations)
+ (begin
+ (for-each
+ (lambda (p) (variable-set! (cadr p) (cddr p)))
+ (ly:get-undead lilypond-declarations))
+ (run-hook after-session-hook))))
+
+(define lilypond-interfaces #f)
+
+(define-public (session-initialize thunk)
+ "Initialize this session. The first session in a LilyPond run is
+initialized by calling @var{thunk}, then recording the values of all
+variables in the current module as well as those defined with
+@code{define-session}. Subsequent calls of @code{session-initialize}
+ignore @var{thunk} and instead just reinitialize all recorded
+variables to their value after the initial call of @var{thunk}."
+
+ ;; We need to save the variables of the current module along with
+ ;; their values: functions defined in the module might refer to the
+ ;; variables.
+
+ ;; The entries in lilypond-declarations consist of a cons* consisting
+ ;; of symbol, variable, and value. Variables defined with
+ ;; define-session have the symbol set to #f.
+
+ (if (ly:undead? lilypond-declarations)
+ (begin
+ (module-use-interfaces! (current-module) (reverse lilypond-interfaces))
+ (for-each
+ (lambda (p)
+ (let ((var (cadr p))
+ (val (cddr p)))
+ (variable-set! var val)
+ (if (car p)
+ (module-add! (current-module) (car p) var))))
+ (ly:get-undead lilypond-declarations)))
+ (begin
+ (thunk)
+ (set! lilypond-interfaces
+ (filter (lambda (m) (eq? 'interface (module-kind m)))
+ (module-uses (current-module))))
+ (let ((decl (map! (lambda (v)
+ (cons* #f v (variable-ref v)))
+ lilypond-declarations)))
+ (module-for-each
+ (lambda (s v)
+ (let ((val (variable-ref v)))
+ (if (not (ly:lily-parser? val))
+ (set! decl
+ (cons
+ (cons* s v val)
+ decl)))))
+ (current-module))
+ (set! lilypond-declarations (ly:make-undead decl))))))
+
+(define scheme-options-definitions
+ `(
+ ;; NAMING: either
+
+ ;; - [subject-]object-object-verb +"ing"
+ ;; - [subject-]-verb-object-object
+
+ ;; Avoid overlong lines in `lilypond -dhelp'! Strings should not
+ ;; be longer than 48 characters per line.
+
+ (anti-alias-factor 1
+ "Render at higher resolution (using given factor)
+and scale down result to prevent jaggies in
+PNG images.")
+ (aux-files
+ #t
+ "Create .tex, .texi, .count files in the
+EPS backend.")
+ (backend
+ ps
+ "Select backend. Possible values: 'eps, 'null,
+'ps, 'scm, 'socket, 'svg.")
+ (check-internal-types
+ #f
+ "Check every property assignment for types.")
+ (clip-systems
+ #f
+ "Generate cut-out snippets of a score.")
+ (datadir
+ #f
+ "LilyPond prefix for data files (read-only).")
+ (debug-gc
+ #f
+ "Dump memory debugging statistics.")
+ (debug-gc-assert-parsed-dead
+ #f
+ "For memory debugging: Ensure that all
+references to parsed objects are dead. This is
+an internal option, and is switched on
+automatically for `-ddebug-gc'.")
+ (debug-lexer
+ #f
+ "Debug the flex lexer.")
+ (debug-page-breaking-scoring
+ #f
+ "Dump scores for many different page breaking
+configurations.")
+ (debug-parser
+ #f
+ "Debug the bison parser.")
+ (debug-property-callbacks
+ #f
+ "Debug cyclic callback chains.")
+ (debug-skylines
+ #f
+ "Debug skylines.")
+ (delete-intermediate-files
+ #t
+ "Delete unusable, intermediate PostScript files.")
+ (dump-profile
+ #f
+ "Dump memory and time information for each file.")
+ (dump-cpu-profile
+ #f
+ "Dump timing information (system-dependent).")
+ (dump-signatures
+ #f
+ "Dump output signatures of each system. Used for
+regression testing.")
+ (eps-box-padding
+ #f
+ "Pad left edge of the output EPS bounding box by
+given amount (in mm).")
+ (gs-load-fonts
+ #f
+ "Load fonts via Ghostscript.")
+ (gs-load-lily-fonts
+ #f
+ "Load only LilyPond fonts via Ghostscript.")
+ (gui
+ #f
+ "Run LilyPond from a GUI and redirect stderr to
+a log file.")
+ (help
+ #f
+ "Show this help.")
+ (include-book-title-preview
+ #t
+ "Include book titles in preview images.")
+ (include-eps-fonts
+ #t
+ "Include fonts in separate-system EPS files.")
+ (include-settings
+ #f
+ "Include file for global settings, included before the score is processed.")
+ (job-count
+ #f
+ "Process in parallel, using the given number of
+jobs.")
+ (log-file
+ #f
+ "If string FOO is given as argument, redirect
+output to log file `FOO.log'.")
+ (max-markup-depth
+ 1024
+ "Maximum depth for the markup tree. If a markup has more levels,
+assume it will not terminate on its own, print a warning and return a
+null markup instead.")
+ (midi-extension ,(if (eq? PLATFORM 'windows)
+ "mid"
+ "midi")
+ "Set the default file extension for MIDI output
+file to given string.")
+ (music-strings-to-paths
+ #f
+ "Convert text strings to paths when glyphs belong
+to a music font.")
+ (point-and-click
+ #t
+ "Add point & click links to PDF output.")
+ (paper-size
+ "a4"
+ "Set default paper size.")
+ (pixmap-format
+ "png16m"
+ "Set GhostScript's output format for pixel images.")
+ (preview
+ #f
+ "Create preview images also.")
+ (print-pages
+ #t
+ "Print pages in the normal way.")
+ (protected-scheme-parsing
+ #t
+ "Continue when errors in inline scheme are caught
+in the parser. If #f, halt on errors and print
+a stack trace.")
+ (profile-property-accesses
+ #f
+ "Keep statistics of get_property() calls.")
+ (resolution
+ 101
+ "Set resolution for generating PNG pixmaps to
+given value (in dpi).")
+ (read-file-list
+ #f
+ "Specify name of a file which contains a list of
+input files to be processed.")
+ (relative-includes
+ #f
+ "When processing an \\include command, look for
+the included file relative to the current file\
+\n(instead of the root file)")
+ (safe
+ #f
+ "Run in safer mode.")
+ (separate-log-files
+ #f
+ "For input files `FILE1.ly', `FILE2.ly', ...
+output log data to files `FILE1.log',
+`FILE2.log', ...")
+ (show-available-fonts
+ #f
+ "List available font names.")
+ (strict-infinity-checking
+ #f
+ "Force a crash on encountering Inf and NaN
+floating point exceptions.")
+ (strip-output-dir
+ #t
+ "Don't use directories from input files while
+constructing output file names.")
+ (strokeadjust
+ #f
+ "Set the PostScript strokeadjust operator explicitly.
+This employs different drawing primitives, resulting in
+large PDF file size increases but often markedly better
+PDF previews.")
+ (svg-woff
+ #f
+ "Use woff font files in SVG backend.")
+ (trace-memory-frequency
+ #f
+ "Record Scheme cell usage this many times per
+second. Dump results to `FILE.stacks' and
+`FILE.graph'.")
+ (trace-scheme-coverage
+ #f
+ "Record coverage of Scheme files in `FILE.cov'.")
+ (verbose ,(ly:verbose-output?)
+ "Verbose output, i.e. loglevel at least DEBUG (read-only).")
+ (warning-as-error
+ #f
+ "Change all warning and programming_error
+messages into errors.")
+ ))