diff --git a/pyproject.toml b/pyproject.toml index bf268c3b..423e8d46 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -33,6 +33,7 @@ dependencies = [ "parsedatetime", "python-dateutil", "pyxdg", + "tabulate", "urwid", ] dynamic = ["version"] diff --git a/todoman/cli.py b/todoman/cli.py index 478ea44c..e0ea2de3 100644 --- a/todoman/cli.py +++ b/todoman/cli.py @@ -211,6 +211,7 @@ def __init__(self): @cached_property def ui_formatter(self): return formatters.DefaultFormatter( + self.config["tableformat"], self.config["date_format"], self.config["time_format"], self.config["dt_separator"], @@ -219,6 +220,7 @@ def ui_formatter(self): @cached_property def formatter(self): return self.formatter_class( + self.config["tableformat"], self.config["date_format"], self.config["time_format"], self.config["dt_separator"], diff --git a/todoman/configuration.py b/todoman/configuration.py index 3ca4129b..17e50e39 100644 --- a/todoman/configuration.py +++ b/todoman/configuration.py @@ -24,6 +24,9 @@ def validate_cache_path(path: str) -> str: return expand_path(path) +def validate_tableformat(tablefmt: str) -> str: + return tablefmt + def validate_date_format(fmt: str) -> str: if any(x in fmt for x in ("%H", "%M", "%S", "%X")): raise ConfigurationError( @@ -74,6 +77,15 @@ class ConfigEntry(NamedTuple): files) will be treated as a list.""", expand_path, ), + ConfigEntry( + "tableformat", + str, + "plain", + """ +The name of a TableFormat a la tabulate which will be used to display +any tabular data.""", + validate_tableformat, + ), ConfigEntry( "color", str, diff --git a/todoman/formatters.py b/todoman/formatters.py index b28b6e81..c3240120 100644 --- a/todoman/formatters.py +++ b/todoman/formatters.py @@ -8,6 +8,7 @@ from datetime import datetime from datetime import timedelta from datetime import tzinfo +from tabulate import tabulate from time import mktime from typing import Iterable @@ -73,11 +74,13 @@ def format_database(self, database: TodoList) -> str: class DefaultFormatter(Formatter): def __init__( self, + tablefmt: str = "plain", date_format: str = "%Y-%m-%d", time_format: str = "%H:%M", dt_separator: str = " ", tz_override: tzinfo | None = None, ) -> None: + self.tablefmt = tablefmt self.date_format = date_format self.time_format = time_format self.dt_separator = dt_separator @@ -126,24 +129,23 @@ def compact_multiple(self, todos: Iterable[Todo], hide_list: bool = False) -> st recurring = "⟳" if todo.is_recurring else "" - if hide_list: - summary = f"{todo.summary} {percent}" - else: + summary = todo.summary + if not hide_list: if not todo.list: raise ValueError("Cannot format todo without a list") - summary = f"{todo.summary} {self.format_database(todo.list)}{percent}" - - # TODO: add spaces on the left based on max todos" + summary = f"{summary} {self.format_database(todo.list)}" - # FIXME: double space when no priority - # split into parts to satisfy linter line too long - table.append( - f"[{completed}] {todo.id} {priority} {due} " - f"{recurring}{summary}{categories}" - ) + table.append([ + f"[{completed}]", + todo.id, + priority, + f"{due}{recurring}", + percent, + f"{summary}{categories}" + ]) - return "\n".join(table) + return tabulate(table, tablefmt=self.tablefmt) def _due_colour(self, todo: Todo) -> str: now = self.now if isinstance(todo.due, datetime) else self.now.date()