diff options
-rw-r--r-- | AUTHORS | 1 | ||||
-rw-r--r-- | CHANGES | 5 | ||||
-rw-r--r-- | doc/docs/styles.rst | 51 | ||||
-rw-r--r-- | pygments/formatters/terminal256.py | 20 | ||||
-rw-r--r-- | pygments/style.py | 18 | ||||
-rw-r--r-- | tests/test_lexers_other.py | 49 | ||||
-rw-r--r-- | tests/test_terminal_formatter.py | 71 |
7 files changed, 84 insertions, 131 deletions
@@ -33,6 +33,7 @@ Other contributors, listed alphabetically, are: * Adam Blinkinsop -- Haskell, Redcode lexers * Frits van Bommel -- assembler lexers * Pierre Bourdon -- bugfixes +* Matthias Bussonnier -- ANSI style handling for terminal-256 formatter * chebee7i -- Python traceback lexer improvements * Hiram Chirino -- Scaml and Jade lexers * Ian Cooper -- VGL lexer @@ -17,7 +17,7 @@ Version 2.2 * TypoScript (#1173) * Varnish config (PR#554) -- Added `lexers.find_lexer_class_by_name()` (#1203) +- Added `lexers.find_lexer_class_by_name()`. (#1203) - Added new token types and lexing for magic methods and variables in Python and PHP. @@ -28,6 +28,9 @@ Version 2.2 - Added a new token type for heredoc (and similar) string delimiters and lexing for them in C++, Perl, PHP, Postgresql and Ruby lexers. +- Styles can now define colors with ANSI colors for use in the 256-color + terminal formatter. (PR#531) + Version 2.1.1 ------------- diff --git a/doc/docs/styles.rst b/doc/docs/styles.rst index 0076d062..394c8ed2 100644 --- a/doc/docs/styles.rst +++ b/doc/docs/styles.rst @@ -152,19 +152,19 @@ Terminal Styles .. versionadded:: 2.2 -Custom styles used with `Terminal256` formatter can also defines colors using -ansi-color. To do so use the `#ansigreen`, `#ansired` or any other colors -defined in ``pygments.style.ansilist``. Foreground ANSI colors will be mapped -to the corresponding `escape codes 30 to 37 -<https://en.wikipedia.org/wiki/ANSI_escape_code#Colors>`_ thus respecting any -custom color mapping and themes provided by many terminal emulators. Light -variant are treated for foreground color with and extra bold flag. -`bg:#ansi<color>` will also be respected, except the light variant will be the -same shade as their light variant. - -See following example where the color of the string `"hello world"` is governed -by the escape sequence `\x1b34;01m` (Ansi Blue, Bold, `41` beeing red background) -instead of an extended foreground & background color. +Custom styles used with the 256-color terminal formatter can also map colors to +use the 8 default ANSI colors. To do so, use ``#ansigreen``, ``#ansired`` or +any other colors defined in :attr:`pygments.style.ansilist`. Foreground ANSI +colors will be mapped to the corresponding `escape codes 30 to 37 +<https://en.wikipedia.org/wiki/ANSI_escape_code#Colors>`_ thus respecting any +custom color mapping and themes provided by many terminal emulators. Light +variants are treated as foreground color with and an added bold flag. +``bg:#ansi<color>`` will also be respected, except the light variant will be the +same shade as their dark variant. + +See the following example where the color of the string ``"hello world"`` is +governed by the escape sequence ``\x1b[34;01m`` (Ansi Blue, Bold, 41 being red +background) instead of an extended foreground & background color. .. sourcecode:: pycon @@ -182,23 +182,20 @@ instead of an extended foreground & background color. >>> code = 'print("Hello World")' >>> result = highlight(code, Python3Lexer(), Terminal256Formatter(style=MyStyle)) >>> print(result.encode()) - b'print(\x1b[34;41;01m"\x1b[39;49;00m\x1b[34;41;01mHello World\x1b[39;49;00m\x1b[34;41;01m"\x1b[39;49;00m)\n' + b'\x1b[34;41;01m"\x1b[39;49;00m\x1b[34;41;01mHello World\x1b[39;49;00m\x1b[34;41;01m"\x1b[39;49;00m' -Style that use `#ansi*` colors might not correctly work with -formatters others than ``Terminal256``. `HtmlFormatter` is capable of handling -some `#ansi*` code and will map to a fixed HTML/CSS color. For example, -`#ansiblue` will be converted to `color:#0000ff` , `#ansired` to `color:#ff0000`. +Colors specified using ``#ansi*`` are converted to a default set of RGB colors +when used with formatters other than the terminal-256 formatter. -By definition of Ansi color the following color are considered "light" colors, -and will be rendered by most terminal as bold: +By definition of ANSI, the following colors are considered "light" colors, and +will be rendered by most terminals as bold: - - "darkgray", "red", "green", "yellow", "blue", "fuchsia", "turquoise", - "white" +- "darkgray", "red", "green", "yellow", "blue", "fuchsia", "turquoise", "white" +The following are considered "dark" colors and will be rendered as non-bold: -The following are considered "dark" color and will be rendered as non-bold: - - - "black", "darkred", "darkgreen", "brown", "darkblue", "purple", "teal", - "lightgray" +- "black", "darkred", "darkgreen", "brown", "darkblue", "purple", "teal", + "lightgray" -Exact behavior might depends on the terminal emulator you are using, and its settings. +Exact behavior might depends on the terminal emulator you are using, and its +settings. diff --git a/pygments/formatters/terminal256.py b/pygments/formatters/terminal256.py index 1aa19f25..03c3a42a 100644 --- a/pygments/formatters/terminal256.py +++ b/pygments/formatters/terminal256.py @@ -55,14 +55,14 @@ class EscapeSequence: self.bold = True # extract fg color code. attrs.append(esc[2:4]) - else : + else: attrs.extend(("38", "5", "%i" % self.fg)) if self.bg is not None: if self.bg in ansilist: esc = codes[self.bg[5:]] # extract fg color code, add 10 for bg. attrs.append(str(int(esc[2:4])+10)) - else : + else: attrs.extend(("48", "5", "%i" % self.bg)) if self.bold: attrs.append("01") @@ -103,15 +103,13 @@ class Terminal256Formatter(Formatter): and converts them to nearest ANSI 256-color escape sequences. Bold and underline attributes from the style are preserved (and displayed). - .. versionadded:: 2.2 - - If the used style defined foreground colors in the form `#ansi*`, then - `Terminal256Formatter` will map these to non extended foreground color. - - See AnsiTerminalStyle_ for more informations. - .. versionadded:: 0.9 + .. versionchanged:: 2.2 + If the used style defines foreground colors in the form ``#ansi*``, then + `Terminal256Formatter` will map these to non extended foreground color. + See :ref:`AnsiTerminalStyle` for more information. + Options accepted: `style` @@ -190,8 +188,8 @@ class Terminal256Formatter(Formatter): def _color_index(self, color): index = self.best_match.get(color, None) - if color in ansilist: - # strip the `#ansi` part an look up code + if color in ansilist: + # strip the `#ansi` part and look up code index = color self.best_match[color] = index if index is None: diff --git a/pygments/style.py b/pygments/style.py index 8c7de528..797a1d34 100644 --- a/pygments/style.py +++ b/pygments/style.py @@ -12,10 +12,9 @@ from pygments.token import Token, STANDARD_TYPES from pygments.util import add_metaclass - - +# Default mapping of #ansixxx to RGB colors. _ansimap = { - ## + # dark '#ansiblack': '000000', '#ansidarkred': '7f0000', '#ansidarkgreen': '007f00', @@ -24,7 +23,7 @@ _ansimap = { '#ansipurple': '7f007f', '#ansiteal': '007f7f', '#ansilightgray': 'e5e5e5', - ### normal + # normal '#ansidarkgray': '555555', '#ansired': 'ff0000', '#ansigreen': '00ff00', @@ -33,8 +32,9 @@ _ansimap = { '#ansifuchsia': 'ff00ff', '#ansiturquoise': '00ffff', '#ansiwhite': 'ffffff', - } -ansilist = set(_ansimap.keys()) +} +ansilist = set(_ansimap) + class StyleMeta(type): @@ -104,12 +104,11 @@ class StyleMeta(type): def style_for_token(cls, token): t = cls._styles[token] - ansicolor = None + ansicolor = bgansicolor = None color = t[0] if color.startswith('#ansi'): ansicolor = color color = _ansimap[color] - bgansicolor = None bgcolor = t[4] if bgcolor.startswith('#ansi'): bgansicolor = bgcolor @@ -126,8 +125,7 @@ class StyleMeta(type): 'sans': bool(t[7]) or None, 'mono': bool(t[8]) or None, 'ansicolor': ansicolor, - 'bgansicolor': bgansicolor, - + 'bgansicolor': bgansicolor, } def list_styles(cls): diff --git a/tests/test_lexers_other.py b/tests/test_lexers_other.py index 4c5132ad..90d05ef8 100644 --- a/tests/test_lexers_other.py +++ b/tests/test_lexers_other.py @@ -13,29 +13,10 @@ import unittest from pygments.lexers import guess_lexer from pygments.lexers.scripting import EasytrieveLexer, JclLexer, RexxLexer + def _exampleFilePath(filename): return os.path.join(os.path.dirname(__file__), 'examplefiles', filename) -_MAX_LENGTH = 80 - -def safe_repr(obj, short=False): - try: - result = repr(obj) - except Exception: - result = object.__repr__(obj) - if not short or len(result) < _MAX_LENGTH: - return result - return result[:_MAX_LENGTH] + ' [truncated]...' - - -class MyTestCase(unittest.TestCase): - ### Assert less is 2.7+ only. - def assertLess(self, a, b, msg=None): - """Just like self.assertTrue(a < b), but with a nicer default message.""" - if not a < b: - standardMsg = '%s not less than %s' % (safe_repr(a), safe_repr(b)) - self.fail(self._formatMessage(msg, standardMsg)) - class AnalyseTextTest(unittest.TestCase): def _testCanRecognizeAndGuessExampleFiles(self, lexer): @@ -48,8 +29,8 @@ class AnalyseTextTest(unittest.TestCase): text = fp.read().decode('utf-8') probability = lexer.analyse_text(text) self.assertTrue(probability > 0, - '%s must recognize %r' % ( - lexer.name, exampleFilePath)) + '%s must recognize %r' % ( + lexer.name, exampleFilePath)) guessedLexer = guess_lexer(text) self.assertEqual(guessedLexer.name, lexer.name) @@ -62,27 +43,27 @@ class AnalyseTextTest(unittest.TestCase): for lexerToTest in LEXERS_TO_TEST: self._testCanRecognizeAndGuessExampleFiles(lexerToTest) -class EasyTrieveLexerTest(MyTestCase): + +class EasyTrieveLexerTest(unittest.TestCase): def testCanGuessFromText(self): - self.assertLess(0, EasytrieveLexer.analyse_text('MACRO')) - self.assertLess(0, EasytrieveLexer.analyse_text('\nMACRO')) - self.assertLess(0, EasytrieveLexer.analyse_text(' \nMACRO')) - self.assertLess(0, EasytrieveLexer.analyse_text(' \n MACRO')) - self.assertLess(0, EasytrieveLexer.analyse_text('*\nMACRO')) - self.assertLess(0, EasytrieveLexer.analyse_text( + self.assertTrue(EasytrieveLexer.analyse_text('MACRO')) + self.assertTrue(EasytrieveLexer.analyse_text('\nMACRO')) + self.assertTrue(EasytrieveLexer.analyse_text(' \nMACRO')) + self.assertTrue(EasytrieveLexer.analyse_text(' \n MACRO')) + self.assertTrue(EasytrieveLexer.analyse_text('*\nMACRO')) + self.assertTrue(EasytrieveLexer.analyse_text( '*\n *\n\n \n*\n MACRO')) class RexxLexerTest(unittest.TestCase): def testCanGuessFromText(self): - self.assertAlmostEqual(0.01, - RexxLexer.analyse_text('/* */')) + self.assertAlmostEqual(0.01, RexxLexer.analyse_text('/* */')) self.assertAlmostEqual(1.0, - RexxLexer.analyse_text('''/* Rexx */ + RexxLexer.analyse_text('''/* Rexx */ say "hello world"''')) val = RexxLexer.analyse_text('/* */\n' - 'hello:pRoceduRe\n' - ' say "hello world"') + 'hello:pRoceduRe\n' + ' say "hello world"') self.assertTrue(val > 0.5, val) val = RexxLexer.analyse_text('''/* */ if 1 > 0 then do diff --git a/tests/test_terminal_formatter.py b/tests/test_terminal_formatter.py index 84373790..cb5c6c44 100644 --- a/tests/test_terminal_formatter.py +++ b/tests/test_terminal_formatter.py @@ -14,7 +14,8 @@ import re from pygments.util import StringIO from pygments.lexers.sql import PlPgsqlLexer -from pygments.formatters import TerminalFormatter,Terminal256Formatter, HtmlFormatter, LatexFormatter +from pygments.formatters import TerminalFormatter, Terminal256Formatter, \ + HtmlFormatter, LatexFormatter from pygments.style import Style from pygments.token import Token @@ -31,9 +32,11 @@ DEMO_TOKENS = list(DEMO_LEXER().get_tokens(DEMO_TEXT)) ANSI_RE = re.compile(r'\x1b[\w\W]*?m') + def strip_ansi(x): return ANSI_RE.sub('', x) + class TerminalFormatterTest(unittest.TestCase): def test_reasonable_output(self): out = StringIO() @@ -56,22 +59,17 @@ class TerminalFormatterTest(unittest.TestCase): self.assertTrue(a in b) - - - - class MyStyle(Style): - styles = { Token.Comment: '#ansidarkgray', - Token.String: '#ansiblue bg:#ansidarkred', - Token.Number : '#ansigreen bg:#ansidarkgreen', - Token.Number.Hex: '#ansidarkgreen bg:#ansired', + Token.String: '#ansiblue bg:#ansidarkred', + Token.Number: '#ansigreen bg:#ansidarkgreen', + Token.Number.Hex: '#ansidarkgreen bg:#ansired', } - -code = ''' +class Terminal256FormatterTest(unittest.TestCase): + code = ''' # this should be a comment print("Hello World") async def function(a,b,c, *d, **kwarg:Bool)->Bool: @@ -79,49 +77,26 @@ async def function(a,b,c, *d, **kwarg:Bool)->Bool: return 123, 0xb3e3 ''' -_MAX_LENGTH = 80 - -def safe_repr(obj, short=False): - try: - result = repr(obj) - except Exception: - result = object.__repr__(obj) - if not short or len(result) < _MAX_LENGTH: - return result - return result[:_MAX_LENGTH] + ' [truncated]...' - -class MyTest(unittest.TestCase): - - def assertIn(self, member, container, msg=None): - """Just like self.assertTrue(a in b), but with a nicer default message.""" - if member not in container: - standardMsg = '%s not found in %s' % (safe_repr(member), - safe_repr(container)) - self.fail(self._formatMessage(msg, standardMsg)) - - -termtest = lambda x: highlight(x, Python3Lexer(), Terminal256Formatter(style=MyStyle)) -class Terminal256FormatterTest(MyTest): - def test_style_html(self): style = HtmlFormatter(style=MyStyle).get_style_defs() - self.assertIn('#555555',style, "ansigray for comment not html css style") + self.assertTrue('#555555' in style, + "ansigray for comment not html css style") - def test_tex_works(self): - """check tex Formatter don't crash""" - highlight(code, Python3Lexer(), LatexFormatter(style=MyStyle)) - - def test_html_works(self): - highlight(code, Python3Lexer(), HtmlFormatter(style=MyStyle)) + def test_others_work(self): + """check other formatters don't crash""" + highlight(self.code, Python3Lexer(), LatexFormatter(style=MyStyle)) + highlight(self.code, Python3Lexer(), HtmlFormatter(style=MyStyle)) def test_256esc_seq(self): """ test that a few escape sequences are actualy used when using #ansi<> color codes """ - self.assertIn('32;41',termtest('0x123')) - self.assertIn('32;42',termtest('123')) - self.assertIn('30;01',termtest('#comment')) - self.assertIn('34;41',termtest('"String"')) - - + def termtest(x): + return highlight(x, Python3Lexer(), + Terminal256Formatter(style=MyStyle)) + + self.assertTrue('32;41' in termtest('0x123')) + self.assertTrue('32;42' in termtest('123')) + self.assertTrue('30;01' in termtest('#comment')) + self.assertTrue('34;41' in termtest('"String"')) |