@k-l-lambda/lilylet 0.1.74 → 0.1.76

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -67,6 +67,7 @@ export const HIGHLIGHT_RULES: HighlightRule[] = [
67
67
  { re: /(?:\[instrument\b)/iy, scope: "header" },
68
68
  { re: /(?:\[genre\b)/iy, scope: "header" },
69
69
  { re: /(?:\[staves\b)/iy, scope: "header" },
70
+ { re: /(?:\[measures\b)/iy, scope: "header" },
70
71
  { re: /(?:\[auto-beam\b)/iy, scope: "header" },
71
72
  { re: /(?:\])/iy, scope: "squareBracket" },
72
73
  { re: /(?:"[^"]*")/iy, scope: "string" },
@@ -3,6 +3,7 @@ export * from "./types";
3
3
  export * from "./parser";
4
4
  export * from "./serializer";
5
5
  export * from "./staffLayout";
6
+ export * from "./measureLayout";
6
7
 
7
8
  import * as meiEncoder from "./meiEncoder";
8
9
  import * as musicXmlDecoder from "./musicXmlDecoder";
@@ -130,6 +130,13 @@
130
130
  const dynamicEvent = (type) => ({ type: 'dynamic', dynamicType: type });
131
131
  const markupMark = (content) => ({ markType: 'markup', content });
132
132
 
133
+ // Strip the surrounding quotes from a STRING token and unescape the control
134
+ // sequences the serializer emits (escapeString): \n \r \t \" \\ . A single
135
+ // left-to-right pass over each \x pair avoids the sequential-replace bug where
136
+ // an escaped backslash (\\) followed by n would be misread as a newline.
137
+ const UNESCAPE = { n: '\n', r: '\r', t: '\t', '"': '"', '\\': '\\' };
138
+ const unq = (s) => s.slice(1, -1).replace(/\\(.)/g, (m, c) => (c in UNESCAPE ? UNESCAPE[c] : m));
139
+
133
140
  // Build an { instruments: { <key>: { name, shortName? } } } fragment from a
134
141
  // `[instrument-<key>` header token. <key> is a staff-layout group key — a single
135
142
  // staff id ("1", "v1", "b") or a range ("1-2", "pl-pr") — taken verbatim after the
@@ -250,6 +257,7 @@
250
257
  \[instrument return 'HEADER_INSTRUMENT'
251
258
  \[genre return 'HEADER_GENRE'
252
259
  \[staves return 'HEADER_STAVES'
260
+ \[measures return 'HEADER_MEASURES'
253
261
  \[auto\-beam return 'HEADER_AUTOBEAM'
254
262
  \] return ']'
255
263
 
@@ -390,17 +398,18 @@ headers
390
398
  ;
391
399
 
392
400
  header
393
- : HEADER_TITLE STRING ']' -> ({ title: $2.slice(1, -1) })
394
- | HEADER_SUBTITLE STRING ']' -> ({ subtitle: $2.slice(1, -1) })
395
- | HEADER_COMPOSER STRING ']' -> ({ composer: $2.slice(1, -1) })
396
- | HEADER_ARRANGER STRING ']' -> ({ arranger: $2.slice(1, -1) })
397
- | HEADER_LYRICIST STRING ']' -> ({ lyricist: $2.slice(1, -1) })
398
- | HEADER_OPUS STRING ']' -> ({ opus: $2.slice(1, -1) })
399
- | HEADER_INSTRUMENT STRING ']' -> ({ instrument: $2.slice(1, -1) })
400
- | HEADER_INSTRUMENT_STAFF STRING ']' -> instrumentStaff($1, $2.slice(1, -1))
401
- | HEADER_INSTRUMENT_STAFF STRING STRING ']' -> instrumentStaff($1, $2.slice(1, -1), $3.slice(1, -1))
402
- | HEADER_GENRE STRING ']' -> ({ genre: $2.slice(1, -1) })
401
+ : HEADER_TITLE STRING ']' -> ({ title: unq($2) })
402
+ | HEADER_SUBTITLE STRING ']' -> ({ subtitle: unq($2) })
403
+ | HEADER_COMPOSER STRING ']' -> ({ composer: unq($2) })
404
+ | HEADER_ARRANGER STRING ']' -> ({ arranger: unq($2) })
405
+ | HEADER_LYRICIST STRING ']' -> ({ lyricist: unq($2) })
406
+ | HEADER_OPUS STRING ']' -> ({ opus: unq($2) })
407
+ | HEADER_INSTRUMENT STRING ']' -> ({ instrument: unq($2) })
408
+ | HEADER_INSTRUMENT_STAFF STRING ']' -> instrumentStaff($1, unq($2))
409
+ | HEADER_INSTRUMENT_STAFF STRING STRING ']' -> instrumentStaff($1, unq($2), unq($3))
410
+ | HEADER_GENRE STRING ']' -> ({ genre: unq($2) })
403
411
  | HEADER_STAVES STRING ']' -> ({ staves: $2.slice(1, -1) })
412
+ | HEADER_MEASURES STRING ']' -> ({ measureLayout: $2.slice(1, -1) })
404
413
  | HEADER_AUTOBEAM STRING ']' -> ({ autoBeam: $2.slice(1, -1) })
405
414
  ;
406
415
 
@@ -484,9 +493,9 @@ harmony_event
484
493
  ;
485
494
 
486
495
  markup_event
487
- : CMD_MARKUP STRING -> markupEvent($2.slice(1, -1))
488
- | '^' CMD_MARKUP STRING -> markupEvent($3.slice(1, -1), 'above')
489
- | '_' CMD_MARKUP STRING -> markupEvent($3.slice(1, -1), 'below')
496
+ : CMD_MARKUP STRING -> markupEvent(unq($2))
497
+ | '^' CMD_MARKUP STRING -> markupEvent(unq($3), 'above')
498
+ | '_' CMD_MARKUP STRING -> markupEvent(unq($3), 'below')
490
499
  ;
491
500
 
492
501
  dynamic_event
@@ -604,8 +613,8 @@ partial_cmd
604
613
  ;
605
614
 
606
615
  tempo_cmd
607
- : CMD_TEMPO STRING duration '=' NUMBER -> ({ text: $2.slice(1, -1), beat: $3, bpm: Number($5) })
608
- | CMD_TEMPO STRING -> ({ text: $2.slice(1, -1) })
616
+ : CMD_TEMPO STRING duration '=' NUMBER -> ({ text: unq($2), beat: $3, bpm: Number($5) })
617
+ | CMD_TEMPO STRING -> ({ text: unq($2) })
609
618
  | CMD_TEMPO duration '=' NUMBER -> ({ beat: $2, bpm: Number($4) })
610
619
  ;
611
620
 
@@ -767,5 +776,5 @@ navigation_mark
767
776
  ;
768
777
 
769
778
  markup_mark
770
- : CMD_MARKUP STRING -> markupMark($2.slice(1, -1))
779
+ : CMD_MARKUP STRING -> markupMark(unq($2))
771
780
  ;
@@ -751,26 +751,30 @@ const encodeVoice = (
751
751
  const encodeMetadata = (metadata: Metadata): string => {
752
752
  const entries: string[] = [];
753
753
 
754
+ // LilyPond strings escape backslash and double-quote with a backslash. An
755
+ // unescaped " (or \) inside a header value otherwise breaks the \header block.
756
+ const ly = (s: string): string => s.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
757
+
754
758
  if (metadata.title) {
755
- entries.push(` title = "${metadata.title}"`);
759
+ entries.push(` title = "${ly(metadata.title)}"`);
756
760
  }
757
761
  if (metadata.subtitle) {
758
- entries.push(` subtitle = "${metadata.subtitle}"`);
762
+ entries.push(` subtitle = "${ly(metadata.subtitle)}"`);
759
763
  }
760
764
  if (metadata.composer) {
761
- entries.push(` composer = "${metadata.composer}"`);
765
+ entries.push(` composer = "${ly(metadata.composer)}"`);
762
766
  }
763
767
  if (metadata.arranger) {
764
- entries.push(` arranger = "${metadata.arranger}"`);
768
+ entries.push(` arranger = "${ly(metadata.arranger)}"`);
765
769
  }
766
770
  if (metadata.lyricist) {
767
- entries.push(` poet = "${metadata.lyricist}"`);
771
+ entries.push(` poet = "${ly(metadata.lyricist)}"`);
768
772
  }
769
773
  if (metadata.opus) {
770
- entries.push(` opus = "${metadata.opus}"`);
774
+ entries.push(` opus = "${ly(metadata.opus)}"`);
771
775
  }
772
776
  if (metadata.instrument) {
773
- entries.push(` instrument = "${metadata.instrument}"`);
777
+ entries.push(` instrument = "${ly(metadata.instrument)}"`);
774
778
  }
775
779
 
776
780
  entries.push(' tagline = ##f');