else:
return f"ColouredString({self.s!r}, {self.c!r})"
+ def __iter__(self):
+ return (ColouredString(sc, cc) for sc, cc in zip(self.s, self.c))
+
def __eq__(self, rhs):
rhs = type(self)(rhs)
return (self.s, self.c) == (rhs.s, rhs.c)
'S': '\033[0;1;7;44;37m',
'D': '\033[0;7;44;37m',
'F': '\033[0;1;32m',
+ 'c': '\033[0;33m',
+ '#': '\033[0;36m',
+ '@': '\033[0;32m',
}[cc])
colour = cc
buf.write(sc)
def __init__(self):
super().__init__()
self.paras = [Paragraph()]
+ self.colourstack = [' ']
def handle_starttag(self, tag, attrs):
- if tag in {"a", "span"}:
- # FIXME: maybe handle some cases of this to spot hashtags,
- # usernames etc?
+ attrdict = dict(attrs)
+
+ if tag == "a":
+ classes = set(attrdict.get("class", "").split())
+ colour = ("#" if "hashtag" in classes else
+ "@" if "mention" in classes else " ")
+ self.colourstack.append(colour)
+ return
+
+ if tag == "span":
return
if tag == "p":
self.paras.append(Paragraph())
return
+ if tag == "code":
+ self.colourstack.append('c')
+ return
+
+ # FIXME: need <pre>, e.g. in
+ # https://neuromatch.social/@mstimberg/111375114784712346
+ # and _perhaps_ that ought to generate paragraphs with a
+ # 'truncate, don't wrap' attribute?
+
print("UNKNOWN START", tag, attrs)
def handle_endtag(self, tag):
- if tag in {"a", "span"}:
+ if tag == "span":
return
if tag == "p":
self.paras.append(Paragraph())
return
+ if tag in {"a", "code"}:
+ self.colourstack.pop()
+ return
+
def handle_data(self, data):
- self.paras[-1].add(data)
+ self.paras[-1].add(ColouredString(data, self.colourstack[-1]))
def done(self):
for para in self.paras:
return list(itertools.chain(*[para.render(width)
for para in pp.paras]))
- def testHTML(self):
+ def testParagraphs(self):
html = "<p>Testing, testing, 1, 2, 3</p>"
self.assertEqual(self.parse_html(html), [
ColouredString('Testing, testing, 1, 2, 3'),
ColouredString('Second line'),
])
- def testWrap(self):
+ def testWrapping(self):
html = ("<p>Pease porridge hot, pease porridge cold, pease porridge "
"in the pot, nine days old</p>")
self.assertEqual(self.parse_html(html), [
ColouredString('Pease porridge hot, pease porridge cold, pease'),
ColouredString('porridge in the pot, nine days old'),
])
+
+ def testMarkup(self):
+ html = "<p>Test of some <code>literal code</code></p>"
+ self.assertEqual(self.parse_html(html), [
+ ColouredString('Test of some literal code',
+ ' cccccccccccc'),
+ ])
+
+ html = """<p>Test of a <a href="https://some.instance/tags/hashtag" class="mention hashtag" rel="nofollow noopener noreferrer" target="_blank">#<span>hashtag</span></a></p>"""
+ self.assertEqual(self.parse_html(html), [
+ ColouredString('Test of a #hashtag',
+ ' ########'),
+ ])
+
+ html = """<p>Test of a <span class="h-card" translate="no"><a href="https://some.instance/@username" class="u-url mention" rel="nofollow noopener noreferrer" target="_blank">@<span>username</span></a></span></p>"""
+ self.assertEqual(self.parse_html(html), [
+ ColouredString('Test of a @username',
+ ' @@@@@@@@@'),
+ ])