diff --git a/sphinx_click/ext.py b/sphinx_click/ext.py index f1ddd36..0aa15af 100644 --- a/sphinx_click/ext.py +++ b/sphinx_click/ext.py @@ -1,3 +1,4 @@ +import re import traceback import warnings @@ -16,6 +17,8 @@ NESTED_SHORT = 'short' NESTED_NONE = 'none' +ANSI_ESC_SEQ_RE = re.compile(r'\x1B\[\d+(;\d+){0,2}m', flags=re.MULTILINE) + def _indent(text, level=1): prefix = ' ' * (4 * level) @@ -109,6 +112,8 @@ def _format_description(ctx): if not help_string: return + help_string = ANSI_ESC_SEQ_RE.sub('', help_string) + bar_enabled = False for line in statemachine.string2lines( help_string, tab_width=4, convert_whitespace=True diff --git a/tests/test_formatter.py b/tests/test_formatter.py index 43496e8..7d32199 100644 --- a/tests/test_formatter.py +++ b/tests/test_formatter.py @@ -266,6 +266,38 @@ def hello(name): '\n'.join(output), ) + def test_ansi_escape_sequences(self): + """Validate that ANSI escape sequences are stripped.""" + + @click.command() + def foobar(): + """A sample command with **sparkles**. + + We've got \033[31mred text\033[0m, \033[104mblue backgrounds\033[0m, a + dash of \033[1mbold\033[0m and even some \033[4munderlined words\033[0m. + """ + pass + + ctx = click.Context(foobar, info_name='foobar') + output = list(ext._format_command(ctx, nested='short')) + + self.assertEqual( + textwrap.dedent( + """ + A sample command with **sparkles**. + + We've got red text, blue backgrounds, a + dash of bold and even some underlined words. + + .. program:: foobar + .. code-block:: shell + + foobar [OPTIONS] + """ + ).lstrip(), + '\n'.join(output), + ) + class GroupTestCase(unittest.TestCase): """Validate basic ``click.Group`` instances."""