From 5ee61bf22a4dc2619261f2122189fc80ad5fff83 Mon Sep 17 00:00:00 2001 From: Mischa Salle Date: Mon, 20 Jan 2025 13:14:01 +0100 Subject: [PATCH 1/4] 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 | 12 +++++------ gcalcli/gcal.py | 46 ++++++++++++++++++++++++------------------- 2 files changed, 31 insertions(+), 27 deletions(-) diff --git a/gcalcli/argparsers.py b/gcalcli/argparsers.py index 5baadcec..d832b574 100644 --- a/gcalcli/argparsers.py +++ b/gcalcli/argparsers.py @@ -66,9 +66,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 @@ -100,9 +101,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_output_parser(parents=[]): @@ -116,9 +115,8 @@ def get_output_parser(parents=[]): output_parser.add_argument( '--nodeclined', action='store_true', dest='ignore_declined', 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', + '--width', '-w', default=get_auto_width(), dest='width', type=validwidth, help='Set output width') has_24_hours = locale_has_24_hours() output_parser.add_argument( diff --git a/gcalcli/gcal.py b/gcalcli/gcal.py index 6cce704c..1c938d31 100644 --- a/gcalcli/gcal.py +++ b/gcalcli/gcal.py @@ -68,10 +68,16 @@ def __init__(self, cal_names=(), printer=PRINTER, **options): self.cals = [] self.printer = printer self.options = options + 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 + self._get_cached() self._select_cals(cal_names) @@ -340,7 +346,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'] ) ) @@ -406,7 +412,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): @@ -417,7 +423,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])) @@ -435,11 +441,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: @@ -455,7 +461,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)] @@ -472,7 +478,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 @@ -480,7 +486,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) @@ -498,7 +504,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) @@ -533,7 +539,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) @@ -566,7 +572,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, @@ -626,23 +632,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' @@ -794,7 +800,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 ) ) + @@ -804,7 +810,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 ) ) + @@ -819,7 +825,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, From 53b3a6517e94c42df0bd823cc2f26adba6a14e0b Mon Sep 17 00:00:00 2001 From: Mischa Salle Date: Tue, 21 Jan 2025 18:16:48 +0100 Subject: [PATCH 2/4] Fix tests for new width behaviour --- tests/test_argparsers.py | 10 +++++----- tests/test_gcalcli.py | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/test_argparsers.py b/tests/test_argparsers.py index 03ab5562..aa462ac2 100644 --- a/tests/test_argparsers.py +++ b/tests/test_argparsers.py @@ -30,23 +30,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 dddf86e9..a0855b5c 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') @@ -296,13 +296,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) From 6fdafb5bdbec86879ec60916ea50949e9e1e1093 Mon Sep 17 00:00:00 2001 From: Mischa Salle Date: Mon, 27 Jan 2025 13:48:01 +0100 Subject: [PATCH 3/4] Fix codespell and tox:lint errors --- gcalcli/argparsers.py | 4 +++- gcalcli/gcal.py | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/gcalcli/argparsers.py b/gcalcli/argparsers.py index d832b574..1d1cca0b 100644 --- a/gcalcli/argparsers.py +++ b/gcalcli/argparsers.py @@ -69,7 +69,9 @@ def validwidth(value): minwidth=30 ival = int(value) if ival < minwidth: - raise argparse.ArgumentTypeError(f'Width must be a number >= {minwidth}') + raise argparse.ArgumentTypeError( + 'Width must be a number >= %d' % minwidth + ) return ival diff --git a/gcalcli/gcal.py b/gcalcli/gcal.py index 1c938d31..671f2d7b 100644 --- a/gcalcli/gcal.py +++ b/gcalcli/gcal.py @@ -75,7 +75,7 @@ def __init__(self, cal_names=(), printer=PRINTER, **options): # 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 + # Minimal day table cell is 10 self.width['day'] = day_width if day_width > 9 else 10 self._get_cached() From 9c7518290dbf751ad8765b3b2c5415a8f6270a64 Mon Sep 17 00:00:00 2001 From: Mischa Salle Date: Wed, 29 Jan 2025 11:24:17 +0100 Subject: [PATCH 4/4] Fix width for noweekend option day_width was calculated assuming 7 days a week. This is not correct when noweekend is set. days was already calculated in _GraphEvents, so move it to instance level and set it in __init__ and use it to calculate the correct width. --- gcalcli/gcal.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/gcalcli/gcal.py b/gcalcli/gcal.py index 671f2d7b..a85ba510 100644 --- a/gcalcli/gcal.py +++ b/gcalcli/gcal.py @@ -72,9 +72,11 @@ def __init__(self, cal_names=(), printer=PRINTER, **options): self.details = options.get('details', {}) + # Number of days to print in calw/calm + self.days = 7 if self.options.get('cal_weekend', True) else 5 # 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) + day_width = int(( self.width['cal'] - self.days - 1) / self.days) # Minimal day table cell is 10 self.width['day'] = day_width if day_width > 9 else 10 @@ -462,16 +464,17 @@ def _GraphEvents(self, cmd, start_datetime, count, event_list): event_list = event_list[1:] 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)] + day_names = [date(2001, 1, i + 1).strftime('%A') + for i in range(self.days)] if not self.options['cal_monday'] or not self.options['cal_weekend']: day_names = day_names[6:] + day_names[:6] def build_divider(left, center, right): return ( self.printer.art[left] + day_width_line + - ((days - 1) * (self.printer.art[center] + day_width_line)) + + ( (self.days - 1) * (self.printer.art[center] + + day_width_line) ) + self.printer.art[right] ) @@ -486,7 +489,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.width['day'] * days) + (days - 1) + month_width = (self.width['day'] * self.days) + (self.days - 1) month_title += ' ' * (month_width - self._printed_len(month_title)) self.printer.art_msg('vrt', color_border) @@ -521,7 +524,7 @@ def build_divider(left, center, right): for i in range(count): # create and print the date line for a week - for j in range(days): + for j in range(self.days): if cmd == 'calw': d = (start_week_datetime + timedelta(days=j)).strftime('%d %b') @@ -561,7 +564,7 @@ def build_divider(left, center, right): # stop when everything has been printed done = True self.printer.art_msg('vrt', color_border) - for j in range(days): + for j in range(self.days): if not week_events[j]: # no events today self.printer.msg(