From 166d8257ec739dca681042399acb280e58bc9f97 Mon Sep 17 00:00:00 2001 From: Rapptz Date: Tue, 5 Jul 2016 21:44:14 -0400 Subject: [PATCH] [commands] Refactor pagination into its own class. This change allows users to more easily create their own formatters without having to reinvent the pagination logic themselves. Hopefully this makes it less of a pain for people to create custom help pages. --- discord/ext/commands/__init__.py | 2 +- discord/ext/commands/formatter.py | 118 ++++++++++++++++++------------ 2 files changed, 71 insertions(+), 49 deletions(-) diff --git a/discord/ext/commands/__init__.py b/discord/ext/commands/__init__.py index 6d82962de..ed28b6a06 100644 --- a/discord/ext/commands/__init__.py +++ b/discord/ext/commands/__init__.py @@ -14,5 +14,5 @@ from .bot import Bot, when_mentioned, when_mentioned_or from .context import Context from .core import * from .errors import * -from .formatter import HelpFormatter +from .formatter import HelpFormatter, Paginator from .converter import * diff --git a/discord/ext/commands/formatter.py b/discord/ext/commands/formatter.py index d20d65799..452fd40e6 100644 --- a/discord/ext/commands/formatter.py +++ b/discord/ext/commands/formatter.py @@ -51,6 +51,64 @@ from .errors import CommandError # Type help command for more info on a command. # You can also type help category for more info on a category. +class Paginator: + """A class that aids in paginating code blocks for Discord messages. + + Attributes + ----------- + prefix: str + The prefix inserted to every page. e.g. three backticks. + suffix: str + The suffix appended at the end of every page. e.g. three backticks. + max_size: int + The maximum amount of codepoints allowed in a page. + """ + def __init__(self, prefix='```', suffix='```', max_size=2000): + self.prefix = prefix + self.suffix = suffix + self.max_size = max_size - len(suffix) + self._current_page = [prefix] + self._count = len(prefix) + 1 # prefix + newline + self._pages = [] + + def add_line(self, line='', *, empty=False): + """Adds a line to the current page. + + Parameters + ----------- + line: str + The line to add. + empty: bool + Indicates if another empty line should be added. + """ + if self._count + len(line) + 1 > self.max_size: + self.close_page() + + self._count += len(line) + 1 + self._current_page.append(line) + + if empty: + self._current_page.append('') + self._count += 1 + + def close_page(self): + """Prematurely terminate a page.""" + self._current_page.append(self.suffix) + self._pages.append('\n'.join(self._current_page)) + self._current_page = [self.prefix] + self._count = len(self.prefix) + 1 # prefix + newline + + @property + def pages(self): + """Returns the rendered list of pages.""" + # we have more than just the prefix in our current page + if len(self._current_page) > 1: + self.close_page() + return self._pages + + def __repr__(self): + fmt = '' + return fmt.format(self) class HelpFormatter: """The default base implementation that handles formatting of the help @@ -190,18 +248,6 @@ class HelpFormatter: iterator = self.command.commands.items() if not self.is_cog() else self.context.bot.commands.items() return filter(predicate, iterator) - def _check_new_page(self): - # be a little on the safe side - # we're adding 1 extra newline per page - if self._count + len(self._current_page) >= 1980: - # add the page - self._current_page.append('```') - self._pages.append('\n'.join(self._current_page)) - self._current_page = ['```'] - self._count = 4 - return True - return False - def _add_subcommands_to_page(self, max_width, commands): for name, command in commands: if name in command.aliases: @@ -210,10 +256,7 @@ class HelpFormatter: entry = ' {0:<{width}} {1}'.format(name, command.short_doc, width=max_width) shortened = self.shorten(entry) - self._count += len(shortened) - if self._check_new_page(): - self._count += len(shortened) - self._current_page.append(shortened) + self._paginator.add_line(shortened) def format_help_for(self, context, command_or_bot): """Formats the help page and handles the actual heavy lifting of how @@ -246,9 +289,7 @@ class HelpFormatter: list A paginated output of the help command. """ - self._pages = [] - self._count = 4 # ``` + '\n' - self._current_page = ['```'] + self._paginator = Paginator() # we need a padding of ~80 or so @@ -256,30 +297,21 @@ class HelpFormatter: if description: # portion - self._current_page.append(description) - self._current_page.append('') - self._count += len(description) + self._paginator.add_line(description, empty=True) if isinstance(self.command, Command): # signature = self.get_command_signature() - self._count += 2 + len(signature) # '\n' sig '\n' - self._current_page.append(signature) - self._current_page.append('') + self._paginator.add_line(signature, empty=True) # section if self.command.help: - self._count += 2 + len(self.command.help) - self._current_page.append(self.command.help) - self._current_page.append('') - self._check_new_page() + self._paginator.add_line(self.command.help, empty=True) # end it here if it's just a regular command if not self.has_subcommands(): - self._current_page.append('```') - self._pages.append('\n'.join(self._current_page)) - return self._pages - + self._paginator.close_page() + return self._paginator.pages max_width = self.max_name_size @@ -295,25 +327,15 @@ class HelpFormatter: # there simply is no prettier way of doing this. commands = list(commands) if len(commands) > 0: - self._current_page.append(category) - self._count += len(category) - self._check_new_page() + self._paginator.add_line(category) self._add_subcommands_to_page(max_width, commands) else: - self._current_page.append('Commands:') - self._count += 1 + len(self._current_page[-1]) + self._paginator.add_line('Commands:') self._add_subcommands_to_page(max_width, self.filter_command_list()) # add the ending note - self._current_page.append('') + self._paginator.add_line() ending_note = self.get_ending_note() - self._count += len(ending_note) - self._check_new_page() - self._current_page.append(ending_note) - - if len(self._current_page) > 1: - self._current_page.append('```') - self._pages.append('\n'.join(self._current_page)) - - return self._pages + self._paginator.add_line(ending_note) + return self._paginator.pages