From dd74b14d76de6c29f1ea37ad239aa3092b7e4d6b Mon Sep 17 00:00:00 2001 From: Suhas Date: Sat, 10 Apr 2021 19:49:56 -0400 Subject: [PATCH] More graceful handling of low linewrap values (#1219) * behavior outline * enforce positive initial linewrap Check column widths update gitignore throw error when linewrap too small simply check for large enough linewrap value * delete unused error message * PR feedback make exception more informative update check_linewrap signature in src and test make check_linewrap a free function * delete unused function * delete else..pass block * newline for make format --- features/format.feature | 16 ++++++++++++++++ features/steps/export_steps.py | 8 ++++++++ jrnl/exception.py | 10 +++++++++- jrnl/plugins/fancy_exporter.py | 19 ++++++++++++++++++- tests/test_export.py | 28 ++++++++++++++++++++++++++++ 5 files changed, 79 insertions(+), 2 deletions(-) create mode 100644 tests/test_export.py diff --git a/features/format.feature b/features/format.feature index 7bdaac4d..f935e8c8 100644 --- a/features/format.feature +++ b/features/format.feature @@ -323,6 +323,22 @@ Feature: Custom formats | basic_folder | | basic_dayone | + + + Scenario Outline: Export fancy with small linewrap + Given we use the config ".yaml" + And we use the password "test" if prompted + When we run "jrnl --config-override linewrap 35 --format fancy -3" + Then we should get no error + And the output should be 35 columns wide + + Examples: configs + | config | + | basic_onefile | + | basic_encrypted | + | basic_folder | + | basic_dayone | + @todo Scenario Outline: Exporting fancy # Needs better emoji support diff --git a/features/steps/export_steps.py b/features/steps/export_steps.py index 3df86237..8141dc36 100644 --- a/features/steps/export_steps.py +++ b/features/steps/export_steps.py @@ -12,6 +12,14 @@ from behave import given from behave import then +@then("the output should be {width:d} columns wide") +def check_output_width(context, width): + out = context.stdout_capture.getvalue() + out_lines = out.splitlines() + for line in out_lines: + assert len(line) <= width + + @then("the output should be parsable as json") def check_output_json(context): out = context.stdout_capture.getvalue() diff --git a/jrnl/exception.py b/jrnl/exception.py index 82a562a0..ac7cd0b2 100644 --- a/jrnl/exception.py +++ b/jrnl/exception.py @@ -30,7 +30,15 @@ class JrnlError(Exception): Removing this file will allow jrnl to save its configuration. """ - ) + ), + "LineWrapTooSmallForDateFormat": textwrap.dedent( + """ + The provided linewrap value of {config_linewrap} is too small by {columns} columns + to display the timestamps in the configured time format for journal {journal}. + + You can avoid this error by specifying a linewrap value that is larger by at least {columns} in the configuration file or by using --config-override at the command line + """ + ), } return error_messages[self.error_type].format(**kwargs) diff --git a/jrnl/plugins/fancy_exporter.py b/jrnl/plugins/fancy_exporter.py index 74cc6958..15efc19b 100644 --- a/jrnl/plugins/fancy_exporter.py +++ b/jrnl/plugins/fancy_exporter.py @@ -3,6 +3,7 @@ # Copyright (C) 2012-2021 jrnl contributors # License: https://www.gnu.org/licenses/gpl-3.0.html +from jrnl.exception import JrnlError from textwrap import TextWrapper from .text_exporter import TextExporter @@ -14,12 +15,14 @@ class FancyExporter(TextExporter): names = ["fancy", "boxed"] extension = "txt" + # Top border of the card border_a = "┎" border_b = "─" border_c = "╮" border_d = "╘" border_e = "═" border_f = "╕" + border_g = "┃" border_h = "│" border_i = "┠" @@ -33,16 +36,19 @@ class FancyExporter(TextExporter): """Returns a fancy unicode representation of a single entry.""" date_str = entry.date.strftime(entry.journal.config["timeformat"]) linewrap = entry.journal.config["linewrap"] or 78 - initial_linewrap = linewrap - len(date_str) - 2 + initial_linewrap = max((1, linewrap - len(date_str) - 2)) body_linewrap = linewrap - 2 card = [ cls.border_a + cls.border_b * (initial_linewrap) + cls.border_c + date_str ] + check_provided_linewrap_viability(linewrap, card, entry.journal) + w = TextWrapper( width=initial_linewrap, initial_indent=cls.border_g + " ", subsequent_indent=cls.border_g + " ", ) + title_lines = w.wrap(entry.title) card.append( title_lines[0].ljust(initial_linewrap + 1) @@ -74,3 +80,14 @@ class FancyExporter(TextExporter): def export_journal(cls, journal): """Returns a unicode representation of an entire journal.""" return "\n".join(cls.export_entry(entry) for entry in journal) + + +def check_provided_linewrap_viability(linewrap, card, journal): + if len(card[0]) > linewrap: + width_violation = len(card[0]) - linewrap + raise JrnlError( + "LineWrapTooSmallForDateFormat", + config_linewrap=linewrap, + columns=width_violation, + journal=journal, + ) diff --git a/tests/test_export.py b/tests/test_export.py new file mode 100644 index 00000000..0f494f79 --- /dev/null +++ b/tests/test_export.py @@ -0,0 +1,28 @@ +from jrnl.exception import JrnlError +from jrnl.plugins.fancy_exporter import check_provided_linewrap_viability + +import pytest + + +@pytest.fixture() +def datestr(): + yield "2020-10-20 16:59" + + +def build_card_header(datestr): + top_left_corner = "┎─╮" + content = top_left_corner + datestr + return content + + +class TestFancy: + def test_too_small_linewrap(self, datestr): + + journal = "test_journal" + content = build_card_header(datestr) + + total_linewrap = 12 + + with pytest.raises(JrnlError) as e: + check_provided_linewrap_viability(total_linewrap, [content], journal) + assert e.value.error_type == "LineWrapTooSmallForDateFormat"