From a37c56a51aa1c9a4c403d6563461a4d20d8f455e Mon Sep 17 00:00:00 2001 From: Stuart Sears Date: Fri, 23 Feb 2024 13:47:25 +0000 Subject: [PATCH] Add support for singers' instructions --- tests/test_patterns.py | 73 ++++++++++++++++++++++++------------------ ukedown/patterns.py | 3 ++ ukedown/udn.py | 12 +++++++ 3 files changed, 57 insertions(+), 31 deletions(-) diff --git a/tests/test_patterns.py b/tests/test_patterns.py index c02b541..2030bfd 100644 --- a/tests/test_patterns.py +++ b/tests/test_patterns.py @@ -7,16 +7,18 @@ def pytest_generate_tests(metafunc: pytest.Metafunc): metafunc.parametrize( - 'case', - getattr(metafunc.cls, metafunc.function.__name__.replace('test_', ''), []), - scope='class' + "case", + getattr(metafunc.cls, metafunc.function.__name__.replace("test_", ""), []), + scope="class", ) class Base: @property def pattern(self) -> re.Pattern: - return re.compile(getattr(patterns, self.__class__.__name__.replace('Test', '').upper())) + return re.compile( + getattr(patterns, self.__class__.__name__.replace("Test", "").upper()) + ) def test_matches(self, case: str): assert self.pattern.match(case) @@ -27,73 +29,82 @@ def test_failures(self, case: str): class TestChord(Base): matches = [ - '(C)', - '(C#)', - '(Cb)', - '(Dm)', + "(C)", + "(C#)", + "(Cb)", + "(Dm)", ] failures = [ - '(H)', + "(H)", ] class TestHyphens(Base): matches = [ - '-', - '- ', - ' -', - ' - ', - '—', + "-", + "- ", + " -", + " - ", + "—", ] failures = [ - '_', + "_", ] class TestVox(Base): matches = [ - '(I can mash potato)', - '(I can (Bb)mash po(C)tato)', + "(I can mash potato)", + "(I can (Bb)mash po(C)tato)", + """(G) I'll be (C)there for (D)you (when the rain starts to +(G)Pour) I'll be (C)there for (D)you (like I've been there +Be(G)fore) I'll be (C)there for (D)you ('Cause you're there for me (F)too)""", ] class TestNotes(Base): matches = [ - '{repeat x2}', - '{demented choir noises}', + "{repeat x2}", + "{demented choir noises}", ] class TestBox(Base): matches = [ - '| {repeat x2}', + "| {repeat x2}", # New paragraph within box? - '| |', + "| |", ] failures = [ # TODO: These should (probably) not fail? # https://github.com/birdcolour/ukulele-wednesdays/issues/24 - '| (D) (D) (D) (F) | (D) (D) (D) (F)', - '| (C) (C) (A7sus4) (A7sus4) | (G) (G) (F) (F)', - '| (D) (D) (D) (F) | (D) (D) (D) (F) |', - '| (C) (C) (A7sus4) (A7sus4) | (G) (G) (F) (F) |', + "| (D) (D) (D) (F) | (D) (D) (D) (F)", + "| (C) (C) (A7sus4) (A7sus4) | (G) (G) (F) (F)", + "| (D) (D) (D) (F) | (D) (D) (D) (F) |", + "| (C) (C) (A7sus4) (A7sus4) | (G) (G) (F) (F) |", # https://github.com/birdcolour/ukulele-wednesdays/issues/25 - '|', - '| ', + "|", + "| ", + ] + +class TestSinger(Base): + matches = [ + "", + "", ] class TestHeader(Base): matches = [ - '[chorus]', - '[bridge]', + "[chorus]", + "[bridge]", ] class TestRepeats(Base): matches = [ - 'x2', + "x2", ] failures = [ - 'x 3', + "x 3", ] diff --git a/ukedown/patterns.py b/ukedown/patterns.py index b2b0e80..278eea3 100644 --- a/ukedown/patterns.py +++ b/ukedown/patterns.py @@ -45,3 +45,6 @@ # pattern to pickup and "boldify" repetitions REPEATS = r"(x\d+)" + +# Singers' instructions/notes now in angled brackets +SINGER = r"<([^<]+)>" diff --git a/ukedown/udn.py b/ukedown/udn.py index 010fe41..2cbac51 100644 --- a/ukedown/udn.py +++ b/ukedown/udn.py @@ -98,6 +98,13 @@ def handleMatch(self, m, data): el.text = m.group(1) return el, m.start(0), m.end(0) +class SingerProcessor(InlineProcessor): + def handleMatch(self, m, data): + el = etree.Element("span") + el.set("class", "singer") + el.text = m.group(1) + return el, m.start(0), m.end(0) + class RepeatsProcessor(InlineProcessor): def handleMatch(self, m, data): @@ -263,6 +270,7 @@ def __init__(self, **kwargs): "inline_element": ["span", "HTML element for inline items"], "chord_pattern": [patterns.CHORD, "regex matching chords"], "vox_pattern": [patterns.VOX, "regex matching backing vocals"], + "singer_pattern": [patterns.SINGER, "regex matching singers' notes"], "notes_pattern": [ patterns.NOTES, "regex matching notes/instructions", @@ -304,6 +312,10 @@ def extendMarkdown(self, md): VoxProcessor(self.getConfig("vox_pattern"), md), "vox", 177 ) + md.inlinePatterns.register( + SingerProcessor(self.getConfig("singer_pattern"), md), "singer", 177 + ) + md.inlinePatterns.register( NotesProcessor(self.getConfig("notes_pattern"), md), "notes", 176 )