From 4602e53db1c958a5da2abf5aaa0b957dcd0846cc Mon Sep 17 00:00:00 2001 From: Mischa Salle Date: Tue, 21 Jan 2025 20:17:44 +0200 Subject: [PATCH] Fix width argument, set overall width Fix the width for the description details, both default and as cmdline option, and make sure we don't break the width of the calw and calm output. The (pretty much undocumented) option --width/-w option was used for setting the width of a day table cell in calw and calm but seemed to also be intended for setting the overall width of the description in agenda. The latter part was broken and the details description width would always default to 80. We change the behaviour to set the overall width of the table, defaulting to the terminal width, both for calw and calm and for description details. The width of the day cell is calculated from the overall width. --- gcalcli/argparsers.py | 14 ++++++------ gcalcli/gcal.py | 46 +++++++++++++++++++++++----------------- tests/test_argparsers.py | 10 ++++----- tests/test_gcalcli.py | 10 ++++----- 4 files changed, 42 insertions(+), 38 deletions(-) diff --git a/gcalcli/argparsers.py b/gcalcli/argparsers.py index 3e84cc01..0326d57f 100644 --- a/gcalcli/argparsers.py +++ b/gcalcli/argparsers.py @@ -108,9 +108,10 @@ def __call__(self, parser, namespace, value, option_string=None): def validwidth(value): + minwidth=30 ival = int(value) - if ival < 10: - raise argparse.ArgumentTypeError('Width must be a number >= 10') + if ival < minwidth: + raise argparse.ArgumentTypeError(f'Width must be a number >= {minwidth}') return ival @@ -146,9 +147,7 @@ def locale_has_24_hours(): def get_auto_width(): - console_width = get_terminal_size().columns - day_width = int((console_width - 8) / 7) - return day_width if day_width > 9 else 10 + return get_terminal_size().columns def get_calendars_parser(nargs_multiple: bool) -> argparse.ArgumentParser: @@ -204,12 +203,11 @@ def get_output_parser(parents=[]): default=False, help='Hide events that have been declined', ) - auto_width = get_auto_width() output_parser.add_argument( '--width', '-w', - default=auto_width, - dest='cal_width', + default=get_auto_width(), + dest='width', type=validwidth, help='Set output width', ) diff --git a/gcalcli/gcal.py b/gcalcli/gcal.py index 3c8619d9..514e9604 100644 --- a/gcalcli/gcal.py +++ b/gcalcli/gcal.py @@ -79,10 +79,16 @@ def __init__( self.printer = printer self.options = options self.userless_mode = userless_mode + self.width = {} self.details = options.get('details', {}) - # stored as detail, but provided as option: TODO: fix that - self.details['width'] = options.get('width', 80) + + # Store overall calendar width and width for day table cells + self.width['cal'] = int(options.get('width', 80)) + day_width = int(( self.width['cal'] - 8) / 7) + # Mimimal day table cell is 10 + self.width['day'] = day_width if day_width > 9 else 10 + if self.userless_mode: print( "Running in GCALCLI_USERLESS_MODE. Most operations will fail!", @@ -463,7 +469,7 @@ def _get_week_events(self, start_dt, end_dt, event_list): days_since_epoch(event['s'])): week_events[event_daynum].append( EventTitle( - '\n' + self.options['cal_width'] * '-', + '\n' + self.width['day'] * '-', self.options['color_now_marker'] ) ) @@ -526,7 +532,7 @@ def _word_cut(self, word): stop = 0 for i, char in enumerate(word): stop += self._printed_len(char) - if stop >= self.options['cal_width']: + if stop >= self.width['day']: return stop, i + 1 def _next_cut(self, string): @@ -537,7 +543,7 @@ def _next_cut(self, string): for i, word in enumerate(words): word_lens.append(self._printed_len(word)) - if (word_lens[-1] + print_len) >= self.options['cal_width']: + if (word_lens[-1] + print_len) >= self.width['day']: # this many words is too many, try to cut at the prev word cut_idx = len(' '.join(words[:i])) @@ -555,11 +561,11 @@ def _get_cut_index(self, event_string): # newline in string is a special case idx = event_string.find('\n') - if idx > -1 and idx <= self.options['cal_width']: + if idx > -1 and idx <= self.width['day']: return (self._printed_len(event_string[:idx]), len(event_string[:idx])) - if print_len <= self.options['cal_width']: + if print_len <= self.width['day']: return (print_len, len(event_string)) else: @@ -575,7 +581,7 @@ def _GraphEvents(self, cmd, start_datetime, count, event_list): while (len(event_list) and event_list[0]['s'] < start_datetime): event_list = event_list[1:] - day_width_line = self.options['cal_width'] * self.printer.art['hrz'] + day_width_line = self.width['day'] * self.printer.art['hrz'] days = 7 if self.options['cal_weekend'] else 5 # Get the localized day names... January 1, 2001 was a Monday day_names = [date(2001, 1, i + 1).strftime('%A') for i in range(days)] @@ -593,7 +599,7 @@ def build_divider(left, center, right): week_top = build_divider('ulc', 'ute', 'urc') week_divider = build_divider('lte', 'crs', 'rte') week_bottom = build_divider('llc', 'bte', 'lrc') - empty_day = self.options['cal_width'] * ' ' + empty_day = self.width['day'] * ' ' if cmd == 'calm': # month titlebar @@ -601,7 +607,7 @@ def build_divider(left, center, right): self.printer.msg(month_title_top + '\n', color_border) month_title = start_datetime.strftime('%B %Y') - month_width = (self.options['cal_width'] * days) + (days - 1) + month_width = (self.width['day'] * days) + (days - 1) month_title += ' ' * (month_width - self._printed_len(month_title)) self.printer.art_msg('vrt', color_border) @@ -619,7 +625,7 @@ def build_divider(left, center, right): self.printer.art_msg('vrt', color_border) for day_name in day_names: day_name += ' ' * ( - self.options['cal_width'] - self._printed_len(day_name) + self.width['day'] - self._printed_len(day_name) ) self.printer.msg(day_name, self.options['color_date']) self.printer.art_msg('vrt', color_border) @@ -654,7 +660,7 @@ def build_divider(left, center, right): tmp_date_color = self.options['color_now_marker'] d += ' **' - d += ' ' * (self.options['cal_width'] - self._printed_len(d)) + d += ' ' * (self.width['day'] - self._printed_len(d)) # print dates self.printer.art_msg('vrt', color_border) @@ -687,7 +693,7 @@ def build_divider(left, center, right): curr_event = week_events[j][0] print_len, cut_idx = self._get_cut_index(curr_event.title) - padding = ' ' * (self.options['cal_width'] - print_len) + padding = ' ' * (self.width['day'] - print_len) self.printer.msg( curr_event.title[:cut_idx] + padding, @@ -747,23 +753,23 @@ def _format_descr(descr, indent, box): if box: wrapper.initial_indent = (indent + ' ') wrapper.subsequent_indent = (indent + ' ') - wrapper.width = (self.details.get('width') - 2) + wrapper.width = (self.width['cal'] - 2) else: wrapper.initial_indent = indent wrapper.subsequent_indent = indent - wrapper.width = self.details.get('width') + wrapper.width = self.width['cal'] new_descr = '' for line in descr.split('\n'): if box: tmp_line = wrapper.fill(line) for single_line in tmp_line.split('\n'): single_line = single_line.ljust( - self.details.get('width'), ' ' + self.width['cal'], ' ' ) new_descr += single_line[:len(indent)] + \ self.printer.art['vrt'] + \ single_line[(len(indent) + 1): - (self.details.get('width') - 1)] + \ + (self.width['cal'] - 1)] + \ self.printer.art['vrt'] + '\n' else: new_descr += wrapper.fill(line) + '\n' @@ -915,7 +921,7 @@ def _format_descr(descr, indent, box): descr_indent + self.printer.art['ulc'] + (self.printer.art['hrz'] * - ((self.details.get('width') - len(descr_indent)) + ((self.width['cal'] - len(descr_indent)) - 2 ) ) + @@ -925,7 +931,7 @@ def _format_descr(descr, indent, box): descr_indent + self.printer.art['llc'] + (self.printer.art['hrz'] * - ((self.details.get('width') - len(descr_indent)) + ((self.width['cal'] - len(descr_indent)) - 2 ) ) + @@ -940,7 +946,7 @@ def _format_descr(descr, indent, box): ) else: marker = descr_indent + '-' * \ - (self.details.get('width') - len(descr_indent)) + (self.width['cal'] - len(descr_indent)) xstr = '%s Description:\n%s\n%s\n%s\n' % ( details_indent, marker, diff --git a/tests/test_argparsers.py b/tests/test_argparsers.py index b4b90789..35994b3a 100644 --- a/tests/test_argparsers.py +++ b/tests/test_argparsers.py @@ -32,23 +32,23 @@ def fake_get_terminal_size(): return fake_get_terminal_size output_parser = argparsers.get_output_parser() - argv = shlex.split('-w 9') + argv = shlex.split('-w 29') with pytest.raises(SystemExit): output_parser.parse_args(argv) - argv = shlex.split('-w 10') - assert output_parser.parse_args(argv).cal_width == 10 + argv = shlex.split('-w 30') + assert output_parser.parse_args(argv).width == 30 argv = shlex.split('') monkeypatch.setattr(argparsers, 'get_terminal_size', sub_terminal_size(70)) output_parser = argparsers.get_output_parser() - assert output_parser.parse_args(argv).cal_width == 10 + assert output_parser.parse_args(argv).width == 70 argv = shlex.split('') monkeypatch.setattr(argparsers, 'get_terminal_size', sub_terminal_size(100)) output_parser = argparsers.get_output_parser() - assert output_parser.parse_args(argv).cal_width == 13 + assert output_parser.parse_args(argv).width == 100 def test_search_parser(): diff --git a/tests/test_gcalcli.py b/tests/test_gcalcli.py index 29f67afe..abc6ac2d 100644 --- a/tests/test_gcalcli.py +++ b/tests/test_gcalcli.py @@ -86,7 +86,7 @@ def test_cal_query(capsys, PatchedGCalI): art = gcal.printer.art expect_top = ( gcal.printer.colors[gcal.options['color_border']] + art['ulc'] + - art['hrz'] * gcal.options['cal_width']) + art['hrz'] * gcal.width['day']) assert captured.out.startswith(expect_top) gcal.CalQuery('calm') @@ -348,13 +348,13 @@ def test_iterate_events(capsys, PatchedGCalI): def test_next_cut(PatchedGCalI): gcal = PatchedGCalI() # default width is 10 - test_cal_width = 10 - gcal.options['cal_width'] = test_cal_width + test_day_width = 10 + gcal.width['day'] = test_day_width event_title = "first looooong" assert gcal._next_cut(event_title) == (5, 5) - event_title = "tooooooloooong" - assert gcal._next_cut(event_title) == (test_cal_width, test_cal_width) + event_title = "tooooooooooooooooooooooooloooooooooong" + assert gcal._next_cut(event_title) == (test_day_width, test_day_width) event_title = "one two three four" assert gcal._next_cut(event_title) == (7, 7)