Move colorization to the display layer

Color output isn't necessarily always going to be a terminal output
thing, and terminals don't always support the same escape codes (e.g. on
Windows). Thus, all colorization efforts are done in the Display rather
than in the Game.

Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
This commit is contained in:
2021-11-28 17:37:37 -08:00
parent 2357757b23
commit c71077a8f3
4 changed files with 43 additions and 46 deletions

View File

@@ -2,10 +2,11 @@ import argparse
import importlib.util import importlib.util
from pathlib import Path from pathlib import Path
import sys import sys
from agame.color import colorize
from agame.display import ANSIDisplay
from agame.game import Game from agame.game import Game
# TODO - conditional import for this based on OS
from agame.display import ANSIDisplay
# Parse args # Parse args
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(

View File

@@ -1,39 +0,0 @@
import re
__all__ = ("colorize",)
BOLD_PAT = re.compile(r"\*\*(.+?)\*\*", re.MULTILINE)
ITALIC_PAT = re.compile(r"//(.+?)//", re.MULTILINE)
INTEREST_PAT = re.compile(r"\(\((.+?)\)\)", re.MULTILINE)
SHADOW_PAT = re.compile(r"\{\{(.+?)\}\}", re.MULTILINE)
BOLD_COL = "\u001b[1m"
ITALIC_COL = "\u001b[3m"
INTEREST_COL = "\u001b[34;1m"
SHADOW_COL = "\u001b[30;1m"
RESET_COL = "\u001b[0m"
def colorize(text: str) -> str:
"""
Colorizes text for output on an ANSI terminal.
This will use escape codes to replace things.
Style guide:
((This)) is "interest" styling. This will make the text blue.
{{This}} is "shadow" styling. This will make the text a dark grey (or at
least, more subtle.)
"""
replacements = [
(INTEREST_PAT, INTEREST_COL),
(SHADOW_PAT, SHADOW_COL),
(BOLD_PAT, BOLD_COL),
(ITALIC_PAT, ITALIC_COL),
]
for (pat, col) in replacements:
text = pat.sub(col + r"\1" + RESET_COL, text)
return text

View File

@@ -1,7 +1,7 @@
import re
import sys import sys
import termios import termios
from typing import Optional, Sequence, TextIO, Tuple, TYPE_CHECKING from typing import Optional, Sequence, TextIO, Tuple, TYPE_CHECKING
from agame.color import colorize
from agame.action import Action from agame.action import Action
from agame.dialog import DialogOption from agame.dialog import DialogOption
from .display import Display from .display import Display
@@ -9,8 +9,44 @@ from .display import Display
if TYPE_CHECKING: if TYPE_CHECKING:
from agame.game import Game from agame.game import Game
__all__ = ("ANSIDisplay",)
ESC = "\u001b" ESC = "\u001b"
ANSI_MOVE_BOTTOM = f"{ESC}[999;0f" ANSI_MOVE_BOTTOM = f"{ESC}[999;0f"
ANSI_BOLD_COL = f"{ESC}[1m"
ANSI_ITALIC_COL = f"{ESC}[3m"
ANSI_INTEREST_COL = f"{ESC}[34;1m"
ANSI_SHADOW_COL = f"{ESC}[30;1m"
ANSI_RESET_COL = f"{ESC}[0m"
BOLD_PAT = re.compile(r"\*\*(.+?)\*\*", re.MULTILINE)
ITALIC_PAT = re.compile(r"//(.+?)//", re.MULTILINE)
INTEREST_PAT = re.compile(r"\(\((.+?)\)\)", re.MULTILINE)
SHADOW_PAT = re.compile(r"\{\{(.+?)\}\}", re.MULTILINE)
def colorize(text: str) -> str:
"""
Colorizes text for output on an ANSI terminal.
This will use escape codes to replace things.
Style guide:
((This)) is "interest" styling. This will make the text blue.
{{This}} is "shadow" styling. This will make the text a dark grey (or at
least, more subtle.)
"""
replacements = [
(INTEREST_PAT, ANSI_INTEREST_COL),
(SHADOW_PAT, ANSI_SHADOW_COL),
(BOLD_PAT, ANSI_BOLD_COL),
(ITALIC_PAT, ANSI_ITALIC_COL),
]
for (pat, col) in replacements:
text = pat.sub(col + r"\1" + ANSI_RESET_COL, text)
return text
class ANSIDisplay(Display): class ANSIDisplay(Display):
@@ -47,6 +83,7 @@ class ANSIDisplay(Display):
self.clear() self.clear()
def line(self, line: str = ""): def line(self, line: str = ""):
line = colorize(line)
self.stdout.write(line) self.stdout.write(line)
self.stdout.write("\n") self.stdout.write("\n")

View File

@@ -2,7 +2,6 @@ import dataclasses
import textwrap import textwrap
from typing import Any, MutableMapping, List, Match, Optional, Sequence, Union from typing import Any, MutableMapping, List, Match, Optional, Sequence, Union
from agame.action import Action from agame.action import Action
from agame.color import colorize
from agame.display import Display from agame.display import Display
from agame.item import Item, ItemInst from agame.item import Item, ItemInst
from agame.room import Room from agame.room import Room
@@ -141,12 +140,11 @@ class Game:
"Format, colorize, wrap, and print the message." "Format, colorize, wrap, and print the message."
if lines: if lines:
head = textwrap.fill(lines[0]) head = textwrap.fill(lines[0])
# TODO - move colorize to Display self.display.line(head)
self.display.line(colorize(head))
for line in lines[1:]: for line in lines[1:]:
message = textwrap.fill(line) message = textwrap.fill(line)
self.display.line() self.display.line()
self.display.line(colorize(message)) self.display.line(message)
else: else:
self.display.line() self.display.line()