Fix pair tag formatting

This commit is contained in:
Tim Van Baak 2020-03-05 20:42:16 -08:00
parent 2a71d4733a
commit f684695836
1 changed files with 61 additions and 24 deletions

View File

@ -47,13 +47,13 @@ class BoldSpan(SpanContainer):
class ItalicSpan(SpanContainer): class ItalicSpan(SpanContainer):
"""A span of text inside italic marks""" """A span of text inside italic marks"""
class CitationSpan(Renderable): class CitationSpan(SpanContainer):
"""A citation to another article""" """A citation to another article"""
def __init__(self, cite_text, cite_target): def __init__(self, spans, cite_target):
self.cite_text = cite_text super().__init__(spans)
self.cite_target = cite_target self.cite_target = cite_target
def __str__(self): def __str__(self):
return f"{{{self.cite_text}:{self.cite_target}}}" return f"{{{' '.join([str(span) for span in self.spans])}:{self.cite_target}}}"
def parse_raw_markdown(text): def parse_raw_markdown(text):
@ -67,46 +67,83 @@ def parse_paragraph(text):
# Parse the paragraph as a span of text # Parse the paragraph as a span of text
text = text.strip() text = text.strip()
if text and text[0] == '~': if text and text[0] == '~':
return SignatureParagraph(parse_citations(text[1:])) return SignatureParagraph(parse_paired_formatting(text[1:]))
else: else:
return BodyParagraph(parse_citations(text)) return BodyParagraph(parse_paired_formatting(text))
def parse_citations(text): def parse_paired_formatting(text, cite=True, bold=True, italic=True):
# Find positions of any paired formatting
first_cite = text.find("[[") if cite else -1
first_bold = text.find("**") if bold else -1
first_italic = text.find("//") if italic else -1
# Load the possible parse handlers into the map
handlers = {}
handlers[first_cite] = lambda: parse_citation(text, bold=bold, italic=italic)
handlers[first_bold] = lambda: parse_bold(text, cite=cite, italic=italic)
handlers[first_italic] = lambda: parse_italic(text, cite=cite, bold=bold)
# If nothing was found, move on to the next parsing step
handlers[-1] = lambda: parse_breaks(text)
# Choose a handler based on the earliest found result
finds = [i for i in (first_cite, first_bold, first_italic) if i > -1]
first = min(finds) if finds else -1
return handlers[first]()
def parse_citation(text, bold=True, italic=True):
cite_open = text.find("[[") cite_open = text.find("[[")
if cite_open > -1: if cite_open > -1:
cite_close = text.find("]]", cite_open + 2) cite_close = text.find("]]", cite_open + 2)
spans_before = parse_bold(text[:cite_open]) # Since we searched for pairs from the beginning, there should be no
spans_after = parse_citations(text[cite_close+2:]) # undetected pair formatting before this one, so move to the next
text_inner = text[cite_open+2:cite_close] # level of parsing
alias_split = text_inner.split("|", 1) spans_before = parse_breaks(text[:cite_open])
citation = CitationSpan(alias_split[0], alias_split[-1]) # Continue parsing pair formatting after this one closes with all
# three as valid choices
spans_after = parse_paired_formatting(text[cite_close + 2:])
# Parse inner text and skip parsing for this format pair
text_inner = text[cite_open + 2:cite_close]
# For citations specifically, we may need to split off a citation
# target from the alias text
inner_split = text_inner.split("|", 1)
text_inner_actual, cite_target = inner_split[0], inner_split[-1]
spans_inner = parse_paired_formatting(text_inner_actual,
cite=False, bold=bold, italic=italic)
citation = CitationSpan(spans_inner, cite_target)
return spans_before + [citation] + spans_after return spans_before + [citation] + spans_after
# No citations, just parse the regular formatting # Should never happen
return parse_bold(text) return parse_breaks(text)
def parse_bold(text): def parse_bold(text, cite=True, italic=True):
bold_open = text.find("**") bold_open = text.find("**")
if bold_open > -1: if bold_open > -1:
bold_close = text.find("**", bold_open + 2) bold_close = text.find("**", bold_open + 2)
spans_before = parse_italic(text[:bold_open]) # Should be no formatting behind us
spans_after = parse_bold(text[bold_close+2:]) spans_before = parse_breaks(text[:bold_open])
spans_inner = parse_italic(text[bold_open+2:bold_close]) # Freely parse formatting after us
spans_after = parse_paired_formatting(text[bold_close+2:])
# Parse inner text minus bold parsing
text_inner = text[bold_open+2:bold_close]
spans_inner = parse_paired_formatting(text_inner,
cite=cite, bold=False, italic=italic)
bold = BoldSpan(spans_inner) bold = BoldSpan(spans_inner)
return spans_before + [bold] + spans_after return spans_before + [bold] + spans_after
# Should never happen
return parse_italic(text) return parse_italic(text)
def parse_italic(text): def parse_italic(text, cite=True, bold=True):
italic_open = text.find("//") italic_open = text.find("//")
if italic_open > -1: if italic_open > -1:
italic_close = text.find("//", italic_open + 2) italic_close = text.find("//", italic_open + 2)
text_before = text[:italic_open] # Should be no formatting behind us
spans_before = parse_breaks(text[:italic_open])
# Freely parse formatting after us
spans_after = parse_paired_formatting(text[italic_close+2:])
# Parse inner text minus italic parsing
text_inner = text[italic_open+2:italic_close] text_inner = text[italic_open+2:italic_close]
text_after = text[italic_close+2:] spans_inner = parse_paired_formatting(text_inner,
spans_before = parse_breaks(text_before) cite=cite, bold=bold, italic=False)
spans_after = parse_italic(text_after)
spans_inner = parse_breaks(text_inner)
italic = ItalicSpan(spans_inner) italic = ItalicSpan(spans_inner)
return spans_before + [italic] + spans_after return spans_before + [italic] + spans_after
# Should never happen
return parse_breaks(text) return parse_breaks(text)
def parse_breaks(text): def parse_breaks(text):