diff --git a/evennia/commands/default/help.py b/evennia/commands/default/help.py index c559ad019b..93201f07db 100644 --- a/evennia/commands/default/help.py +++ b/evennia/commands/default/help.py @@ -28,6 +28,7 @@ from evennia.help.utils import help_search_with_index, parse_entry_for_subcatego COMMAND_DEFAULT_CLASS = class_from_module(settings.COMMAND_DEFAULT_CLASS) HELP_MORE_ENABLED = settings.HELP_MORE_ENABLED DEFAULT_HELP_CATEGORY = settings.DEFAULT_HELP_CATEGORY +HELP_CLICKABLE_TOPICS = settings.HELP_CLICKABLE_TOPICS # limit symbol import for API __all__ = ("CmdHelp", "CmdSetHelp") @@ -97,6 +98,9 @@ class CmdHelp(COMMAND_DEFAULT_CLASS): # separator between subtopics: subtopic_separator_char = r"/" + # should topics disply their help entry when clicked + clickable_topics = HELP_CLICKABLE_TOPICS + def msg_help(self, text): """ messages text to the caller, adding an extra oob argument to indicate @@ -121,21 +125,22 @@ class CmdHelp(COMMAND_DEFAULT_CLASS): self.msg(text=(text, {"type": "help"})) def format_help_entry(self, topic="", help_text="", aliases=None, suggested=None, - subtopics=None): - """ - This visually formats the help entry. + subtopics=None, click_topics=True): + """This visually formats the help entry. This method can be overriden to customize the way a help entry is displayed. Args: - title (str): The title of the help entry. - help_text (str): Text of the help entry. - aliases (list): List of help-aliases (displayed in header). - suggested (list): Strings suggested reading (based on title). - subtopics (list): A list of strings - the subcategories available + title (str, optional): The title of the help entry. + help_text (str, optional): Text of the help entry. + aliases (list, optional): List of help-aliases (displayed in header). + suggested (list, optional): Strings suggested reading (based on title). + subtopics (list, optional): A list of strings - the subcategories available for this entry. + click_topics (bool, optional): Should help topics be clickable. Default is True. - Returns the formatted string, ready to be sent. + Returns: + help_message (str): Help entry formated for console. """ separator = "|C" + "-" * self.client_width() + "|n" @@ -153,7 +158,13 @@ class CmdHelp(COMMAND_DEFAULT_CLASS): help_text = "\n" + dedent(help_text.strip('\n')) if help_text else "" if subtopics: - subtopics = [f"|w{topic}/{subtop}|n" for subtop in subtopics] + if click_topics: + subtopics = [ + f"|lchelp {topic}/{subtop}|lt|w{topic}/{subtop}|n|le" + for subtop in subtopics + ] + else: + subtopics = [f"|w{topic}/{subtop}|n" for subtop in subtopics] subtopics = ( "\n|CSubtopics:|n\n {}".format( "\n ".join(format_grid(subtopics, width=self.client_width()))) @@ -162,7 +173,10 @@ class CmdHelp(COMMAND_DEFAULT_CLASS): subtopics = '' if suggested: - suggested = [f"|w{sug}|n" for sug in suggested] + if click_topics: + suggested = [f"|lchelp {sug}|lt|w{sug}|n|le" for sug in suggested] + else: + suggested = [f"|w{sug}|n" for sug in suggested] suggested = ( "\n|COther topic suggestions:|n\n{}".format( "\n ".join(format_grid(suggested, width=self.client_width()))) @@ -176,9 +190,9 @@ class CmdHelp(COMMAND_DEFAULT_CLASS): return "\n".join(part.rstrip() for part in partorder if part) - def format_help_index(self, cmd_help_dict=None, db_help_dict=None, title_lone_category=False): - """ - Output a category-ordered g for displaying the main help, grouped by + def format_help_index(self, cmd_help_dict=None, db_help_dict=None, title_lone_category=False, + click_topics=True): + """Output a category-ordered g for displaying the main help, grouped by category. Args: @@ -190,14 +204,15 @@ class CmdHelp(COMMAND_DEFAULT_CLASS): be titled with the category name or not. While pointless in a general index, the title should probably show when explicitly listing the category itself. + click_topics (bool, optional): Should help topics be clickable. Default is True. Returns: str: The help index organized into a grid. - The input are the - pre-loaded help files for commands and database-helpfiles - respectively. You can override this method to return a - custom display of the list of commands and topics. + Notes + The input are the pre-loaded help files for commands and database-helpfiles + respectively. You can override this method to return a custom display of the list of + commands and topics. """ def _group_by_category(help_dict): @@ -207,7 +222,16 @@ class CmdHelp(COMMAND_DEFAULT_CLASS): if len(help_dict) == 1 and not title_lone_category: # don't list categories if there is only one for category in help_dict: + # gather and sort the entries from the help dictionary entries = sorted(set(help_dict.get(category, []))) + + # make the help topics clickable + if click_topics: + entries = [ + f'|lchelp {entry}|lt{entry}|le' for entry in entries + ] + + # add the entries to the grid grid.extend(entries) else: # list the categories @@ -222,7 +246,16 @@ class CmdHelp(COMMAND_DEFAULT_CLASS): ) verbatim_elements.append(len(grid) - 1) + # gather and sort the entries from the help dictionary entries = sorted(set(help_dict.get(category, []))) + + # make the help topics clickable + if click_topics: + entries = [ + f'|lchelp {entry}|lt{entry}|le' for entry in entries + ] + + # add the entries to the grid grid.extend(entries) return grid, verbatim_elements @@ -449,6 +482,7 @@ class CmdHelp(COMMAND_DEFAULT_CLASS): """ caller = self.caller query, subtopics, cmdset = self.topic, self.subtopics, self.cmdset + clickable_topics = self.clickable_topics if not query: # list all available help entries, grouped by category. We want to @@ -470,7 +504,8 @@ class CmdHelp(COMMAND_DEFAULT_CLASS): # generate the index and display output = self.format_help_index(cmd_help_by_category, - file_db_help_by_category) + file_db_help_by_category, + click_topics=clickable_topics) self.msg_help(output) return @@ -526,7 +561,8 @@ class CmdHelp(COMMAND_DEFAULT_CLASS): output = self.format_help_entry( topic=None, # this will give a no-match style title help_text=help_text, - suggested=suggestions + suggested=suggestions, + click_topics=clickable_topics ) self.msg_help(output) @@ -542,7 +578,8 @@ class CmdHelp(COMMAND_DEFAULT_CLASS): if category_lower == topic.help_category] output = self.format_help_index({category: cmds_in_category}, {category: topics_in_category}, - title_lone_category=True) + title_lone_category=True, + click_topics=clickable_topics) self.msg_help(output) return @@ -596,7 +633,8 @@ class CmdHelp(COMMAND_DEFAULT_CLASS): output = self.format_help_entry( topic=topic, help_text=f"No help entry found for '{checked_topic}'", - subtopics=subtopic_index + subtopics=subtopic_index, + click_topics=clickable_topics ) self.msg_help(output) return @@ -616,7 +654,8 @@ class CmdHelp(COMMAND_DEFAULT_CLASS): help_text=help_text, aliases=aliases if not subtopics else None, subtopics=subtopic_index, - suggested=suggested + suggested=suggested, + click_topics=clickable_topics ) self.msg_help(output) diff --git a/evennia/settings_default.py b/evennia/settings_default.py index 1450fc6c52..227d3db96c 100644 --- a/evennia/settings_default.py +++ b/evennia/settings_default.py @@ -614,6 +614,9 @@ DEFAULT_HELP_CATEGORY = "general" # File-based help entries. These are modules containing dicts defining help # entries. They can be used together with in-database entries created in-game. FILE_HELP_ENTRY_MODULES = ["world.help_entries"] +# if topics listed in help should be clickable +# clickable links only work on clients that support MXP +HELP_CLICKABLE_TOPICS = False ###################################################################### # FuncParser diff --git a/evennia/utils/utils.py b/evennia/utils/utils.py index 6e3d274080..aaac63b6bf 100644 --- a/evennia/utils/utils.py +++ b/evennia/utils/utils.py @@ -1856,12 +1856,14 @@ def format_grid(elements, width=78, sep=" ", verbatim_elements=None): be inserted into the grid at the correct position and may be surrounded by padding unless filling the entire line. This is useful for embedding decorations in the grid, such as horizontal bars. + ignore_ansi (bool, optional): Ignore ansi markups when calculating white spacing. Returns: list: The grid as a list of ready-formatted rows. We return it like this to make it easier to insert decorations between rows, such as horizontal bars. """ + def _minimal_rows(elements): """ Minimalistic distribution with minimal spacing, good for single-line @@ -1869,8 +1871,8 @@ def format_grid(elements, width=78, sep=" ", verbatim_elements=None): """ rows = [""] for element in elements: - rowlen = len(rows[-1]) - elen = len(element) + rowlen = display_len((rows[-1])) + elen = display_len((element)) if rowlen + elen <= width: rows[-1] += element else: @@ -1882,8 +1884,7 @@ def format_grid(elements, width=78, sep=" ", verbatim_elements=None): Dynamic-space, good for making even columns in a multi-line grid but will look strange for a single line. """ - - wls = [len(elem) for elem in elements] + wls = [display_len((elem)) for elem in elements] wls_percentile = [wl for iw, wl in enumerate(wls) if iw not in verbatim_elements] if wls_percentile: @@ -1898,7 +1899,8 @@ def format_grid(elements, width=78, sep=" ", verbatim_elements=None): # one line per row, output directly since this is trivial # we use rstrip here to remove extra spaces added by sep return [ - crop(element.rstrip(), width) + " " * max(0, width - len(element.rstrip())) + crop(element.rstrip(), width) + " " \ + * max(0, width - display_len((element.rstrip()))) for iel, element in enumerate(elements) ] @@ -1910,7 +1912,7 @@ def format_grid(elements, width=78, sep=" ", verbatim_elements=None): for ie, element in enumerate(elements): wl = wls[ie] - lrow = len(row) + lrow = display_len((row)) # debug = row.replace(" ", ".") if lrow + wl > width: @@ -1949,7 +1951,7 @@ def format_grid(elements, width=78, sep=" ", verbatim_elements=None): if ie >= nelements - 1: # last element, make sure to store - row += " " * max(0, width - len(row)) + row += " " * max(0, width - display_len((row))) rows.append(row) return rows @@ -1962,7 +1964,7 @@ def format_grid(elements, width=78, sep=" ", verbatim_elements=None): # add sep to all but the very last element elements = [elements[ie] + sep for ie in range(nelements - 1)] + [elements[-1]] - if sum(len(element) for element in elements) <= width: + if sum(display_len((element)) for element in elements) <= width: # grid fits in one line return _minimal_rows(elements) else: