| # SPDX-License-Identifier: GPL-2.0 | |
| import argparse | |
| import datetime | |
| import fnmatch | |
| import os | |
| import time | |
| import _hkml_cli | |
| import _hkml_date | |
| import _hkml_list_cache | |
| import hkml_cache | |
| import hkml_config | |
| import hkml_export | |
| import hkml_forward | |
| import hkml_list | |
| import hkml_open | |
| import hkml_patch | |
| import hkml_reply | |
| import hkml_tag | |
| import hkml_view | |
| import hkml_view_text | |
| import hkml_write | |
| # mails list | |
| def get_mails_view_data(slist): | |
| '''Returns MailsViewData that associated to the slist''' | |
| return slist.data | |
| def mail_item_of_row(slist, row): | |
| line_nr_mail_idx_map = slist.data.list_data.line_nr_mail_idx_map | |
| # in case of cached output reuse, the map is None | |
| if line_nr_mail_idx_map is None: | |
| refresh_list(slist, show_tagged_mails=False) | |
| line_nr_mail_idx_map = slist.data.list_data.line_nr_mail_idx_map | |
| row -= slist.data.list_data.len_comments | |
| if not row in line_nr_mail_idx_map: | |
| return None | |
| mail_idx = line_nr_mail_idx_map[row] | |
| mail_items = slist.data.list_data.mail_items | |
| return mail_items[mail_idx] | |
| def mail_of_row(slist, row): | |
| line_nr_mail_idx_map = slist.data.list_data.line_nr_mail_idx_map | |
| # in case of cached output reuse, the map is None | |
| if line_nr_mail_idx_map is None: | |
| refresh_list(slist, show_tagged_mails=False) | |
| line_nr_mail_idx_map = slist.data.list_data.line_nr_mail_idx_map | |
| row -= slist.data.list_data.len_comments | |
| if not row in line_nr_mail_idx_map: | |
| return None | |
| mail_idx = line_nr_mail_idx_map[row] | |
| mail_items = slist.data.list_data.mail_items | |
| return mail_items[mail_idx].mail | |
| def get_focused_mail_item(slist): | |
| return mail_item_of_row(slist, slist.focus_row) | |
| def get_focused_mail(slist): | |
| return mail_of_row(slist, slist.focus_row) | |
| def focused_mail_idx(slist): | |
| row = slist.focus_row | |
| line_nr_mail_idx_map = slist.data.list_data.line_nr_mail_idx_map | |
| # in case of cached output reuse, the map is None | |
| if line_nr_mail_idx_map is None: | |
| refresh_list(slist, show_tagged_mails=False) | |
| line_nr_mail_idx_map = slist.data.list_data.line_nr_mail_idx_map | |
| row -= slist.data.list_data.len_comments | |
| return line_nr_mail_idx_map[row] | |
| def open_focused_mail(c, slist): | |
| mail_item = get_focused_mail_item(slist) | |
| mail = mail_item.mail | |
| if mail is None: | |
| return | |
| _, cols = slist.screen.getmaxyx() | |
| lines = hkml_open.mail_display_str(mail, cols).split('\n') | |
| cursor_position_cache = slist.data.last_cursor_position | |
| msgid = mail.get_msgid() | |
| if msgid in cursor_position_cache: | |
| cursor_position = cursor_position_cache[msgid] | |
| else: | |
| cursor_position = None | |
| text_view_data = hkml_view_text.TextViewData(mail, slist, mail_item) | |
| text_view_list = hkml_view_text.show_text_viewer( | |
| slist.screen, | |
| args=hkml_view_text.ShowTextViewerArgs( | |
| text_lines=lines, text_view_data=text_view_data, | |
| cursor_position=cursor_position)) | |
| cursor_position_cache[msgid] = [text_view_list.focus_row, | |
| text_view_list.focus_col] | |
| def get_attach_files(): | |
| answer = _hkml_cli.ask_yes_no( | |
| question='Do you want to attach files to the mail?', | |
| default_answer='n') | |
| if answer == 'n': | |
| return [] | |
| files = [] | |
| while True: | |
| file_path = hkml_view.receive_file_path(for_read=True) | |
| if file_path is None: | |
| return [] | |
| files.append(file_path) | |
| print() | |
| answer = _hkml_cli.ask_yes_no( | |
| question='Do you have more files to attach?', | |
| default_answer='n') | |
| if answer != 'y': | |
| break | |
| return files | |
| def suggest_continuing_draft(drafts): | |
| if len(drafts) == 0: | |
| return None | |
| drafts = sorted(drafts, key=lambda d: d.date) | |
| print('you have drafts of subject "%s" written at below dates' % | |
| drafts[0].subject) | |
| print() | |
| for idx, draft_mail in enumerate(drafts): | |
| print('%d. %s' % (idx, draft_mail.date)) | |
| print() | |
| while True: | |
| answer = input(' '.join( | |
| ['Continue writing the draft that written most recently?', | |
| '[Y/n/index of other draft] '])) | |
| if answer.lower() == 'n': | |
| return None | |
| if answer == '': | |
| answer = -1 | |
| try: | |
| return drafts[int(answer)] | |
| except: | |
| print('wrong input...') | |
| pass | |
| return None | |
| def reply_mail(slist, mail, cursor_row=0): | |
| hkml_view.shell_mode_start(slist) | |
| reply_subject = hkml_reply.format_reply_subject(mail) | |
| drafts = hkml_tag.get_mails_of_subject_tag(reply_subject, 'drafts') | |
| draft = suggest_continuing_draft(drafts) | |
| if draft is not None: | |
| hkml_write.write_send_mail( | |
| draft_mail=draft, subject=None, in_reply_to=None, to=None, | |
| cc=None, body=None, attach=None, format_only=None) | |
| else: | |
| files = get_attach_files() | |
| hkml_reply.reply(mail, attach_files=files, format_only=None, | |
| cursor_row=cursor_row) | |
| hkml_view.shell_mode_end(slist) | |
| # this function might be called from text view, with TextView data | |
| if type(slist.data) is hkml_view_text.TextViewData: | |
| if slist.data.mails_slist is not None: | |
| refresh_list(slist.data.mails_slist, show_tagged_mails=True) | |
| elif type(slist.data) is MailsViewData: | |
| refresh_list(slist, show_tagged_mails=True) | |
| def reply_focused_mail(c, slist): | |
| mail = get_focused_mail(slist) | |
| if mail is None: | |
| return | |
| reply_mail(slist, mail) | |
| def forward_mail(slist, mail): | |
| hkml_view.shell_mode_start(slist) | |
| files = get_attach_files() | |
| hkml_forward.forward(mail, attach_files=files) | |
| hkml_view.shell_mode_end(slist) | |
| def forward_focused_mail(c, slist): | |
| mail = get_focused_mail(slist) | |
| if mail is None: | |
| return | |
| forward_mail(slist, mail) | |
| def hkml_list_args_for_msgid(msgid, current_list_args=None): | |
| parser = argparse.ArgumentParser() | |
| hkml_list.set_argparser(parser) | |
| args = parser.parse_args([msgid]) | |
| args.source_type = ['msgid'] | |
| # --hkml_dir, --directory and --command are added to 'hkml list', from | |
| # hkml.py | |
| args.hkml_dir = None | |
| args.directory = None | |
| args.command = 'list' | |
| # inherit non-default environmental arguments from current list arguments. | |
| if current_list_args is not None: | |
| args.hkml_dir = current_list_args.hkml_dir | |
| args.directory = current_list_args.directory | |
| args.command = current_list_args.command | |
| args.manifest = current_list_args.manifest | |
| return args | |
| def list_thread_of_focused_mail(c, slist): | |
| msgid = get_focused_mail(slist).get_msgid() | |
| args = hkml_list_args_for_msgid(msgid, slist.data.list_args) | |
| gen_show_mails_list(slist.screen, args) | |
| def refresh_list(slist, show_tagged_mails): | |
| comment_lines = [] | |
| for line in slist.lines: | |
| if line.startswith('#'): | |
| comment_lines.append(line) | |
| collapsed_mails = slist.data.collapsed_mails | |
| mail_items = get_complete_mail_items(slist) | |
| old_mail_keys = [i.mail_cache_key for i in mail_items] | |
| if show_tagged_mails: | |
| hkml_list.update_special_tagged_mail_items(mail_items) | |
| new_mail_keys = [i.mail_cache_key for i in mail_items] | |
| if old_mail_keys != new_mail_keys: | |
| hkml_cache.writeback_mails() | |
| decorator = hkml_list.MailListDecorator(slist.data.list_args) | |
| lines, line_nr_mail_idx_map = hkml_list.fmt_mails_text( | |
| mail_items, decorator, collapsed_mails) | |
| text = '\n'.join(comment_lines + lines) | |
| slist.data.list_data = hkml_list.MailsListData( | |
| text, len(comment_lines), mail_items=mail_items, | |
| line_nr_mail_idx_map=line_nr_mail_idx_map) | |
| list_cache_key = hkml_list.args_to_lists_cache_key(slist.data.list_args) | |
| _hkml_list_cache.set_item( | |
| list_cache_key, slist.data.list_data, keep_date=True) | |
| slist.set_lines(comment_lines + lines) | |
| slist.focus_row = min(slist.focus_row, len(slist.lines) - 1) | |
| slist.data.display_effect_cache = {} | |
| slist.screen.clear() | |
| def collapse_focused_thread(c, slist): | |
| collapsed_mails = slist.data.collapsed_mails | |
| collapsed_mails[focused_mail_idx(slist)] = True | |
| refresh_list(slist, show_tagged_mails=False) | |
| def expand_focused_thread(c, slist): | |
| collapsed_mails = slist.data.collapsed_mails | |
| del collapsed_mails[focused_mail_idx(slist)] | |
| refresh_list(slist, show_tagged_mails=False) | |
| def write_mail_draft(slist, mail): | |
| hkml_view.shell_mode_start(slist) | |
| hkml_write.write_send_mail( | |
| draft_mail=mail, subject=None, in_reply_to=None, to=None, | |
| cc=None, body=None, attach=None, format_only=None) | |
| hkml_view.shell_mode_end(slist) | |
| def do_add_tags(data, answer, selection): | |
| mail, tags, slist = data | |
| prompt = ' '.join(['Enter tags to add, separated by white spaces', | |
| '(enter \'cancel_tag\' to cancel): ']) | |
| tags = input(prompt).split() | |
| if 'cancel_tag' in tags: | |
| _ = input('Canceled. Press enter to return') | |
| return 'canceled' | |
| hkml_tag.do_add_tags(mail, tags, None) | |
| refresh_list(slist, show_tagged_mails=True) | |
| def do_remove_tags(data, answer, selection): | |
| mail, tags, slist = data | |
| tags_to_remove, err = _hkml_cli.ask_input( | |
| prompt='Enter tags to remove, separated by white spaces') | |
| if err is not None: | |
| return err | |
| tags_to_remove = tags_to_remove.split() | |
| for tag in tags_to_remove: | |
| if not tag in tags: | |
| print('the mail is not tagged as %s' % tag) | |
| _ = input('Canceled. Press enter to return') | |
| return 'the mail is not tagged as %s' % tag | |
| hkml_tag.do_remove_tags(mail, tags_to_remove) | |
| refresh_list(slist, show_tagged_mails=True) | |
| def manage_tags_of_mail(slist, mail): | |
| msgid = mail.get_msgid() | |
| tags = hkml_tag.tags_of_msgid(msgid) | |
| msg_lines = ['Handle tags of the mail ("%s")' % mail.subject] | |
| if len(tags) > 0: | |
| msg_lines.append('') | |
| msg_lines.append('the mail has below tags:') | |
| for tag in tags: | |
| msg_lines.append('- %s' % tag) | |
| _hkml_cli.ask_selection(desc='\n'.join(msg_lines), prompt='Select', | |
| handler_common_data=[mail, tags, slist], | |
| selections=[ | |
| _hkml_cli.Selection('Add tags', handle_fn=do_add_tags), | |
| _hkml_cli.Selection('Remove tags', | |
| handle_fn=do_remove_tags)]) | |
| def do_check_patch(data, answer, selection): | |
| mail_item = data | |
| err = hkml_patch.check_apply_or_export_item( | |
| mail_item, argparse.Namespace( | |
| hkml_dir=None, command='patch', dont_add_cv='ask', | |
| action='check', checker=None, check_recipients='do')) | |
| if err is not None: | |
| print('applying action failed (%s)' % err) | |
| def do_apply_patch(data, answer, selection): | |
| mail_item = data | |
| err = hkml_patch.check_apply_or_export_item( | |
| mail_item, argparse.Namespace( | |
| hkml_dir=None, command='patch', dont_add_cv=True, | |
| action='apply', repo='./', check_recipients='do')) | |
| if err is not None: | |
| print('applying action failed (%s)' % err) | |
| def do_export_patch(data, answer, selection): | |
| export_dir, err = _hkml_cli.ask_input( | |
| prompt='Enter relative directory to export patch files') | |
| if err == 'canceled': | |
| return | |
| if not os.path.exists(export_dir): | |
| os.makedirs(export_dir) | |
| elif not os.path.isdir(export_dir): | |
| print('%s exists, and not a directory' % export_dir) | |
| return | |
| mail_item = data | |
| err = hkml_patch.check_apply_or_export_item( | |
| mail_item, argparse.Namespace( | |
| hkml_dir=None, command='patch', dont_add_cv='ask', | |
| action='export', repo='./', export_dir=export_dir, | |
| check_recipients='do')) | |
| if err is not None: | |
| print('applying action failed (%s)' % err) | |
| def do_sashiko_patch(data, answer, selection): | |
| mail = data.mail | |
| msgid = mail.get_msgid()[1:-1] | |
| return hkml_patch.fetch_pr_sashiko_review(msgid, thread_status=False, | |
| for_forwarding=False) | |
| def do_sashiko_patch_status(data, answer, selection): | |
| mail = data.mail | |
| msgid = mail.get_msgid()[1:-1] | |
| return hkml_patch.fetch_pr_sashiko_reviews(msgid, for_forwarding=False) | |
| def do_sashiko_patch_forward(data, answer, selection): | |
| mail = data.mail | |
| msgid = mail.get_msgid()[1:-1] | |
| return hkml_patch.forward_sashiko( | |
| msgid=msgid, thread_status=False, mail=mail) | |
| def do_sashiko_patch_status_forward(data, answer, selection): | |
| mail = data.mail | |
| msgid = mail.get_msgid()[1:-1] | |
| return hkml_patch.forward_sashiko( | |
| msgid=msgid, thread_status=True, mail=mail) | |
| def handle_patches_of_mail_item(mail_item): | |
| subject = mail_item.mail.subject | |
| _hkml_cli.ask_selection( | |
| desc='Handle the mail (\'%s\') as patch[es].' % subject, | |
| prompt='Enter the item number', | |
| handler_common_data=mail_item, | |
| selections=[ | |
| _hkml_cli.Selection( | |
| 'check patch[es]', handle_fn=do_check_patch), | |
| _hkml_cli.Selection( | |
| 'apply patch[es]', handle_fn=do_apply_patch), | |
| _hkml_cli.Selection( | |
| 'export patch[es]', handle_fn=do_export_patch), | |
| _hkml_cli.Selection( | |
| 'show sashiko.dev review status', | |
| handle_fn=do_sashiko_patch_status), | |
| _hkml_cli.Selection( | |
| 'show sashiko.dev review', handle_fn=do_sashiko_patch), | |
| _hkml_cli.Selection( | |
| 'forward sashiko.dev review status', | |
| handle_fn=do_sashiko_patch_status_forward), | |
| _hkml_cli.Selection( | |
| 'forward sashiko.dev review', | |
| handle_fn=do_sashiko_patch_forward), | |
| ] | |
| ) | |
| def complete_mail_items(mail_items, slist): | |
| for mail_item in mail_items: | |
| if mail_item.mail is not None: | |
| return | |
| mail_key = mail_item.mail_cache_key | |
| mail_item.mail = hkml_cache.get_mail(key=mail_key) | |
| if mail_item.mail is None: | |
| hkml_view.shell_mode_start(slist) | |
| print('\n'.join([ | |
| 'Getting a cached mail of key "%s" failed.' % mail_key, | |
| 'There is no good way to recover from this. I will exit...', | |
| ])) | |
| hkml_view.shell_mode_end(slist) | |
| exit(1) | |
| # set parent and replies | |
| hkml_list.thread_items_of(mail_items, do_find_ancestors_from_cache=False) | |
| def get_complete_mail_items(slist): | |
| mail_items = slist.data.list_data.mail_items | |
| if mail_items is not None: | |
| complete_mail_items(mail_items, slist) | |
| return mail_items | |
| def do_export(data, answer, selection): | |
| slist, idx = data | |
| try: | |
| answer = int(answer) | |
| except: | |
| time.sleep(1) | |
| hkml_view.shell_mode_end(slist) | |
| err = 'wrong input. Return to hkml' | |
| print(err) | |
| return err | |
| if answer == 1: | |
| export_range = [idx, idx + 1] | |
| elif answer == 2: | |
| answer = input( | |
| 'Enter starting/ending index (inclusive) of mails to export: ') | |
| try: | |
| export_range = [int(x) for x in answer.split()] | |
| if len(export_range) != 2: | |
| err = 'wrong number of inputs.' | |
| print(err) | |
| return err | |
| export_range[1] += 1 # export receives half-open range | |
| except: | |
| err = 'wrong input.' | |
| print(err) | |
| return err | |
| else: | |
| export_range = None | |
| answer = input('may I export those in human/chatbot-readable form? [y/N] ') | |
| human_readable = answer.lower() == 'y' | |
| file_name = hkml_view.receive_file_path(for_read=False) | |
| if file_name is None: | |
| return 'file unselected' | |
| mail_items = get_complete_mail_items(slist) | |
| if mail_items is None: | |
| return 'getting complete mail items fail' | |
| if export_range is not None: | |
| mail_items = mail_items[export_range[0]:export_range[1]] | |
| hkml_export.export_mails( | |
| [i.mail for i in mail_items], file_name, | |
| human_readable=human_readable) | |
| print() | |
| def export_mails(c, slist): | |
| idx = focused_mail_idx(slist) | |
| _hkml_cli.ask_selection( | |
| desc='Focused mail: %d' % idx, | |
| handler_common_data=[slist, idx], | |
| selections=[ | |
| _hkml_cli.Selection( | |
| 'Export only focused mail', handle_fn=do_export), | |
| _hkml_cli.Selection( | |
| 'Export a range of mails of the list', | |
| handle_fn=do_export), | |
| _hkml_cli.Selection( | |
| 'Export all mails of the list', handle_fn=do_export) | |
| ]) | |
| def menu_open_mail(slist, answer, selection): | |
| hkml_view.shell_mode_end(slist) | |
| open_focused_mail(None, slist) | |
| hkml_view.shell_mode_start(slist) | |
| def menu_list_thread(slist, answer, selection): | |
| hkml_view.shell_mode_end(slist) | |
| list_thread_of_focused_mail(None, slist) | |
| hkml_view.shell_mode_start(slist) | |
| def menu_collapse_expand(slist, answer, selection): | |
| if focused_mail_idx(slist) in slist.data.collapsed_mails: | |
| hkml_view.shell_mode_end(slist) | |
| expand_focused_thread(None, slist) | |
| hkml_view.shell_mode_start(slist) | |
| return | |
| hkml_view.shell_mode_end(slist) | |
| collapse_focused_thread(None, slist) | |
| hkml_view.shell_mode_start(slist) | |
| class MailDisplayEffect: | |
| min_date = None | |
| max_date = None | |
| old_effect = None | |
| effect = None | |
| def eligible(self, mail): | |
| if self.min_date is not None: | |
| if self.min_date != 'min' and mail.date < self.min_date: | |
| return False | |
| if self.max_date is not None: | |
| if self.max_date != 'max' and mail.date > self.max_date: | |
| return False | |
| if self.effect is None: | |
| return False | |
| return True | |
| def effect_str(self): | |
| return { | |
| hkml_view.ScrollableList.effect_normal: 'no effect', | |
| hkml_view.ScrollableList.effect_dim: 'dim', | |
| hkml_view.ScrollableList.effect_bold: 'bold', | |
| hkml_view.ScrollableList.effect_italic: 'italic', | |
| hkml_view.ScrollableList.effect_blink: 'blink', | |
| hkml_view.ScrollableList.effect_reverse: 'reverse', | |
| hkml_view.ScrollableList.effect_underline: 'underline', | |
| }[self.effect] | |
| def __str__(self): | |
| return '\n'.join([ | |
| 'minimum date: %s' % self.min_date, | |
| 'maximum date: %s' % self.max_date, | |
| 'effect: %s' % self.effect_str() | |
| ]) | |
| def interactive_setup_dates(self): | |
| prompt = ' '.join(['Minimum date.', | |
| _hkml_date.date_format_description(), | |
| '"min" keyword is also supported.']) | |
| answer, err = _hkml_cli.ask_input(prompt=prompt) | |
| if err is not None: | |
| return | |
| if answer == 'min': | |
| self.min_date = answer | |
| else: | |
| self.min_date, err = _hkml_date.parse_date(answer) | |
| if err is not None: | |
| print(err) | |
| return | |
| prompt = ' '.join(['Maximum date.', | |
| _hkml_date.date_format_description(), | |
| '"max" keyword is also supported.']) | |
| answer, err = _hkml_cli.ask_input(prompt=prompt) | |
| if err is not None: | |
| return | |
| if answer == 'max': | |
| self.max_date = answer | |
| else: | |
| self.max_date, err = _hkml_date.parse_date(answer) | |
| if err is not None: | |
| print(err) | |
| return | |
| def __init__(self, interactive, old_effect=None): | |
| if interactive is False: | |
| return | |
| def handle_selection(data, answer, selection): | |
| rule, selections = data | |
| rule.effect = selections[int(answer) - 1].data | |
| selections = [ | |
| _hkml_cli.Selection( | |
| text='Normal', handle_fn=handle_selection, | |
| data=hkml_view.ScrollableList.effect_normal), | |
| _hkml_cli.Selection( | |
| text='Dim', handle_fn=handle_selection, | |
| data=hkml_view.ScrollableList.effect_dim), | |
| _hkml_cli.Selection( | |
| text='Bold', handle_fn=handle_selection, | |
| data=hkml_view.ScrollableList.effect_bold), | |
| _hkml_cli.Selection( | |
| text='Italic', handle_fn=handle_selection, | |
| data=hkml_view.ScrollableList.effect_italic), | |
| _hkml_cli.Selection( | |
| text='Blink', handle_fn=handle_selection, | |
| data=hkml_view.ScrollableList.effect_blink), | |
| _hkml_cli.Selection( | |
| text='Reverse', handle_fn=handle_selection, | |
| data=hkml_view.ScrollableList.effect_reverse), | |
| _hkml_cli.Selection( | |
| text='Underline', handle_fn=handle_selection, | |
| data=hkml_view.ScrollableList.effect_underline), | |
| ] | |
| _, selection, err = _hkml_cli.ask_selection( | |
| desc='Select the display effect to apply.', prompt=None, | |
| selections=selections, handler_common_data=[self, selections]) | |
| if err is not None: | |
| self.effect = old_effect | |
| return | |
| self.interactive_setup_dates() | |
| def menu_effect_mails(slist, answer, selection): | |
| print('Apply a display effect to specific mails.') | |
| print() | |
| print('current display effect:') | |
| print('%s' % slist.data.display_rule) | |
| print() | |
| # In case the user cancels the effect selection, we want to be able | |
| # to restore their old settings. | |
| old_mail_display_effect = slist.data.display_rule | |
| if old_mail_display_effect is not None: | |
| old_effect = old_mail_display_effect.effect | |
| else: | |
| old_effect = None | |
| slist.data.update_display_rule(MailDisplayEffect( | |
| interactive=True, old_effect=old_effect)) | |
| def mk_dim_old_rule(max_date): | |
| effect_rule = MailDisplayEffect(interactive=False) | |
| effect_rule.effect = hkml_view.ScrollableList.effect_dim | |
| effect_rule.min_date = 'min' | |
| effect_rule.max_date = max_date | |
| return effect_rule | |
| def build_suggest_dim_old_prompt(last_dates): | |
| lines = [''] | |
| now_time = datetime.datetime.now().astimezone() | |
| if len(last_dates) == 0: | |
| lines += [ | |
| 'May I dim mails on the list that are older than a date?', | |
| "- Enter the date to dim mails older than it." | |
| "- Or, enter 'n' if you don't want to dim any mail.", | |
| '', | |
| _hkml_date.date_format_description(), | |
| ] | |
| else: | |
| lines.append('Dates you generated old versions of the list are:') | |
| for idx, last_date in enumerate(last_dates): | |
| lines.append(' %2d. %s (%s before)' % | |
| (idx, last_date, now_time - last_date)) | |
| lines += [ | |
| '', | |
| 'May I dim mails older than the latest one?', | |
| "- Enter 'y' or nothing if yes.", | |
| "- Enter 'n' if you don't want to dim any mail.", | |
| "- Enter an index on the above list to select the date of it.", | |
| "- Or, enter custom date to dim mails older than it.", | |
| '', | |
| _hkml_date.date_format_description(), | |
| ] | |
| lines += ['', 'Enter: '] | |
| return lines | |
| def suggest_dim_old(keys): | |
| last_dates = [] | |
| for key in keys: | |
| last_dates += _hkml_list_cache.get_cache_creation_dates(key) | |
| last_dates = sorted(list(set(last_dates)))[-10:] | |
| # the very last created date is that for the current list. Exclude it. | |
| last_dates = last_dates[:-1] | |
| answer = input('\n'.join(build_suggest_dim_old_prompt(last_dates))) | |
| answer_fields = answer.split() | |
| _, err = _hkml_date.parse_date_arg(answer_fields) | |
| if err is None: | |
| return answer_fields | |
| if answer.lower() == 'n': | |
| return None | |
| if len(last_dates) == 0: | |
| print('The input is not a valid date. Do not dim_old') | |
| return None | |
| try: | |
| the_date = last_dates[int(answer)] | |
| except: | |
| if answer in ['', 'y']: | |
| the_date = last_dates[-1] | |
| else: | |
| print('Wrong input. Do not dim_old') | |
| return None | |
| date_str = the_date.strftime('%Y-%m-%d %H:%M') | |
| print('\nThe list will dim mails older than %s' % date_str) | |
| return [date_str] | |
| def menu_dim_old_mails(slist, answer, selection): | |
| mails_view_data = get_mails_view_data(slist) | |
| gen_args = mails_view_data.list_args | |
| key = hkml_list.args_to_lists_cache_key(gen_args) | |
| max_date_str = suggest_dim_old([key]) | |
| if max_date_str is None: | |
| return | |
| max_date, err = _hkml_date.parse_date_arg(max_date_str) | |
| if err is not None: | |
| print(err) | |
| return | |
| print('Dim-display mails older than %s' % max_date) | |
| slist.data.update_display_rule(mk_dim_old_rule(max_date)) | |
| def menu_reply_mail(slist, answer, selection): | |
| hkml_view.shell_mode_end(slist) | |
| reply_focused_mail(None, slist) | |
| hkml_view.shell_mode_start(slist) | |
| def menu_forward_mail(slist, answer, selection): | |
| hkml_view.shell_mode_end(slist) | |
| forward_focused_mail(None, slist) | |
| hkml_view.shell_mode_start(slist) | |
| def menu_write_draft(slist, answer, selection): | |
| mail = selection.data | |
| if mail is None: | |
| print('no mail is selected') | |
| return | |
| hkml_view.shell_mode_end(slist) | |
| write_mail_draft(slist, mail) | |
| hkml_view.shell_mode_start(slist) | |
| def menu_manage_tags(slist, answer, selection): | |
| mail = selection.data | |
| if mail is None: | |
| print('no mail is selected') | |
| return | |
| manage_tags_of_mail(slist, mail) | |
| def menu_handle_patches(slist, answer, selection): | |
| mail_item = selection.data | |
| mail = mail_item.mail | |
| if mail is None: | |
| print('no mail is selected') | |
| return | |
| handle_patches_of_mail_item(mail_item) | |
| class MailsViewData: | |
| list_data = None | |
| list_args = None | |
| display_rule = None | |
| collapsed_mails = None | |
| last_cursor_position = None | |
| focus_row_display_effect = None | |
| focus_row_color = None | |
| # cache for per-line display effect decisions that made by display_rule. | |
| display_effect_cache = None | |
| def __init__(self, list_data, list_args, display_rule): | |
| self.list_data = list_data | |
| self.list_args = list_args | |
| self.display_rule = display_rule | |
| self.collapsed_mails = {} | |
| self.last_cursor_position = {} | |
| self.display_effect_cache = {} | |
| def update_display_rule(self, rule): | |
| self.display_rule = rule | |
| # clear display decisions per line that made with old rule | |
| self.display_effect_cache = {} | |
| def display_effect_for(self, line_idx, slist): | |
| display_effects_cache = self.display_effect_cache | |
| if line_idx in display_effects_cache: | |
| return display_effects_cache[line_idx] | |
| if self.display_rule is None: | |
| return slist.effect_normal | |
| mail = mail_of_row(slist, line_idx) | |
| if mail is None: | |
| display_effects_cache[line_idx] = slist.effect_normal | |
| return slist.effect_normal | |
| mail_display_rule = self.display_rule | |
| if mail_display_rule.eligible(mail): | |
| display_effects_cache[line_idx] = mail_display_rule.effect | |
| return mail_display_rule.effect | |
| display_effects_cache[line_idx] = slist.effect_normal | |
| return slist.effect_normal | |
| def menu_refresh_mails(slist, answer, selection): | |
| gen_args = slist.data.list_args | |
| if type(gen_args) is argparse.Namespace and gen_args.fetch is False: | |
| answer = input('"--fetch" is unset. Set it? [Y/n] ') | |
| if answer.lower() != 'n': | |
| gen_args.fetch = True | |
| mails_view_data, err = generate_mails_view_data(gen_args) | |
| if err is not None: | |
| print('Generating mails list again failed (%s).' % err) | |
| return | |
| list_data = mails_view_data.list_data | |
| hkml_view.shell_mode_end(slist) | |
| slist.data = mails_view_data | |
| slist.set_lines(list_data.text.split('\n')) | |
| slist.screen.clear() | |
| hkml_view.shell_mode_start(slist) | |
| def handle_searched_lines(slist, searched_lines): | |
| slist.set_searched_lines(searched_lines) | |
| if len(searched_lines) > 0: | |
| hkml_view.ask_highlight_enabling(slist) | |
| slist.search_keyword = None | |
| print( | |
| "\nMails on %d lines are searched." % len(searched_lines), | |
| "Press 'n' and 'N' to move the cursor to", | |
| "the next or the previous searched line.") | |
| else: | |
| print('No mail for the search is found.') | |
| def menu_search_mail_body_keywords(handler_common_data, user_input, selection): | |
| slist = handler_common_data | |
| answer = user_input | |
| answer, err = _hkml_cli.ask_input( | |
| prompt='Enter keywords') | |
| if err is not None: | |
| return | |
| keywords = answer.split() | |
| searched_lines = [] | |
| for row in range(0, len(slist.lines)): | |
| mail = mail_of_row(slist, row) | |
| if mail is None: | |
| continue | |
| body = mail.get_body() | |
| searched = True | |
| for keyword in keywords: | |
| if not keyword in body: | |
| searched = False | |
| continue | |
| if searched: | |
| searched_lines. append(row) | |
| handle_searched_lines(slist, searched_lines) | |
| def menu_search_mail_from(handler_common_data, user_input, selection): | |
| slist = handler_common_data | |
| searched_lines = [] | |
| for row in range(0, len(slist.lines)): | |
| mail = mail_of_row(slist, row) | |
| if mail is None: | |
| continue | |
| sender = mail.get_from() | |
| searched = False | |
| if slist.data.list_args.from_keywords is not None: | |
| for keyword in slist.data.list_args.from_keywords: | |
| if hkml_list.keywords_in(keyword, sender): | |
| searched = True | |
| continue | |
| if searched: | |
| searched_lines.append(row) | |
| handle_searched_lines(slist, searched_lines) | |
| def reviewed_by_replies(reply_items): | |
| if reply_items is None or len(reply_items) == 0: | |
| return False | |
| for mail_item in reply_items: | |
| mail = mail_item.mail | |
| for line in mail.get_body().split('\n'): | |
| if line.startswith('Reviewed-by:'): | |
| return True | |
| return reviewed_by_replies(mail_item.reply_items) | |
| def is_reviewed(mail_item): | |
| mail = mail_item.mail | |
| body = mail.get_body() | |
| pars = body.split('\n---\n') | |
| if len(pars) < 2: | |
| return False, '--- line not found' | |
| tags_par = pars[0].split('\n\n')[-1] | |
| for tag in tags_par.split('\n'): | |
| if tag.startswith('Reviewed-by:'): | |
| return True, None | |
| return reviewed_by_replies(mail_item.reply_items), None | |
| def menu_search_reviewed_by(handler_common_data, user_input, selection): | |
| slist = handler_common_data | |
| search_reviewed = selection.data | |
| searched_lines = [] | |
| for row in range(0, len(slist.lines)): | |
| mail_item = mail_item_of_row(slist, row) | |
| if mail_item is None: | |
| continue | |
| if not 'patch' in mail_item.mail.subject_tags: | |
| continue | |
| if hkml_patch.is_cover_letter(mail_item.mail): | |
| continue | |
| reviewed, err = is_reviewed(mail_item) | |
| if err is not None: | |
| continue | |
| if reviewed == search_reviewed: | |
| searched_lines.append(row) | |
| handle_searched_lines(slist, searched_lines) | |
| def get_files_for_reviewer(reviewer, maintainers_file_content): | |
| pars = maintainers_file_content.split('\n\n') | |
| files_for_reviewer = [] | |
| for par in pars: | |
| section_for_reviewer = False | |
| m_r_check_started = False | |
| for line in par.split('\n'): | |
| fields = line.split() | |
| if len(fields) < 2: | |
| continue | |
| if fields[0] in ['M:', 'R:']: | |
| if ' '.join(fields[1:]) == reviewer: | |
| section_for_reviewer = True | |
| continue | |
| if m_r_check_started is True and section_for_reviewer is False: | |
| break | |
| if section_for_reviewer is True and fields[0] == 'F:': | |
| files_for_reviewer.append(fields[1]) | |
| return files_for_reviewer | |
| def files_touched_by(patch_mail): | |
| touched_files = [] | |
| for idx, line in enumerate(patch_mail.get_body().split('\n')): | |
| # parse diff lines that look like, e.g. | |
| # diff --git a/mm/swap.h b/mm/swap.h | |
| fields = line.split() | |
| if len(fields) == 4 and fields[:2] == ['diff', '--git']: | |
| touched_files.append(fields[2][2:]) | |
| touched_files.append(fields[3][2:]) | |
| return set(touched_files) | |
| def maintainer_file_pattern_matching(filepath, pattern): | |
| # * cannot be recursive | |
| if pattern[-1] == '*': | |
| if len(filepath.split('/')) != len(pattern.split('/')): | |
| return False | |
| if os.path.isdir(pattern) and fnmatch.fnmatch(filepath, '%s/*' % pattern): | |
| return True | |
| return fnmatch.fnmatch(filepath, pattern) | |
| def patch_is_touching(patch_mail, files_for_reviewer): | |
| for touched_file in files_touched_by(patch_mail): | |
| for file_pattern in files_for_reviewer: | |
| if maintainer_file_pattern_matching(touched_file, file_pattern): | |
| return True | |
| return False | |
| def menu_search_for_reviewer(handler_common_data, user_input, selection): | |
| slist = handler_common_data | |
| reviewer, err = _hkml_cli.ask_input( | |
| prompt='Enter the M: or R: identifier ' | |
| 'of the reviewer to search mails for.\n' | |
| 'e.g., Foo Bar <foo@bar.com>') | |
| if err is not None: | |
| return | |
| with open('MAINTAINERS', 'r') as f: | |
| maintainers_file_content = f.read() | |
| files_for_reviewer = get_files_for_reviewer( | |
| reviewer, maintainers_file_content) | |
| searched_lines = [] | |
| for row in range(0, len(slist.lines)): | |
| mail = mail_of_row(slist, row) | |
| if mail is None: | |
| continue | |
| if not 'patch' in mail.subject_tags: | |
| continue | |
| if hkml_patch.is_cover_letter(mail): | |
| continue | |
| if patch_is_touching(mail, files_for_reviewer): | |
| searched_lines.append(row) | |
| handle_searched_lines(slist, searched_lines) | |
| def menu_search(slist, answer, selection): | |
| selections = [ | |
| _hkml_cli.Selection( | |
| text='Search mails having keywords', | |
| handle_fn=menu_search_mail_body_keywords), | |
| _hkml_cli.Selection( | |
| text='Patches not having Reviewed-by:', | |
| handle_fn=menu_search_reviewed_by, | |
| data=False), | |
| _hkml_cli.Selection( | |
| text='Patches having Reviewed-by:', | |
| handle_fn=menu_search_reviewed_by, | |
| data=True), | |
| _hkml_cli.Selection( | |
| text='Search mails matching --from:', | |
| handle_fn=menu_search_mail_from, | |
| data=True), | |
| ] | |
| if os.path.isfile('MAINTAINERS'): | |
| selections.append( | |
| _hkml_cli.Selection( | |
| text='Patches for a reviewer (or maintainer)', | |
| handle_fn=menu_search_for_reviewer)) | |
| _, _, err = _hkml_cli.ask_selection( | |
| desc='Select search category', handler_common_data=slist, | |
| selections=selections) | |
| if err is not None: | |
| print(err) | |
| def menu_new_list(slist, answer, selection): | |
| answer, err = _hkml_cli.ask_input( | |
| desc='Open new list with different arguments', | |
| prompt=' '.join([ | |
| "Enter 'hml list' command line arguments", | |
| 'for the new list'])) | |
| if err is not None: | |
| return | |
| parser = argparse.ArgumentParser() | |
| hkml_list.set_argparser(parser) | |
| try: | |
| args = parser.parse_args(answer.split()) | |
| except Exception as e: | |
| print('parsing new option failed: %s' % e) | |
| return | |
| gen_show_mails_list(slist.screen, args) | |
| def menu_export_mails(slist, answer, selection): | |
| export_mails(None, slist) | |
| def menu_save_as(slist, answer, selection): | |
| hkml_view.save_as('\n'.join(slist.lines)) | |
| def menu_open_content_with(slist, answer, selection): | |
| err = hkml_view.open_content_with('\n'.join(slist.lines)) | |
| if err is not None: | |
| print(err) | |
| def menu_list_info(slist, answer, selection): | |
| mails_view_data = get_mails_view_data(slist) | |
| list_args = mails_view_data.list_args | |
| print('Sources') | |
| print('-------') | |
| for idx, source in enumerate(list_args.sources): | |
| print('- %s (%s)' % (source, list_args.source_type[idx])) | |
| if hkml_list.use_cached_output(list_args): | |
| print('(Cached outputs is reused)') | |
| else: | |
| print('(Freshly generated)') | |
| print() | |
| print('Display rules') | |
| print('-------------') | |
| print('%s' % mails_view_data.display_rule) | |
| print() | |
| key = hkml_list.args_to_lists_cache_key(list_args) | |
| dates = _hkml_list_cache.get_cache_creation_dates(key) | |
| print('Last opened dates') | |
| print('-----------------') | |
| now = datetime.datetime.now().astimezone() | |
| for date in dates: | |
| print('- %s (%s before)' % (date, now - date)) | |
| print() | |
| print('Addiotinal info') | |
| print('---------------') | |
| print() | |
| print('\n'.join(mails_view_data.list_data.comments_lines)) | |
| def menu_focus_row_display_effect(slist, answer, selection): | |
| mails_view_data = get_mails_view_data(slist) | |
| ask_focus_row_display_effect(mails_view_data, called_from_menu=True) | |
| def show_mails_list_menu(c, slist): | |
| mail_item = get_focused_mail_item(slist) | |
| mail = mail_item.mail | |
| if mail is None: | |
| menu_desc = 'selected mail: none' | |
| else: | |
| menu_desc = 'selected mail: %s' % mail.subject | |
| msgid = mail.get_msgid()[1:-1] | |
| menu_desc += '\nmsgid: %s' % msgid | |
| hkml_view.shell_mode_start(slist) | |
| _hkml_cli.ask_selection( | |
| desc=menu_desc, | |
| prompt='Enter menu item number', | |
| handler_common_data=slist, | |
| selections=[ | |
| _hkml_cli.Selection('open', handle_fn=menu_open_mail), | |
| _hkml_cli.Selection( | |
| 'list complete thread', handle_fn=menu_list_thread), | |
| _hkml_cli.Selection( | |
| 'collapse/expand focused thread', | |
| handle_fn=menu_collapse_expand), | |
| _hkml_cli.Selection( | |
| 'dim old mails', handle_fn=menu_dim_old_mails), | |
| _hkml_cli.Selection( | |
| 'set display effects', handle_fn=menu_effect_mails), | |
| _hkml_cli.Selection('reply', handle_fn=menu_reply_mail), | |
| _hkml_cli.Selection( | |
| 'forward', handle_fn=menu_forward_mail), | |
| _hkml_cli.Selection( | |
| 'continue draft writing', handle_fn=menu_write_draft, | |
| data=mail), | |
| _hkml_cli.Selection( | |
| 'manage tags', handle_fn=menu_manage_tags, data=mail), | |
| _hkml_cli.Selection( | |
| 'handle as patches', handle_fn=menu_handle_patches, | |
| data=mail_item), | |
| _hkml_cli.Selection( | |
| 'refresh', handle_fn=menu_refresh_mails), | |
| _hkml_cli.Selection( | |
| 'search mails', handle_fn=menu_search), | |
| _hkml_cli.Selection( | |
| 'open new list', handle_fn=menu_new_list), | |
| _hkml_cli.Selection( | |
| 'export as an mbox file', handle_fn=menu_export_mails), | |
| _hkml_cli.Selection( | |
| 'save screen content to ...', handle_fn=menu_save_as), | |
| _hkml_cli.Selection( | |
| 'open screen content with ...', | |
| handle_fn=menu_open_content_with), | |
| _hkml_cli.Selection( | |
| 'list info', handle_fn=menu_list_info), | |
| _hkml_cli.Selection( | |
| 'set focused row display effect', | |
| handle_fn=menu_focus_row_display_effect), | |
| ]) | |
| hkml_view.shell_mode_end(slist) | |
| def get_mails_list_input_handlers(): | |
| return [ | |
| hkml_view.InputHandler( | |
| ['o', '\n'], open_focused_mail, 'open focused mail'), | |
| hkml_view.InputHandler( | |
| ['r'], reply_focused_mail, 'reply focused mail'), | |
| hkml_view.InputHandler( | |
| ['f'], forward_focused_mail, 'forward focused mail'), | |
| hkml_view.InputHandler(['t'], list_thread_of_focused_mail, | |
| 'list complete thread'), | |
| hkml_view.InputHandler( | |
| ['c'], collapse_focused_thread, 'collapse focused thread'), | |
| hkml_view.InputHandler( | |
| ['e'], expand_focused_thread, 'expand focused thread'), | |
| hkml_view.InputHandler( | |
| ['m'], show_mails_list_menu, 'open menu'), | |
| ] | |
| def after_input_handle_callback(slist): | |
| list_data = slist.data.list_data | |
| mail_items = list_data.mail_items | |
| if mail_items is None: | |
| return | |
| last_list_data = _hkml_list_cache.get_last_list() | |
| if last_list_data is None: | |
| last_mail_items = None | |
| else: | |
| last_mail_items = last_list_data.mail_items | |
| if mail_items != last_mail_items: | |
| _hkml_list_cache.set_item('thread_output', list_data) | |
| def mails_display_effect_callback(slist, line_idx): | |
| attrib = slist.data.display_effect_for(line_idx, slist) | |
| focus_row_effect = slist.data.focus_row_display_effect | |
| if focus_row_effect is not None and line_idx == slist.focus_row: | |
| attrib = attrib | focus_row_effect | |
| return attrib | |
| def mails_color_callback(slist, line_idx): | |
| if slist.focus_row != line_idx: | |
| return hkml_view.normal_color | |
| focus_row_color = slist.data.focus_row_color | |
| if focus_row_color is None: | |
| focus_row_color = hkml_view.normal_color | |
| return focus_row_color | |
| def mails_quit_callback(slist): | |
| mails_view_data = slist.data | |
| cache_key = hkml_list.args_to_lists_cache_key(mails_view_data.list_args) | |
| _hkml_list_cache.record_last_cursor_position( | |
| cache_key, [slist.focus_row, slist.focus_col]) | |
| def show_mails_list(screen, mails_view_data): | |
| list_data = mails_view_data.list_data | |
| list_args = mails_view_data.list_args | |
| text_lines = list_data.text.split('\n') | |
| slist = hkml_view.ScrollableList(screen, text_lines, | |
| get_mails_list_input_handlers()) | |
| slist.data = mails_view_data | |
| slist.after_input_handle_callback = after_input_handle_callback | |
| slist.display_effect_callback = mails_display_effect_callback | |
| slist.color_callback = mails_color_callback | |
| slist.quit_callback = mails_quit_callback | |
| if list_data.len_comments is not None: | |
| slist.focus_row = min(list_data.len_comments, len(text_lines) - 1) | |
| if hkml_list.use_cached_output(list_args): | |
| cache_key = hkml_list.args_to_lists_cache_key(list_args) | |
| row_col, err = _hkml_list_cache.get_last_cursor_position(cache_key) | |
| if err is None: | |
| slist.focus_row, slist.focus_col = row_col | |
| elif list_args.source_type == ['msgid']: | |
| for line_nr, mail_idx in list_data.line_nr_mail_idx_map.items(): | |
| mail = list_data.mail_items[mail_idx].mail | |
| mail_msgid = mail.get_msgid()[1:-1] | |
| if mail_msgid in list_args.sources[0]: | |
| slist.focus_row = list_data.len_comments + line_nr | |
| slist.focus_col = 0 | |
| slist.draw() | |
| return slist | |
| def should_fetch(list_args, list_data, err): | |
| if not hkml_list.use_cached_output(list_args): | |
| return False | |
| if err is None: | |
| cache_key = hkml_list.args_to_lists_cache_key(list_args) | |
| row_col, err = _hkml_list_cache.get_last_cursor_position(cache_key) | |
| if err is not None: | |
| return False | |
| last_cursor_position_row = row_col[0] | |
| list_nr_lines = list_data.text.count('\n') | |
| desc = 'You stopped reading the list before. ' \ | |
| 'The cursor position at the time was %d/%d row.' % ( | |
| last_cursor_position_row, list_nr_lines) | |
| selections = [ | |
| _hkml_cli.Selection('Continue reading it'), | |
| _hkml_cli.Selection( | |
| 'Fetch mails from the internet and make a new list'), | |
| ] | |
| _, selection, err = _hkml_cli.ask_selection( | |
| desc=desc, selections=selections, default_selection_idx=0, | |
| allow_cancel=False) | |
| return selection == selections[1] | |
| if err == 'no mail to list': | |
| return _hkml_cli.ask_yes_no( | |
| 'No mails to show. ' \ | |
| 'Shall I fetch the mails from the internet and ' \ | |
| 'do listing again?') == 'y' | |
| return False | |
| def generate_mails_list_data(list_args): | |
| list_data, err = hkml_list.args_to_mails_list_data( | |
| list_args, suggest_manifest_update=True) | |
| if should_fetch(list_args, list_data, err): | |
| list_args.fetch = True | |
| list_data, err = hkml_list.args_to_mails_list_data( | |
| list_args, suggest_manifest_update=True) | |
| return list_data, err | |
| def ask_focus_row_display_effect(mails_view_data, called_from_menu): | |
| if called_from_menu is False: | |
| val = hkml_config.get_config('list-display-rule-focused-row') | |
| if val is not None: | |
| effect = None | |
| color = None | |
| if val == 'reverse': | |
| effect = hkml_view.ScrollableList.effect_reverse | |
| elif val == 'color': | |
| effect = hkml_view.focus_color | |
| mails_view_data.focus_row_display_effect = effect | |
| mails_view_data.focus_row_color = color | |
| return | |
| desc = 'Select cursor-focused line display effect.' | |
| if called_from_menu is False: | |
| desc += '\n- You can change this later using ' \ | |
| '"set focused row display effect" menu' | |
| desc += '\n- You can also skip this question using\n' \ | |
| ' "hkml config set list-display-rule-focused-row ' \ | |
| '<none|reverse|color>"' | |
| _, selection, err = _hkml_cli.ask_selection( | |
| desc=desc, selections_txt=['No effect', 'reverse', 'color'], | |
| prompt='Select', default_selection_idx=0) | |
| if err is not None: | |
| print('Error (%s). Set no effect for focused row.' % err) | |
| return None | |
| display_effect = None | |
| color = None | |
| if selection == 1: | |
| display_effect = hkml_view.ScrollableList.effect_reverse | |
| elif selection == 2: | |
| color = hkml_view.focus_color | |
| mails_view_data.focus_row_display_effect = display_effect | |
| mails_view_data.focus_row_color = color | |
| def generate_mails_view_data(args): | |
| # returns MailsViewData and error | |
| list_data, err = generate_mails_list_data(args) | |
| if err is not None: | |
| return None, err | |
| if not hasattr(args, 'dim_old') or args.dim_old is None: | |
| keys = [hkml_list.args_to_lists_cache_key(args)] | |
| thread_msgids = [] | |
| if args.source_type == ['msgid'] and list_data.line_nr_mail_idx_map: | |
| thread_msgids = [] | |
| for line_nr, mail_idx in list_data.line_nr_mail_idx_map.items(): | |
| mail = list_data.mail_items[mail_idx].mail | |
| msgid = mail.get_msgid() | |
| if len(thread_msgids) > 0 and thread_msgids[-1] == msgid: | |
| continue | |
| thread_msgids.append(mail.get_msgid()) | |
| for msgid in thread_msgids: | |
| keys.append(hkml_list.args_to_lists_cache_key( | |
| hkml_list_args_for_msgid(msgid, args))) | |
| args.dim_old = suggest_dim_old(keys) | |
| if args.dim_old is None: | |
| mails_view_data = MailsViewData(list_data, args, None) | |
| ask_focus_row_display_effect( | |
| mails_view_data, called_from_menu=False) | |
| return mails_view_data, err | |
| max_date, err = _hkml_date.parse_date_arg(args.dim_old) | |
| if err is not None: | |
| err = 'wrong --dim_old (%s)' % err | |
| display_effect_rule = None | |
| else: | |
| display_effect_rule = mk_dim_old_rule(max_date) | |
| mails_view_data = MailsViewData(list_data, args, display_effect_rule) | |
| ask_focus_row_display_effect(mails_view_data, called_from_menu=False) | |
| return mails_view_data, err | |
| def gen_show_mails_list(screen, list_args): | |
| ''' | |
| This function will be passed to hkml_view.view() as draw_fn. | |
| ''' | |
| hkml_view.shell_mode_start(screen) | |
| mails_view_data, err = generate_mails_view_data(list_args) | |
| if err is not None: | |
| print('Failed mails list generating (%s).' % err) | |
| hkml_view.shell_mode_end(screen) | |
| if err is not None: | |
| return | |
| return show_mails_list(screen, mails_view_data) |