150 lines
5 KiB
Python
150 lines
5 KiB
Python
import unicodedata
|
||
from typing import Set
|
||
|
||
import yaboli
|
||
|
||
from ..cursor_rendering import CursorRenderer
|
||
from ..element import Message, RenderedMessage
|
||
from ..markup import AT, AttributedText, Attributes
|
||
|
||
__all__ = ["EuphRenderer"]
|
||
|
||
class EuphRenderer(CursorRenderer):
|
||
|
||
YEAR_FORMAT = "%y-%m-%d "
|
||
YEAR_WIDTH = 11
|
||
|
||
TIME_FORMAT = "%H:%M"
|
||
TIME_WIDTH = 5
|
||
|
||
SECOND_FORMAT = ":%S"
|
||
SECOND_WIDTH = 3
|
||
|
||
NORMAL_WIDTH = {"N", "Na", "H", "A"}
|
||
|
||
def __init__(self,
|
||
nick: str,
|
||
replace_wide_unicode: bool = True,
|
||
replace_character_categories: Set[str] = {"Cf"},
|
||
unicode_placeholder: str = "<EFBFBD>",
|
||
# Meta settings
|
||
show_year: bool = False,
|
||
show_seconds: bool = False,
|
||
meta_attrs: Attributes = {},
|
||
# Surround settings
|
||
surround_left: str = "[",
|
||
surround_right: str = "]",
|
||
surround_attrs: Attributes = {},
|
||
# Cursor settings
|
||
cursor_surround_left: str = "<",
|
||
cursor_surround_right: str = ">",
|
||
cursor_surround_attrs: Attributes = {},
|
||
cursor_own_nick_attrs: Attributes = {},
|
||
cursor_fill: str = " ",
|
||
cursor_fill_attrs: Attributes = {},
|
||
# Various attributes
|
||
nick_attrs: Attributes = {},
|
||
own_nick_attrs: Attributes = {},
|
||
) -> None:
|
||
|
||
self.nick = nick
|
||
self._replace_wide_unicode = replace_wide_unicode
|
||
self._replace_character_categories = replace_character_categories
|
||
self._unicode_placeholder = unicode_placeholder
|
||
# Meta settings
|
||
self._show_year = show_year
|
||
self._show_seconds = show_seconds
|
||
self._meta_attrs = meta_attrs
|
||
# Surround settings
|
||
self._surround_left = surround_left
|
||
self._surround_right = surround_right
|
||
self._surround_attrs = surround_attrs
|
||
# Cursor settings
|
||
self._cursor_surround_left = cursor_surround_left
|
||
self._cursor_surround_right = cursor_surround_right
|
||
self._cursor_surround_attrs = cursor_surround_attrs
|
||
self._cursor_own_nick_attrs = cursor_own_nick_attrs
|
||
self._cursor_fill = cursor_fill
|
||
self._cursor_fill_attrs = cursor_fill_attrs
|
||
# Various attributes
|
||
self._nick_attrs = nick_attrs
|
||
self._own_nick_attrs = own_nick_attrs
|
||
|
||
@property
|
||
def meta_width(self) -> int:
|
||
width = self.TIME_WIDTH + 1 # One space at the end
|
||
|
||
if self._show_year:
|
||
width += self.YEAR_WIDTH
|
||
|
||
if self._show_seconds:
|
||
width += self.SECOND_WIDTH
|
||
|
||
return width
|
||
|
||
def _is_wide(self, char: str) -> bool:
|
||
return unicodedata.east_asian_width(char) not in self.NORMAL_WIDTH
|
||
|
||
def _filter_unicode(self, text: str) -> str:
|
||
new_chars = []
|
||
for char in text:
|
||
if self._replace_wide_unicode and self._is_wide(char):
|
||
new_chars.append(self._unicode_placeholder)
|
||
elif unicodedata.category(char) in self._replace_character_categories:
|
||
pass
|
||
else:
|
||
new_chars.append(char)
|
||
|
||
return "".join(new_chars)
|
||
|
||
def _render_meta(self, message: Message) -> AttributedText:
|
||
elements = [self.TIME_FORMAT]
|
||
|
||
if self._show_year:
|
||
elements.insert(0, self.YEAR_FORMAT)
|
||
|
||
if self._show_seconds:
|
||
elements.append(self.SECOND_FORMAT)
|
||
|
||
text = message.timestamp.strftime("".join(elements))
|
||
return AT(text, attributes=self._meta_attrs) + AT(" ")
|
||
|
||
def render_element(self, message: Message, width: int) -> RenderedMessage:
|
||
meta = self._render_meta(message)
|
||
|
||
if yaboli.similar(self.nick, message.nick):
|
||
nick_attrs = self._own_nick_attrs
|
||
else:
|
||
nick_attrs = self._nick_attrs
|
||
|
||
left = AT(self._surround_left, attributes=self._surround_attrs)
|
||
nick = AT(self._filter_unicode(message.nick),
|
||
attributes=nick_attrs)
|
||
right = AT(self._surround_right, attributes=self._surround_attrs)
|
||
|
||
nick_str = left + nick + right + AT(" ")
|
||
nick_spaces = AT(" " * len(nick))
|
||
|
||
content = self._filter_unicode(message.content)
|
||
lines = []
|
||
for i, line in enumerate(content.split("\n")):
|
||
text = (nick_str if i == 0 else nick_spaces) + AT(line)
|
||
lines.append(text)
|
||
|
||
return RenderedMessage(message.id, lines, meta)
|
||
|
||
def render_cursor(self, width: int) -> AttributedText:
|
||
left = AT(self._cursor_surround_left,
|
||
attributes=self._cursor_surround_attrs)
|
||
nick = AT(self._filter_unicode(self.nick),
|
||
attributes=self._cursor_own_nick_attrs)
|
||
right = AT(self._cursor_surround_right,
|
||
attributes=self._cursor_surround_attrs)
|
||
|
||
nick_str = left + nick + right
|
||
|
||
rest_width = max(0, width - len(nick_str))
|
||
rest_str = AT(self._cursor_fill * rest_width,
|
||
attributes=self._cursor_fill_attrs)
|
||
|
||
return nick_str + rest_str
|