Commit 64c57dbc authored by Jacek Sowiński's avatar Jacek Sowiński

Add %S into list_format which is truncated %s

If %S is used in list_format, PrettyPrinterFormatFilter will now check
if final result fits on the screen. If not, it will truncate it
accordingly and only then return.

© by @MinchinWeb for `topydo.lib.Utils.get_terminal_size`
parent 96383bc2
...@@ -18,11 +18,13 @@ ...@@ -18,11 +18,13 @@
import re import re
from collections import OrderedDict
from six import u from six import u
from topydo.lib.Colors import NEUTRAL_COLOR, Colors from topydo.lib.Colors import NEUTRAL_COLOR, Colors
from topydo.lib.Config import config from topydo.lib.Config import config
from topydo.lib.ListFormat import filler, humanize_date, humanize_dates from topydo.lib.ListFormat import filler, humanize_date, humanize_dates
from topydo.lib.Utils import get_terminal_size
class PrettyPrinterFilter(object): class PrettyPrinterFilter(object):
...@@ -135,59 +137,62 @@ class PrettyPrinterFormatFilter(PrettyPrinterFilter): ...@@ -135,59 +137,62 @@ class PrettyPrinterFormatFilter(PrettyPrinterFilter):
self.format = p_format or config().list_format() self.format = p_format or config().list_format()
def filter(self, p_todo_str, p_todo): def filter(self, p_todo_str, p_todo):
placeholders = { placeholders = OrderedDict()
# absolute creation date # absolute creation date
'c': lambda t: t.creation_date().isoformat() if t.creation_date() else '', placeholders['c'] = lambda t: t.creation_date().isoformat() if t.creation_date() else ''
# relative creation date # relative creation date
'C': lambda t: humanize_date(t.creation_date()) if t.creation_date() else '', placeholders['C'] = lambda t: humanize_date(t.creation_date()) if t.creation_date() else ''
# absolute due date # absolute due date
'd': lambda t: t.due_date().isoformat() if t.due_date() else '', placeholders['d'] = lambda t: t.due_date().isoformat() if t.due_date() else ''
# relative due date # relative due date
'D': lambda t: humanize_date(t.due_date()) if t.due_date() else '', placeholders['D'] = lambda t: humanize_date(t.due_date()) if t.due_date() else ''
# relative dates: due, start # relative dates: due, start
'h': lambda t: humanize_dates(t.due_date(), t.start_date()), placeholders['h'] = lambda t: humanize_dates(t.due_date(), t.start_date())
# relative dates in form: creation, due, start # relative dates in form: creation, due, start
'H': lambda t: humanize_dates(t.due_date(), t.start_date(), t.creation_date()), placeholders['H'] = lambda t: humanize_dates(t.due_date(), t.start_date(), t.creation_date())
# todo ID # todo ID
'i': lambda t: str(self.todolist.number(t)), placeholders['i'] = lambda t: str(self.todolist.number(t))
# todo ID pre-filled with 1 or 2 spaces if its length is <3 # todo ID pre-filled with 1 or 2 spaces if its length is <3
'I': lambda t: filler(str(self.todolist.number(t)), 3), placeholders['I'] = lambda t: filler(str(self.todolist.number(t)), 3)
# list of tags (spaces) without due: and t: # list of tags (spaces) without due: and t:
'k': lambda t: ' '.join([u('{}:{}').format(tag, value) placeholders['k'] = lambda t: ' '.join([u('{}:{}').format(tag, value)
for tag, value in sorted(p_todo.tags()) if for tag, value in sorted(p_todo.tags()) if
tag not in config().hidden_tags() + [config().tag_start(), config().tag_due()]]), tag not in config().hidden_tags() + [config().tag_start(), config().tag_due()]])
# list of tags (spaces) # list of tags (spaces)
'K': lambda t: ' '.join([u('{}:{}').format(tag, value) placeholders['K'] = lambda t: ' '.join([u('{}:{}').format(tag, value)
for tag, value in sorted(p_todo.tags()) if for tag, value in sorted(p_todo.tags()) if
tag not in config().hidden_tags()]), tag not in config().hidden_tags()])
# priority # priority
'p': lambda t: t.priority() if t.priority() else '', placeholders['p'] = lambda t: t.priority() if t.priority() else ''
# text # text
's': lambda t: t.text(), placeholders['s'] = lambda t: t.text()
# absolute start date # absolute start date
't': lambda t: t.start_date().isoformat() if t.start_date() else '', placeholders['t'] = lambda t: t.start_date().isoformat() if t.start_date() else ''
# relative start date # relative start date
'T': lambda t: humanize_date(t.start_date()) if t.start_date() else '', placeholders['T'] = lambda t: humanize_date(t.start_date()) if t.start_date() else ''
# completed # completed
'x': lambda t: 'x ' + t.completion_date().isoformat() if t.is_completed() else '', placeholders['x'] = lambda t: 'x ' + t.completion_date().isoformat() if t.is_completed() else ''
# literal %
placeholders['%'] = lambda _: '%'
# text (truncated if necessary)
placeholders['S'] = lambda t: t.text()
# literal %
'%': lambda _: '%',
}
p_todo_str = self.format p_todo_str = self.format
...@@ -225,6 +230,13 @@ class PrettyPrinterFormatFilter(PrettyPrinterFilter): ...@@ -225,6 +230,13 @@ class PrettyPrinterFormatFilter(PrettyPrinterFilter):
p_todo_str = re.sub(pattern, strip_braces, p_todo_str) p_todo_str = re.sub(pattern, strip_braces, p_todo_str)
p_todo_str = re.sub(r'%{}'.format(placeholder), repl, p_todo_str) p_todo_str = re.sub(r'%{}'.format(placeholder), repl, p_todo_str)
p_todo_str = p_todo_str.rstrip()
return p_todo_str.rstrip() if placeholder == 'S':
line_width = get_terminal_size().columns -1
if len(p_todo_str) > line_width:
text_lim = line_width - len(p_todo_str) - 3
p_todo_str = re.sub(re.escape(repl), repl[:text_lim] + '...', p_todo_str)
return p_todo_str
...@@ -19,6 +19,8 @@ Various utility functions. ...@@ -19,6 +19,8 @@ Various utility functions.
""" """
import re import re
from collections import namedtuple
from datetime import date from datetime import date
...@@ -51,3 +53,25 @@ def escape_ansi(p_string): ...@@ -51,3 +53,25 @@ def escape_ansi(p_string):
return escape_ansi.pattern.sub('', p_string) return escape_ansi.pattern.sub('', p_string)
escape_ansi.pattern = re.compile(r'\x1b[^m]*m') escape_ansi.pattern = re.compile(r'\x1b[^m]*m')
def get_terminal_size():
"""
Try to determine terminal size at run time. If that is not possible,
returns the default size of 80x24.
"""
try:
from shutil import get_terminal_size # pylint: disable=no-name-in-module
except ImportError:
from backports.shutil_get_terminal_size import get_terminal_size
try:
sz = get_terminal_size()
except ValueError:
"""
This can result from the 'underlying buffer being detached', which
occurs during running the unittest on Windows (but not on Linux?)
"""
terminal_size = namedtuple('Terminal_Size', 'columns lines')
sz = terminal_size((80, 24))
return sz
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment