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