From 8b8cd7a7f4417f0be61067ed16f5e0e7a0037757 Mon Sep 17 00:00:00 2001 From: aouxwoux <81102668+aouxwoux@users.noreply.github.com> Date: Wed, 8 Jul 2026 20:13:22 +0530 Subject: [PATCH] Preserve rhythm symbols in chords-over-words lines --- src/parser/chords_over_words/helpers.ts | 25 ++++++++++++++------ test/parser/chords_over_words_parser.test.ts | 17 +++++++++++++ 2 files changed, 35 insertions(+), 7 deletions(-) diff --git a/src/parser/chords_over_words/helpers.ts b/src/parser/chords_over_words/helpers.ts index aff090318..d3a3bfe44 100644 --- a/src/parser/chords_over_words/helpers.ts +++ b/src/parser/chords_over_words/helpers.ts @@ -27,12 +27,14 @@ interface NoChord { column: number, } +type ChordsLineItem = Chord | RhythmSymbol | NoChord; + type DirectionLine = SerializedLine; type InlineMetadata = SerializedLine; interface ChordsLine { type: 'chordsLine', - items: (Chord | RhythmSymbol | NoChord)[] + items: ChordsLineItem[] } interface LyricsLine { @@ -70,8 +72,17 @@ function chordProperties(chord: Chord): ChordProperties { return properties; } -function getChordData(chord: Chord) { - return (chord.type === 'chord') ? { chord: chordProperties(chord) } : { chords: chord.value }; +function getChordData(item: ChordsLineItem) { + switch (item.type) { + case 'chord': + return { chord: chordProperties(item) }; + case 'symbol': + return { chords: item.value, isRhythmSymbol: true }; + case 'noChord': + return { chords: item.value }; + default: + throw new Error(`Unexpected chordsLine item ${item}`); + } } function buildSoftLineBreakResult( @@ -93,8 +104,8 @@ function buildSoftLineBreakResult( } function buildChordLyricsPairForChord( - chord: Chord, - nextChord: Chord | undefined, + chord: ChordsLineItem, + nextChord: ChordsLineItem | undefined, lyrics: string, chopFirstWord: boolean, ): (SerializedChordLyricsPair | SerializedSoftLineBreak)[] | SerializedChordLyricsPair { @@ -121,7 +132,7 @@ function buildChordLyricsPairForChord( } function constructChordLyricsPairs( - chords: Chord[], + chords: ChordsLineItem[], lyrics: string, chopFirstWord: boolean, ): (SerializedChordLyricsPair | SerializedSoftLineBreak)[] { @@ -137,7 +148,7 @@ function pairChordsWithLyrics( ): SerializedLine { const { content: lyrics } = lyricsLine; - const chords = chordsLine.items as Chord[]; + const chords = chordsLine.items; const chordLyricsPairs = constructChordLyricsPairs(chords, lyrics, chopFirstWord); const firstChord = chords[0]; diff --git a/test/parser/chords_over_words_parser.test.ts b/test/parser/chords_over_words_parser.test.ts index 3bcac5bc8..f29833717 100644 --- a/test/parser/chords_over_words_parser.test.ts +++ b/test/parser/chords_over_words_parser.test.ts @@ -282,6 +282,23 @@ describe('ChordsOverWordsParser', () => { expect(line6Pairs[4]).toBeChordLyricsPair('/', ''); }); + it('preserves rhythm symbols in chords over words lines', () => { + const chordOverWords = heredoc` + N.C. Am C - + Hey, Jude, don't make it bad`; + + const parser = new ChordsOverWordsParser(); + const song = parser.parse(chordOverWords, { chopFirstWord: false }); + const { lines } = song; + + const linePairs = lines[0].items; + expect(linePairs[0]).toBeChordLyricsPair('N.C.', 'Hey, '); + expect(linePairs[1]).toBeChordLyricsPair('Am', 'Jude, '); + expect(linePairs[2]).toBeChordLyricsPair('C', 'don\'t make it bad'); + expect(linePairs[3]).toBeChordLyricsPair('-', ''); + expect((linePairs[3] as ChordLyricsPair).isRhythmSymbol).toBe(true); + }); + it('parses comment without a ":"', () => { const chordOverWords = heredoc` title: Let it be