songsheet 6.2.0 → 6.3.0
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.
- package/index.d.ts +6 -1
- package/package.json +1 -1
- package/src/parser.js +30 -4
- package/test/parser.test.js +18 -1
package/index.d.ts
CHANGED
|
@@ -19,9 +19,14 @@ export interface Character {
|
|
|
19
19
|
barLine?: true
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
+
export interface BarLine {
|
|
23
|
+
column: number
|
|
24
|
+
chord?: Chord
|
|
25
|
+
}
|
|
26
|
+
|
|
22
27
|
export interface Line {
|
|
23
28
|
chords: PositionedChord[]
|
|
24
|
-
barLines:
|
|
29
|
+
barLines: BarLine[]
|
|
25
30
|
lyrics: string
|
|
26
31
|
characters: Character[]
|
|
27
32
|
}
|
package/package.json
CHANGED
package/src/parser.js
CHANGED
|
@@ -158,8 +158,10 @@ function collectExprMarkers(expr, sections) {
|
|
|
158
158
|
if (chord.splitMeasure) marker.splitMeasure = chord.splitMeasure
|
|
159
159
|
lineMarkers.push(marker)
|
|
160
160
|
}
|
|
161
|
-
for (const
|
|
162
|
-
|
|
161
|
+
for (const bar of line.barLines) {
|
|
162
|
+
const marker = { col: bar.column, type: 'bar' }
|
|
163
|
+
if (bar.chord) marker.chord = bar.chord
|
|
164
|
+
lineMarkers.push(marker)
|
|
163
165
|
}
|
|
164
166
|
lineMarkers.sort((a, b) => a.col - b.col)
|
|
165
167
|
result.push(...lineMarkers)
|
|
@@ -185,6 +187,7 @@ function compactMarkersToLine(markers) {
|
|
|
185
187
|
const chords = []
|
|
186
188
|
const barLines = []
|
|
187
189
|
let col = 0
|
|
190
|
+
let lastChord = null
|
|
188
191
|
for (const m of markers) {
|
|
189
192
|
if (col > 0) col += 1
|
|
190
193
|
if (m.type === 'chord') {
|
|
@@ -197,9 +200,15 @@ function compactMarkersToLine(markers) {
|
|
|
197
200
|
if (m.stop) chord.stop = true
|
|
198
201
|
if (m.splitMeasure) chord.splitMeasure = m.splitMeasure
|
|
199
202
|
chords.push(chord)
|
|
203
|
+
lastChord = { root: m.root, type: m.quality }
|
|
204
|
+
if (m.bass) lastChord.bass = m.bass
|
|
205
|
+
if (m.nashville) lastChord.nashville = true
|
|
200
206
|
col += name.length
|
|
201
207
|
} else {
|
|
202
|
-
|
|
208
|
+
const bar = { column: col }
|
|
209
|
+
if (m.chord) bar.chord = m.chord
|
|
210
|
+
else if (lastChord) bar.chord = lastChord
|
|
211
|
+
barLines.push(bar)
|
|
203
212
|
col += 1
|
|
204
213
|
}
|
|
205
214
|
}
|
|
@@ -251,6 +260,7 @@ function parseChordLyricBlock(text) {
|
|
|
251
260
|
const allChords = []
|
|
252
261
|
const allLyrics = []
|
|
253
262
|
let i = 0
|
|
263
|
+
let lastChord = null
|
|
254
264
|
|
|
255
265
|
while (i < rawLines.length) {
|
|
256
266
|
const line = rawLines[i]
|
|
@@ -260,7 +270,23 @@ function parseChordLyricBlock(text) {
|
|
|
260
270
|
// This is a chord line — next non-chord line is its paired lyric
|
|
261
271
|
const chordTokens = tokens
|
|
262
272
|
const chords = tokens.filter(t => t.type === 'CHORD').map(t => tokenToPositionedChord(t))
|
|
263
|
-
|
|
273
|
+
|
|
274
|
+
// Build barLines as objects with chord context
|
|
275
|
+
// Sort all tokens by column, walk left-to-right tracking currentChord
|
|
276
|
+
const sortedTokens = [...tokens].sort((a, b) => a.column - b.column)
|
|
277
|
+
let currentChord = lastChord
|
|
278
|
+
const barLines = []
|
|
279
|
+
for (const tok of sortedTokens) {
|
|
280
|
+
if (tok.type === 'CHORD') {
|
|
281
|
+
currentChord = tokenToChord(tok)
|
|
282
|
+
} else if (tok.type === 'BAR_LINE') {
|
|
283
|
+
const bar = { column: tok.column }
|
|
284
|
+
if (currentChord) bar.chord = currentChord
|
|
285
|
+
barLines.push(bar)
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
// Update lastChord for the next line
|
|
289
|
+
if (currentChord) lastChord = currentChord
|
|
264
290
|
|
|
265
291
|
i++
|
|
266
292
|
// Find the paired lyric line (next line that isn't a chord line)
|
package/test/parser.test.js
CHANGED
|
@@ -306,7 +306,12 @@ describe('bar line parsing', () => {
|
|
|
306
306
|
it('parses bar lines in chord lines', () => {
|
|
307
307
|
const song = parse('TEST SONG - AUTHOR\n\n| G | C | D |\nSome lyrics here')
|
|
308
308
|
const line = song.structure[0].lines[0]
|
|
309
|
-
expect(line.barLines).toEqual([
|
|
309
|
+
expect(line.barLines).toEqual([
|
|
310
|
+
{ column: 0 },
|
|
311
|
+
{ column: 4, chord: { root: 'G', type: '' } },
|
|
312
|
+
{ column: 8, chord: { root: 'C', type: '' } },
|
|
313
|
+
{ column: 12, chord: { root: 'D', type: '' } },
|
|
314
|
+
])
|
|
310
315
|
expect(line.chords.map(c => c.root)).toEqual(['G', 'C', 'D'])
|
|
311
316
|
})
|
|
312
317
|
|
|
@@ -316,4 +321,16 @@ describe('bar line parsing', () => {
|
|
|
316
321
|
expect(chars[0].barLine).toBe(true)
|
|
317
322
|
expect(chars[4].barLine).toBe(true)
|
|
318
323
|
})
|
|
324
|
+
|
|
325
|
+
it('carries chord across lines for leading bar lines', () => {
|
|
326
|
+
const song = parse('TEST SONG - AUTHOR\n\n C | F C\nline one\n | | G |\nline two')
|
|
327
|
+
const lines = song.structure[0].lines
|
|
328
|
+
// Line 1: C at col 2, | at col 6, F at col 10, C at col 13
|
|
329
|
+
expect(lines[0].chords.map(c => c.root)).toEqual(['C', 'F', 'C'])
|
|
330
|
+
// Line 2 starts with | — should carry C from end of line 1
|
|
331
|
+
expect(lines[1].barLines[0].chord).toEqual({ root: 'C', type: '' })
|
|
332
|
+
expect(lines[1].barLines[1].chord).toEqual({ root: 'C', type: '' })
|
|
333
|
+
// After G chord, the trailing | should carry G
|
|
334
|
+
expect(lines[1].barLines[2].chord).toEqual({ root: 'G', type: '' })
|
|
335
|
+
})
|
|
319
336
|
})
|