fix: blank lines split implicit verses into separate sections (Closes #29)
This commit was merged in pull request #30.
This commit is contained in:
@@ -24,6 +24,7 @@ object ChordProParser {
|
|||||||
var currentType: SectionType? = null
|
var currentType: SectionType? = null
|
||||||
var currentLabel: String? = null
|
var currentLabel: String? = null
|
||||||
var currentLines = mutableListOf<SongLine>()
|
var currentLines = mutableListOf<SongLine>()
|
||||||
|
var explicitSection = false
|
||||||
|
|
||||||
// Notes block state
|
// Notes block state
|
||||||
var inNotesBlock = false
|
var inNotesBlock = false
|
||||||
@@ -42,6 +43,7 @@ object ChordProParser {
|
|||||||
currentType = null
|
currentType = null
|
||||||
currentLabel = null
|
currentLabel = null
|
||||||
currentLines = mutableListOf()
|
currentLines = mutableListOf()
|
||||||
|
explicitSection = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,8 +74,13 @@ object ChordProParser {
|
|||||||
// Skip comments
|
// Skip comments
|
||||||
if (line.trimStart().startsWith("#")) continue
|
if (line.trimStart().startsWith("#")) continue
|
||||||
|
|
||||||
// Skip empty lines
|
// Blank line: flush implicit sections, skip otherwise
|
||||||
if (line.isBlank()) continue
|
if (line.isBlank()) {
|
||||||
|
if (currentType != null && !explicitSection) {
|
||||||
|
flushSection()
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
// Directive line
|
// Directive line
|
||||||
if (line.trimStart().startsWith("{") && line.trimEnd().endsWith("}")) {
|
if (line.trimStart().startsWith("{") && line.trimEnd().endsWith("}")) {
|
||||||
@@ -109,6 +116,7 @@ object ChordProParser {
|
|||||||
flushSection()
|
flushSection()
|
||||||
currentType = SectionType.VERSE
|
currentType = SectionType.VERSE
|
||||||
currentLabel = value
|
currentLabel = value
|
||||||
|
explicitSection = true
|
||||||
}
|
}
|
||||||
"end_of_verse", "eov" -> {
|
"end_of_verse", "eov" -> {
|
||||||
flushSection()
|
flushSection()
|
||||||
@@ -117,6 +125,7 @@ object ChordProParser {
|
|||||||
flushSection()
|
flushSection()
|
||||||
currentType = SectionType.CHORUS
|
currentType = SectionType.CHORUS
|
||||||
currentLabel = value
|
currentLabel = value
|
||||||
|
explicitSection = true
|
||||||
}
|
}
|
||||||
"end_of_chorus", "eoc" -> {
|
"end_of_chorus", "eoc" -> {
|
||||||
flushSection()
|
flushSection()
|
||||||
@@ -125,6 +134,7 @@ object ChordProParser {
|
|||||||
flushSection()
|
flushSection()
|
||||||
currentType = SectionType.REPEAT
|
currentType = SectionType.REPEAT
|
||||||
currentLabel = value
|
currentLabel = value
|
||||||
|
explicitSection = true
|
||||||
}
|
}
|
||||||
"end_of_repeat", "eor" -> {
|
"end_of_repeat", "eor" -> {
|
||||||
flushSection()
|
flushSection()
|
||||||
|
|||||||
@@ -628,4 +628,145 @@ class ChordProParserTest {
|
|||||||
song.sections[0].lines[1].segments[0].text shouldBe "Some text"
|
song.sections[0].lines[1].segments[0].text shouldBe "Some text"
|
||||||
song.sections[0].lines[2].imagePath shouldBe "img2.png"
|
song.sections[0].lines[2].imagePath shouldBe "img2.png"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `blank line splits implicit verses into separate sections`() {
|
||||||
|
val input = """
|
||||||
|
{title: Am Brunnen vor dem Tore}
|
||||||
|
|
||||||
|
Am [D]Brunnen vor dem Tore
|
||||||
|
Ich [D]träumt in seinem Schatten
|
||||||
|
|
||||||
|
Ich musst auch heute wandern
|
||||||
|
Da hab ich noch im Dunkeln
|
||||||
|
""".trimIndent()
|
||||||
|
val song = ChordProParser.parse(input)
|
||||||
|
song.sections shouldHaveSize 2
|
||||||
|
song.sections[0].type shouldBe SectionType.VERSE
|
||||||
|
song.sections[0].lines shouldHaveSize 2
|
||||||
|
song.sections[0].lines[0].segments shouldHaveSize 2
|
||||||
|
song.sections[0].lines[0].segments[0].chord.shouldBeNull()
|
||||||
|
song.sections[0].lines[0].segments[0].text shouldBe "Am "
|
||||||
|
song.sections[0].lines[0].segments[1].chord shouldBe "D"
|
||||||
|
song.sections[0].lines[0].segments[1].text shouldBe "Brunnen vor dem Tore"
|
||||||
|
song.sections[1].type shouldBe SectionType.VERSE
|
||||||
|
song.sections[1].lines shouldHaveSize 2
|
||||||
|
song.sections[1].lines[0].segments[0].text shouldBe "Ich musst auch heute wandern"
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `blank lines within explicit sections are ignored`() {
|
||||||
|
val input = """
|
||||||
|
{title: Song}
|
||||||
|
{start_of_verse: Verse 1}
|
||||||
|
Line one
|
||||||
|
|
||||||
|
Line two
|
||||||
|
{end_of_verse}
|
||||||
|
""".trimIndent()
|
||||||
|
val song = ChordProParser.parse(input)
|
||||||
|
song.sections shouldHaveSize 1
|
||||||
|
song.sections[0].type shouldBe SectionType.VERSE
|
||||||
|
song.sections[0].label shouldBe "Verse 1"
|
||||||
|
song.sections[0].lines shouldHaveSize 2
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `blank lines between metadata do not create empty sections`() {
|
||||||
|
val input = """
|
||||||
|
{title: Song}
|
||||||
|
|
||||||
|
{lyricist: Someone}
|
||||||
|
|
||||||
|
{composer: Someone Else}
|
||||||
|
|
||||||
|
[Am]Hello world
|
||||||
|
""".trimIndent()
|
||||||
|
val song = ChordProParser.parse(input)
|
||||||
|
song.sections shouldHaveSize 1
|
||||||
|
song.sections[0].type shouldBe SectionType.VERSE
|
||||||
|
song.sections[0].lines shouldHaveSize 1
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `mixed explicit and implicit sections with blank lines`() {
|
||||||
|
val input = """
|
||||||
|
{title: Song}
|
||||||
|
{start_of_chorus}
|
||||||
|
[C]Chorus line
|
||||||
|
|
||||||
|
Still in chorus
|
||||||
|
{end_of_chorus}
|
||||||
|
|
||||||
|
Implicit verse one
|
||||||
|
|
||||||
|
Implicit verse two
|
||||||
|
""".trimIndent()
|
||||||
|
val song = ChordProParser.parse(input)
|
||||||
|
song.sections shouldHaveSize 3
|
||||||
|
song.sections[0].type shouldBe SectionType.CHORUS
|
||||||
|
song.sections[0].lines shouldHaveSize 2
|
||||||
|
song.sections[1].type shouldBe SectionType.VERSE
|
||||||
|
song.sections[1].lines shouldHaveSize 1
|
||||||
|
song.sections[1].lines[0].segments[0].text shouldBe "Implicit verse one"
|
||||||
|
song.sections[2].type shouldBe SectionType.VERSE
|
||||||
|
song.sections[2].lines shouldHaveSize 1
|
||||||
|
song.sections[2].lines[0].segments[0].text shouldBe "Implicit verse two"
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `multiple blank lines between implicit verses`() {
|
||||||
|
val input = """
|
||||||
|
{title: Song}
|
||||||
|
First verse line
|
||||||
|
|
||||||
|
|
||||||
|
Second verse line
|
||||||
|
""".trimIndent()
|
||||||
|
val song = ChordProParser.parse(input)
|
||||||
|
song.sections shouldHaveSize 2
|
||||||
|
song.sections[0].type shouldBe SectionType.VERSE
|
||||||
|
song.sections[0].lines shouldHaveSize 1
|
||||||
|
song.sections[1].type shouldBe SectionType.VERSE
|
||||||
|
song.sections[1].lines shouldHaveSize 1
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `three implicit verses separated by blank lines`() {
|
||||||
|
val input = """
|
||||||
|
{title: Song}
|
||||||
|
[Am]Verse one line one
|
||||||
|
Verse one line two
|
||||||
|
|
||||||
|
[C]Verse two line one
|
||||||
|
Verse two line two
|
||||||
|
|
||||||
|
[G]Verse three line one
|
||||||
|
Verse three line two
|
||||||
|
""".trimIndent()
|
||||||
|
val song = ChordProParser.parse(input)
|
||||||
|
song.sections shouldHaveSize 3
|
||||||
|
song.sections.forEach { section ->
|
||||||
|
section.type shouldBe SectionType.VERSE
|
||||||
|
section.lines shouldHaveSize 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `blank lines within explicit chorus are ignored`() {
|
||||||
|
val input = """
|
||||||
|
{title: Song}
|
||||||
|
{start_of_chorus}
|
||||||
|
Line one
|
||||||
|
|
||||||
|
Line two
|
||||||
|
|
||||||
|
Line three
|
||||||
|
{end_of_chorus}
|
||||||
|
""".trimIndent()
|
||||||
|
val song = ChordProParser.parse(input)
|
||||||
|
song.sections shouldHaveSize 1
|
||||||
|
song.sections[0].type shouldBe SectionType.CHORUS
|
||||||
|
song.sections[0].lines shouldHaveSize 3
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user