chiark / gitweb /
Start colourising the rendering
authorSimon Tatham <anakin@pobox.com>
Sat, 2 Dec 2023 12:30:50 +0000 (12:30 +0000)
committerSimon Tatham <anakin@pobox.com>
Sat, 2 Dec 2023 13:34:32 +0000 (13:34 +0000)
mastodonochrome
text.py

index 8621062849100c1893e305a675608b0645cbf0bc..b5fa0e45370a418d7dea0272a72f643c6ffced61 100755 (executable)
@@ -77,7 +77,7 @@ class MainUI(Client):
             p = Post(item)
             for thing in p.text():
                 for line in thing.render(80):
-                    print(line)
+                    print(line.ecma48())
 
 def main():
     parser = argparse.ArgumentParser(
diff --git a/text.py b/text.py
index faa3eb3d3fddbe8cc8f27e0b8b2651232df8518b..4821b5792255b753ba86e20054a563ec5ff57df8 100644 (file)
--- a/text.py
+++ b/text.py
@@ -1,12 +1,65 @@
 # Represent colourised terminal text in a width-independent form.
 
 import html.parser
+import io
+import itertools
 import time
 import wcwidth
 
+class ColouredString:
+    def __init__(self, string, colour=' '):
+        if isinstance(string, ColouredString):
+            self.s, self.c = string.s, string.c
+        else:
+            if len(colour) != len(string):
+                assert len(colour) == 1, "Colour ids are single characters"
+                colour = colour * len(string)
+            self.s, self.c = string, colour
+
+    def __add__(self, rhs):
+        rhs = type(self)(rhs)
+        return type(self)(self.s + rhs.s, self.c + rhs.c)
+
+    def __mul__(self, rhs):
+        return type(self)(self.s * rhs, self.c * rhs)
+
+    def __len__(self):
+        return len(self.s)
+
+    @property
+    def width(self):
+        return wcwidth.wcswidth(self.s)
+
+    def __str__(self):
+        return self.s
+
+    def __repr__(self):
+        return f"ColouredString({self.s!r}, {self.c!r})"
+
+    def __eq__(self, rhs):
+        rhs = type(self)(rhs)
+        return (self.s, self.c) == (rhs.s, rhs.c)
+    def __ne__(self, rhs):
+        return not (self == rhs)
+
+    def ecma48(self):
+        buf = io.StringIO()
+        colour = ' '
+        for sc, cc in itertools.chain(zip(self.s, self.c), [('',' ')]):
+            if cc != colour:
+                buf.write({
+                    ' ': '\033[m',
+                    'S': '\033[0;1;7;44;37m',
+                    'D': '\033[0;7;44;37m',
+                    'F': '\033[0;1;32m',
+                }[cc])
+                colour = cc
+            buf.write(sc)
+        return buf.getvalue()
+
 class BlankLine:
     def render(self, width):
-        yield ""
+        yield ColouredString("")
 
 class SeparatorLine:
     def __init__(self, timestamp):
@@ -15,9 +68,10 @@ class SeparatorLine:
     def render(self, width):
         date = time.strftime("%a %b %e %H:%M:%S %Y",
                              time.localtime(self.timestamp))
-        # FIXME: colours
-        suffix = "[" + date + "]--"
-        yield "-" * (width - 1 - len(suffix)) + suffix
+        suffix = (ColouredString("[", 'S') +
+                  ColouredString(date, 'D') +
+                  ColouredString("]--", 'S'))
+        yield ColouredString("-", 'S') * (width - 1 - suffix.width) + suffix
 
 class FromLine:
     def __init__(self, account, nameline):
@@ -26,31 +80,31 @@ class FromLine:
 
     def render(self, width):
         # FIXME: truncate
-
-        yield f"From: {self.nameline} ({self.account})"
+        yield (ColouredString("From: ") +
+               ColouredString(f"{self.nameline} ({self.account})", 'F'))
 
 class Paragraph:
     def __init__(self):
         self.words = []
-        self.unfinished_word = ''
+        self.unfinished_word = ColouredString('')
 
     def render(self, width):
         # For the moment, greedy algorithm. We can worry about cleverness later
-        line, space = '', ''
+        line, space = ColouredString(''), ColouredString('')
         for word in self.words:
-            if line != "" and wcwidth.wcswidth(line + space + word) >= width:
+            if line != "" and (line + space + word).width >= width:
                 yield line
-                line, space = '', ''
+                line, space = ColouredString(''), ColouredString('')
 
             line += space + word
-            space = ' '
+            space = ColouredString(' ')
 
-            if wcwidth.wcswidth(line) >= width:
+            if line.width >= width:
                 # FIXME: wrap explicitly?
                 yield line
-                line, space = '', ''
+                line, space = ColouredString(''), ColouredString('')
 
-        if line != "":
+        if len(line) != 0:
             yield line
 
     def empty(self):
@@ -59,7 +113,7 @@ class Paragraph:
     def end_word(self):
         if len(self.unfinished_word) > 0:
             self.words.append(self.unfinished_word)
-            self.unfinished_word = ''
+            self.unfinished_word = ColouredString('')
 
     def add(self, text):
         for c in text: