summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormathieui <mathieui@mathieui.net>2019-09-22 17:35:07 +0200
committermathieui <mathieui@mathieui.net>2020-05-09 19:46:17 +0200
commit41127e50abc3e126f953af5ad638f92d0848f9f1 (patch)
treeebc7c7a4557e1d005f2bf5a52b84f460342dca63
parentd22b4b8c218cbbaee62002d751bd69bfe1d1deab (diff)
downloadpoezio-41127e50abc3e126f953af5ad638f92d0848f9f1.tar.gz
poezio-41127e50abc3e126f953af5ad638f92d0848f9f1.tar.bz2
poezio-41127e50abc3e126f953af5ad638f92d0848f9f1.tar.xz
poezio-41127e50abc3e126f953af5ad638f92d0848f9f1.zip
Move message rendering code to Message.render()
Also: - rename format_chars to FORMAT_CHARS because it’s static constant - move Line, Message, and a few funcs/consts to a new poezio.ui module
-rw-r--r--poezio/tabs/basetabs.py8
-rw-r--r--poezio/text_buffer.py86
-rw-r--r--poezio/ui/__init__.py0
-rw-r--r--poezio/ui/consts.py4
-rw-r--r--poezio/ui/funcs.py (renamed from poezio/windows/funcs.py)4
-rw-r--r--poezio/ui/types.py158
-rw-r--r--poezio/windows/base_wins.py5
-rw-r--r--poezio/windows/info_wins.py2
-rw-r--r--poezio/windows/inputs.py11
-rw-r--r--poezio/windows/text_win.py66
-rw-r--r--poezio/xhtml.py2
11 files changed, 185 insertions, 161 deletions
diff --git a/poezio/tabs/basetabs.py b/poezio/tabs/basetabs.py
index fca54860..40868e2f 100644
--- a/poezio/tabs/basetabs.py
+++ b/poezio/tabs/basetabs.py
@@ -31,7 +31,13 @@ from typing import (
TYPE_CHECKING,
)
-from poezio import mam, poopt, timed_events, xhtml, windows
+from poezio import (
+ mam,
+ poopt,
+ timed_events,
+ xhtml,
+ windows
+)
from poezio.core.structs import Command, Completion, Status
from poezio.common import safeJID
from poezio.config import config
diff --git a/poezio/text_buffer.py b/poezio/text_buffer.py
index 2c0d192a..1667f0dc 100644
--- a/poezio/text_buffer.py
+++ b/poezio/text_buffer.py
@@ -14,90 +14,8 @@ log = logging.getLogger(__name__)
from typing import Dict, Union, Optional, List, Tuple
from datetime import datetime
from poezio.config import config
-from poezio.theming import get_theme, dump_tuple
-
-
-class Message:
- __slots__ = ('txt', 'nick_color', 'time', 'str_time', 'nickname', 'user',
- 'identifier', 'top', 'highlight', 'me', 'old_message', 'revisions',
- 'jid', 'ack')
-
- def __init__(self,
- txt: str,
- time: Optional[datetime],
- nickname: Optional[str],
- nick_color: Optional[Tuple],
- history: bool,
- user: Optional[str],
- identifier: Optional[str],
- top: Optional[bool] = False,
- str_time: Optional[str] = None,
- highlight: bool = False,
- old_message: Optional['Message'] = None,
- revisions: int = 0,
- jid: Optional[str] = None,
- ack: int = 0) -> None:
- """
- Create a new Message object with parameters, check for /me messages,
- and delayed messages
- """
- time = time if time is not None else datetime.now()
- if txt.startswith('/me '):
- me = True
- txt = '\x19%s}%s\x19o' % (dump_tuple(get_theme().COLOR_ME_MESSAGE),
- txt[4:])
- else:
- me = False
- str_time = time.strftime("%H:%M:%S")
- if history:
- txt = txt.replace(
- '\x19o',
- '\x19o\x19%s}' % dump_tuple(get_theme().COLOR_LOG_MSG))
- str_time = time.strftime("%Y-%m-%d %H:%M:%S")
-
- self.txt = txt.replace('\t', ' ') + '\x19o'
- self.nick_color = nick_color
- self.time = time
- self.str_time = str_time
- self.nickname = nickname
- self.user = user
- self.identifier = identifier
- self.top = top
- self.highlight = highlight
- self.me = me
- self.old_message = old_message
- self.revisions = revisions
- self.jid = jid
- self.ack = ack
-
- def _other_elems(self) -> str:
- "Helper for the repr_message function"
- acc = []
- fields = list(self.__slots__)
- fields.remove('old_message')
- for field in fields:
- acc.append('%s=%s' % (field, repr(getattr(self, field))))
- return 'Message(%s, %s' % (', '.join(acc), 'old_message=')
-
- def __repr__(self) -> str:
- """
- repr() for the Message class, for debug purposes, since the default
- repr() is recursive, so it can stack overflow given too many revisions
- of a message
- """
- init = self._other_elems()
- acc = [init]
- next_message = self.old_message
- rev = 1
- while next_message is not None:
- acc.append(next_message._other_elems())
- next_message = next_message.old_message
- rev += 1
- acc.append('None')
- while rev:
- acc.append(')')
- rev -= 1
- return ''.join(acc)
+from poezio.ui.types import Message
+
class CorrectionError(Exception):
diff --git a/poezio/ui/__init__.py b/poezio/ui/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/poezio/ui/__init__.py
diff --git a/poezio/ui/consts.py b/poezio/ui/consts.py
new file mode 100644
index 00000000..91f19a82
--- /dev/null
+++ b/poezio/ui/consts.py
@@ -0,0 +1,4 @@
+FORMAT_CHAR = '\x19'
+# These are non-printable chars, so they should never appear in the input,
+# I guess. But maybe we can find better chars that are even less risky.
+FORMAT_CHARS = '\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x1A'
diff --git a/poezio/windows/funcs.py b/poezio/ui/funcs.py
index 22977374..260cc037 100644
--- a/poezio/windows/funcs.py
+++ b/poezio/ui/funcs.py
@@ -4,14 +4,14 @@ Standalone functions used by the modules
import string
from typing import Optional, List
-from poezio.windows.base_wins import FORMAT_CHAR, format_chars
+from poezio.ui.consts import FORMAT_CHAR, FORMAT_CHARS
DIGITS = string.digits + '-'
def find_first_format_char(text: str,
chars: str = None) -> int:
- to_find = chars or format_chars
+ to_find = chars or FORMAT_CHARS
pos = -1
for char in to_find:
p = text.find(char)
diff --git a/poezio/ui/types.py b/poezio/ui/types.py
new file mode 100644
index 00000000..69d77a07
--- /dev/null
+++ b/poezio/ui/types.py
@@ -0,0 +1,158 @@
+
+from datetime import datetime
+from math import ceil, log10
+from typing import Union, Optional, List, Tuple
+
+from poezio.theming import get_theme, dump_tuple
+from poezio.ui.funcs import truncate_nick, parse_attrs
+from poezio import poopt
+from poezio.ui.consts import FORMAT_CHAR
+
+
+class Message:
+ __slots__ = ('txt', 'nick_color', 'time', 'str_time', 'nickname', 'user',
+ 'identifier', 'top', 'highlight', 'me', 'old_message', 'revisions',
+ 'jid', 'ack')
+
+ def __init__(self,
+ txt: str,
+ time: Optional[datetime],
+ nickname: Optional[str],
+ nick_color: Optional[Tuple],
+ history: bool,
+ user: Optional[str],
+ identifier: Optional[str],
+ top: Optional[bool] = False,
+ str_time: Optional[str] = None,
+ highlight: bool = False,
+ old_message: Optional['Message'] = None,
+ revisions: int = 0,
+ jid: Optional[str] = None,
+ ack: int = 0) -> None:
+ """
+ Create a new Message object with parameters, check for /me messages,
+ and delayed messages
+ """
+ time = time if time is not None else datetime.now()
+ if txt.startswith('/me '):
+ me = True
+ txt = '\x19%s}%s\x19o' % (dump_tuple(get_theme().COLOR_ME_MESSAGE),
+ txt[4:])
+ else:
+ me = False
+ str_time = time.strftime("%H:%M:%S")
+ if history:
+ txt = txt.replace(
+ '\x19o',
+ '\x19o\x19%s}' % dump_tuple(get_theme().COLOR_LOG_MSG))
+ str_time = time.strftime("%Y-%m-%d %H:%M:%S")
+
+ self.txt = txt.replace('\t', ' ') + '\x19o'
+ self.nick_color = nick_color
+ self.time = time
+ self.str_time = str_time
+ self.nickname = nickname
+ self.user = user
+ self.identifier = identifier
+ self.top = top
+ self.highlight = highlight
+ self.me = me
+ self.old_message = old_message
+ self.revisions = revisions
+ self.jid = jid
+ self.ack = ack
+
+ def _other_elems(self) -> str:
+ "Helper for the repr_message function"
+ acc = []
+ fields = list(self.__slots__)
+ fields.remove('old_message')
+ for field in fields:
+ acc.append('%s=%s' % (field, repr(getattr(self, field))))
+ return 'Message(%s, %s' % (', '.join(acc), 'old_message=')
+
+ def __repr__(self) -> str:
+ """
+ repr() for the Message class, for debug purposes, since the default
+ repr() is recursive, so it can stack overflow given too many revisions
+ of a message
+ """
+ init = self._other_elems()
+ acc = [init]
+ next_message = self.old_message
+ rev = 1
+ while next_message is not None:
+ acc.append(next_message._other_elems())
+ next_message = next_message.old_message
+ rev += 1
+ acc.append('None')
+ while rev:
+ acc.append(')')
+ rev -= 1
+ return ''.join(acc)
+
+ def render(self, width: int, timestamp: bool = False, nick_size: int = 10) -> List["Line"]:
+ """
+ Build a list of lines from this message.
+ """
+ txt = self.txt
+ if not txt:
+ return []
+ theme = get_theme()
+ if len(self.str_time) > 8:
+ default_color = (
+ FORMAT_CHAR + dump_tuple(theme.COLOR_LOG_MSG) + '}') # type: Optional[str]
+ else:
+ default_color = None
+ ret = [] # type: List[Union[None, Line]]
+ nick = truncate_nick(self.nickname, nick_size)
+ offset = 0
+ if self.ack:
+ if self.ack > 0:
+ offset += poopt.wcswidth(theme.CHAR_ACK_RECEIVED) + 1
+ else:
+ offset += poopt.wcswidth(theme.CHAR_NACK) + 1
+ if nick:
+ offset += poopt.wcswidth(nick) + 2 # + nick + '> ' length
+ if self.revisions > 0:
+ offset += ceil(log10(self.revisions + 1))
+ if self.me:
+ offset += 1 # '* ' before and ' ' after
+ if timestamp:
+ if self.str_time:
+ offset += 1 + len(self.str_time)
+ if theme.CHAR_TIME_LEFT and self.str_time:
+ offset += 1
+ if theme.CHAR_TIME_RIGHT and self.str_time:
+ offset += 1
+ lines = poopt.cut_text(txt, width - offset - 1)
+ prepend = default_color if default_color else ''
+ attrs = [] # type: List[str]
+ for line in lines:
+ saved = Line(
+ msg=self,
+ start_pos=line[0],
+ end_pos=line[1],
+ prepend=prepend)
+ attrs = parse_attrs(self.txt[line[0]:line[1]], attrs)
+ if attrs:
+ prepend = FORMAT_CHAR + FORMAT_CHAR.join(attrs)
+ else:
+ if default_color:
+ prepend = default_color
+ else:
+ prepend = ''
+ ret.append(saved)
+ return ret
+
+
+# msg is a reference to the corresponding Message object. text_start and
+# text_end are the position delimiting the text in this line.
+class Line:
+ __slots__ = ('msg', 'start_pos', 'end_pos', 'prepend')
+
+ def __init__(self, msg: Message, start_pos: int, end_pos: int, prepend: str) -> None:
+ self.msg = msg
+ self.start_pos = start_pos
+ self.end_pos = end_pos
+ self.prepend = prepend
diff --git a/poezio/windows/base_wins.py b/poezio/windows/base_wins.py
index ac6b4804..6a689067 100644
--- a/poezio/windows/base_wins.py
+++ b/poezio/windows/base_wins.py
@@ -20,10 +20,7 @@ from typing import Optional, Tuple, TYPE_CHECKING
from poezio.theming import to_curses_attr, read_tuple
-FORMAT_CHAR = '\x19'
-# These are non-printable chars, so they should never appear in the input,
-# I guess. But maybe we can find better chars that are even less risky.
-format_chars = '\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x1A'
+from poezio.ui.consts import FORMAT_CHAR
if TYPE_CHECKING:
from _curses import _CursesWindow # pylint: disable=E0611
diff --git a/poezio/windows/info_wins.py b/poezio/windows/info_wins.py
index 3a8d1863..d31130fe 100644
--- a/poezio/windows/info_wins.py
+++ b/poezio/windows/info_wins.py
@@ -10,7 +10,7 @@ from poezio.common import safeJID
from poezio.config import config
from poezio.windows.base_wins import Win
-from poezio.windows.funcs import truncate_nick
+from poezio.ui.funcs import truncate_nick
from poezio.theming import get_theme, to_curses_attr
diff --git a/poezio/windows/inputs.py b/poezio/windows/inputs.py
index 84b95599..5cca8803 100644
--- a/poezio/windows/inputs.py
+++ b/poezio/windows/inputs.py
@@ -10,8 +10,9 @@ from typing import List, Dict, Callable, Optional
from poezio import keyboard
from poezio import common
from poezio import poopt
-from poezio.windows.base_wins import Win, format_chars
-from poezio.windows.funcs import find_first_format_char
+from poezio.windows.base_wins import Win
+from poezio.ui.consts import FORMAT_CHARS
+from poezio.ui.funcs import find_first_format_char
from poezio.config import config
from poezio.theming import to_curses_attr
@@ -487,7 +488,7 @@ class Input(Win):
(\x0E to \x19 instead of \x19 + attr). We do not use any }
char in this version
"""
- chars = format_chars + '\n'
+ chars = FORMAT_CHARS + '\n'
if y is not None and x is not None:
self.move(y, x)
format_char = find_first_format_char(text, chars)
@@ -497,7 +498,7 @@ class Input(Win):
if text[format_char] == '\n':
attr_char = '|'
else:
- attr_char = self.text_attributes[format_chars.index(
+ attr_char = self.text_attributes[FORMAT_CHARS.index(
text[format_char])]
self.addstr(text[:format_char])
self.addstr(attr_char, curses.A_REVERSE)
@@ -696,7 +697,7 @@ class MessageInput(HistoryInput):
def cb(attr_char):
if attr_char in self.text_attributes:
- char = format_chars[self.text_attributes.index(attr_char)]
+ char = FORMAT_CHARS[self.text_attributes.index(attr_char)]
self.do_command(char, False)
self.rewrite_text()
diff --git a/poezio/windows/text_win.py b/poezio/windows/text_win.py
index f4c78c2c..bba66d60 100644
--- a/poezio/windows/text_win.py
+++ b/poezio/windows/text_win.py
@@ -9,28 +9,16 @@ from math import ceil, log10
from typing import Optional, List, Union
from poezio.windows.base_wins import Win, FORMAT_CHAR
-from poezio.windows.funcs import truncate_nick, parse_attrs
+from poezio.ui.funcs import truncate_nick, parse_attrs
from poezio import poopt
from poezio.config import config
from poezio.theming import to_curses_attr, get_theme, dump_tuple
-from poezio.text_buffer import Message
+from poezio.ui.types import Line, Message
log = logging.getLogger(__name__)
-# msg is a reference to the corresponding Message object. text_start and
-# text_end are the position delimiting the text in this line.
-class Line:
- __slots__ = ('msg', 'start_pos', 'end_pos', 'prepend')
-
- def __init__(self, msg: Message, start_pos: int, end_pos: int, prepend: str) -> None:
- self.msg = msg
- self.start_pos = start_pos
- self.end_pos = end_pos
- self.prepend = prepend
-
-
class BaseTextWin(Win):
__slots__ = ('lines_nb_limit', 'pos', 'built_lines', 'lock', 'lock_buffer',
'separator_after')
@@ -360,55 +348,7 @@ class TextWin(BaseTextWin):
"""
if message is None: # line separator
return [None]
- txt = message.txt
- if not txt:
- return []
- theme = get_theme()
- if len(message.str_time) > 8:
- default_color = (
- FORMAT_CHAR + dump_tuple(theme.COLOR_LOG_MSG) + '}') # type: Optional[str]
- else:
- default_color = None
- ret = [] # type: List[Union[None, Line]]
- nick = truncate_nick(message.nickname, nick_size)
- offset = 0
- if message.ack:
- if message.ack > 0:
- offset += poopt.wcswidth(theme.CHAR_ACK_RECEIVED) + 1
- else:
- offset += poopt.wcswidth(theme.CHAR_NACK) + 1
- if nick:
- offset += poopt.wcswidth(nick) + 2 # + nick + '> ' length
- if message.revisions > 0:
- offset += ceil(log10(message.revisions + 1))
- if message.me:
- offset += 1 # '* ' before and ' ' after
- if timestamp:
- if message.str_time:
- offset += 1 + len(message.str_time)
- if theme.CHAR_TIME_LEFT and message.str_time:
- offset += 1
- if theme.CHAR_TIME_RIGHT and message.str_time:
- offset += 1
- lines = poopt.cut_text(txt, self.width - offset - 1)
- prepend = default_color if default_color else ''
- attrs = [] # type: List[str]
- for line in lines:
- saved = Line(
- msg=message,
- start_pos=line[0],
- end_pos=line[1],
- prepend=prepend)
- attrs = parse_attrs(message.txt[line[0]:line[1]], attrs)
- if attrs:
- prepend = FORMAT_CHAR + FORMAT_CHAR.join(attrs)
- else:
- if default_color:
- prepend = default_color
- else:
- prepend = ''
- ret.append(saved)
- return ret
+ return message.render(self.width, timestamp, nick_size)
def refresh(self) -> None:
log.debug('Refresh: %s', self.__class__.__name__)
diff --git a/poezio/xhtml.py b/poezio/xhtml.py
index 899985ef..0b234c29 100644
--- a/poezio/xhtml.py
+++ b/poezio/xhtml.py
@@ -488,7 +488,7 @@ def convert_simple_to_full_colors(text: str) -> str:
a \x19n} formatted one.
"""
# TODO, have a single list of this. This is some sort of
- # duplicate from windows.format_chars
+ # duplicate from ui.consts.FORMAT_CHARS
mapping = str.maketrans({
'\x0E': '\x19b',
'\x0F': '\x19o',