diff --git a/pco_chords.nimble b/pco_chords.nimble index f36e12e..bf0428f 100644 --- a/pco_chords.nimble +++ b/pco_chords.nimble @@ -1,6 +1,6 @@ # Package -version = "0.5.4" +version = "0.5.5" author = "Jonathan Bernard" description = "Chord chart formatter compatible with Planning Center Online" license = "MIT" diff --git a/src/pco_chordspkg/ast.nim b/src/pco_chordspkg/ast.nim index 330b572..f33e3fc 100644 --- a/src/pco_chordspkg/ast.nim +++ b/src/pco_chordspkg/ast.nim @@ -77,7 +77,7 @@ iterator pairs*(ccmd: ChordChartMetadata): tuple[key, value: string] = func kind*(ccn: ChordChartNode): ChordChartNodeKind = ccn.kind -func `$`(pitch: ChordChartPitch): string = +func `$`*(pitch: ChordChartPitch): string = ["A♭", "A", "B♭", "B", "C", "D♭", "D", "E♭", "E", "F", "G♭", "G"][ord(pitch)] func renderPitchInKey*( @@ -259,7 +259,7 @@ proc parsePitch*(ctx: ParserContext, keyValue: string): ChordChartPitch = # see regexr.com/70nv1 let CHORD_REGEX = "([b#♭♮𝄫𝄪]?[A-G1-7][b#♭♮𝄫𝄪]?)" & # chord root - "((min|maj|aug|dim|sus|6\\/9|[mM1-9#b♭♮𝄫𝄪Δ+oøoø][0-9]?|\\([1-9#b♭]+\\))*)" & # chord flavor/type + "((min|maj|aug|dim|sus|6\\/9|[mM1-9#b♭♮𝄫𝄪Δ+oøoø°𝆩][0-9]?|\\([1-9#b♭]+\\))*)" & # chord flavor/type "(\\/([b#♭♮𝄫𝄪]?[A-G1-7][b#♭♮𝄫𝄪]?))?" # optional bass let CHORD_PAT = re(CHORD_REGEX) @@ -344,13 +344,46 @@ let PAGE_BREAK_PAT = re"\s*PAGE_BREAK\s*$" let TRANSPOSE_PAT = re"\s*TRANSPOSE KEY ([+-]\d+)\s*$" let REDEFINE_KEY_PAT = re"\s*REDEFINE KEY ([+-]\d+)\s*$" #let NOTE_PAT = re"^(.*)({{[^}]+}}|{[^}]+})(.*)$" -let NOTE_START_PAT = re"{{" -let NOTE_END_PAT = re"}}" +let NOTE_START_PAT = re"\{\{" +let NOTE_END_PAT = re"\}\}" let SPACE_PAT = re"\s" let CHORD_IN_LYRICS_PAT = re"(\w+)(\[.+)" let CHORD_AND_LYRICS_PAT = re"^\[([^\]]+)\]([^\s\[]+)(.*)$" let BRACED_CHORD_PAT = re"^\[([^\]]+)\]$" -let NAKED_CHORDS_ONLY_PAT = re("^(" & CHORD_REGEX & "\\s*\\|*\\s*)+$") +let NAKED_CHORDS_ONLY_PAT = re("^\\s*(" & CHORD_REGEX & "\\s*\\|*\\s*)+$") + +proc readNote(ctx: var ParserContext, firstLine: string): tuple[pre, note, post: string] = + let startLineNum = ctx.curLineNum + + result = ("", "", "") + + let startMatch = firstLine.find(NOTE_START_PAT) + var endMatch = firstLine.find(NOTE_END_PAT) + + if startMatch.isNone: return + + if startMatch.get.matchBounds.a > 0: + result.pre = firstLine[0.. ctx.curLineNum and ctx.hasNextLine: + let line = ctx.nextLine + endMatch = line.find(NOTE_END_PAT) + if endMatch.isSome: + result.note &= line[0.. map( ChordChartNode( @@ -406,49 +440,28 @@ proc parseLine(ctx: ParserContext, line: string): ChordChartNode = spaceBefore: false, #FIXME chord: ctx.parseChord(it.strip), word: none[string]())) + return + + m = line.match(NOTE_START_PAT) + if m.isSome: + let (pre, note, post) = lentCtx.readNote(line) + result.line = ctx.parseLineParts(pre) & + @[ChordChartNode( + kind: ccnkNote, + inclInLyrics: true, + note: note)] & + ctx.parseLineParts(post) + return else: result.line = ctx.parseLineParts(line.splitWhitespace) -proc readNote(ctx: var ParserContext, endPat: Regex): string = - let startLineNum = ctx.curLineNum - result = "" - - while ctx.lines.len > ctx.curLineNum and ctx.hasNextLine: - let line = ctx.nextLine - let m = line.find(endPat) - if m.isSome: - result &= line[0.. 0: - # if this is not the first character of the line then let's split the - # line and continue to parse - ctx.pushPartialsToParse( - line[0.. * { + display: inline-block; + height: 1.2em; +} + .chord .flavor { font-variant-position: super; } +.note { margin-right: 1em; } + .song-order h3 { font-style: italic; font-weight: normal; @@ -90,10 +97,6 @@ h3 .section-text { @media screen { body { margin: 1em; } } - -@media print { - .page-contents { column-count: 2; } -} """ @@ -110,10 +113,8 @@ html { font-size: 1.5em; } -.page-contents { - column-width: 336px; - column-width: 3.5in; -} +.one-column .page-contents { column-count: 1; } +.two-column .page-contents { column-count: 2; } .column-break { margin-bottom: auto; } @@ -137,6 +138,8 @@ h3 .section-text { margin: 0 0.5em; } +.artist { font-style: italic; } + .line { display: flex; flex-direction: row; @@ -165,10 +168,17 @@ h3 .section-text { margin-right: 0.5em; } +.chord > * { + display: inline-block; + height: 1.2em; +} + .chord .flavor { font-variant-position: super; } +.note { margin-right: 1em; } + .song-order h3 { font-style: italic; font-weight: normal; @@ -182,10 +192,6 @@ h3 .section-text { @media screen { body { margin: 1em; } } - -@media print { - .page-contents { column-count: 2; } -} """ @@ -310,10 +316,7 @@ proc toHtml(ctx: var FormatContext, node: ChordChartNode, indent: string): strin of ccnkTransposeKey: ctx.currentKey = ctx.currentKey + node.transposeSteps - let headingVal = indent & "

Key Change: " & $ctx.currentKey & "

" - if ctx.currentSection.kind == ccnkNone: result &= headingVal - else: - result &= "
" & headingVal & "
" + result &= indent & "

Key Change: " & $ctx.currentKey & "

" of ccnkRedefineKey: let oldKey = ctx.currentKey @@ -361,7 +364,12 @@ proc toHtml*( elif fileExists(sc): result &= "" else: warn "cannot read script file '" & sc & "'" - result &= " \p " + result &= " \p" + + if cc.metadata.contains("columns") and cc.metadata["columns"] == "1": + result &= " " + else: + result &= " " var indent = " " @@ -378,6 +386,9 @@ proc toHtml*( result &= indent & "

" & metadataPieces.join(" | ") & "

\p" + if cc.metadata.contains("artist"): + result &= indent & "
" & cc.metadata["artist"] & "
\p" + result &= "
" result &= join(cc.nodes --> map(ctx.toHtml(it, indent & " ")), "\p")