terminal.py 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. # -*- coding: utf-8 -*-
  2. """
  3. pygments.formatters.terminal
  4. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  5. Formatter for terminal output with ANSI sequences.
  6. :copyright: Copyright 2006-2019 by the Pygments team, see AUTHORS.
  7. :license: BSD, see LICENSE for details.
  8. """
  9. import sys
  10. from pygments.formatter import Formatter
  11. from pygments.token import Keyword, Name, Comment, String, Error, \
  12. Number, Operator, Generic, Token, Whitespace
  13. from pygments.console import ansiformat
  14. from pygments.util import get_choice_opt
  15. __all__ = ['TerminalFormatter']
  16. #: Map token types to a tuple of color values for light and dark
  17. #: backgrounds.
  18. TERMINAL_COLORS = {
  19. Token: ('', ''),
  20. Whitespace: ('gray', 'brightblack'),
  21. Comment: ('gray', 'brightblack'),
  22. Comment.Preproc: ('cyan', 'brightcyan'),
  23. Keyword: ('blue', 'brightblue'),
  24. Keyword.Type: ('cyan', 'brightcyan'),
  25. Operator.Word: ('magenta', 'brightmagenta'),
  26. Name.Builtin: ('cyan', 'brightcyan'),
  27. Name.Function: ('green', 'brightgreen'),
  28. Name.Namespace: ('_cyan_', '_brightcyan_'),
  29. Name.Class: ('_green_', '_brightgreen_'),
  30. Name.Exception: ('cyan', 'brightcyan'),
  31. Name.Decorator: ('brightblack', 'gray'),
  32. Name.Variable: ('red', 'brightred'),
  33. Name.Constant: ('red', 'brightred'),
  34. Name.Attribute: ('cyan', 'brightcyan'),
  35. Name.Tag: ('brightblue', 'brightblue'),
  36. String: ('yellow', 'yellow'),
  37. Number: ('blue', 'brightblue'),
  38. Generic.Deleted: ('brightred', 'brightred'),
  39. Generic.Inserted: ('green', 'brightgreen'),
  40. Generic.Heading: ('**', '**'),
  41. Generic.Subheading: ('*magenta*', '*brightmagenta*'),
  42. Generic.Prompt: ('**', '**'),
  43. Generic.Error: ('brightred', 'brightred'),
  44. Error: ('_brightred_', '_brightred_'),
  45. }
  46. class TerminalFormatter(Formatter):
  47. r"""
  48. Format tokens with ANSI color sequences, for output in a text console.
  49. Color sequences are terminated at newlines, so that paging the output
  50. works correctly.
  51. The `get_style_defs()` method doesn't do anything special since there is
  52. no support for common styles.
  53. Options accepted:
  54. `bg`
  55. Set to ``"light"`` or ``"dark"`` depending on the terminal's background
  56. (default: ``"light"``).
  57. `colorscheme`
  58. A dictionary mapping token types to (lightbg, darkbg) color names or
  59. ``None`` (default: ``None`` = use builtin colorscheme).
  60. `linenos`
  61. Set to ``True`` to have line numbers on the terminal output as well
  62. (default: ``False`` = no line numbers).
  63. """
  64. name = 'Terminal'
  65. aliases = ['terminal', 'console']
  66. filenames = []
  67. def __init__(self, **options):
  68. Formatter.__init__(self, **options)
  69. self.darkbg = get_choice_opt(options, 'bg',
  70. ['light', 'dark'], 'light') == 'dark'
  71. self.colorscheme = options.get('colorscheme', None) or TERMINAL_COLORS
  72. self.linenos = options.get('linenos', False)
  73. self._lineno = 0
  74. def format(self, tokensource, outfile):
  75. # hack: if the output is a terminal and has an encoding set,
  76. # use that to avoid unicode encode problems
  77. if not self.encoding and hasattr(outfile, "encoding") and \
  78. hasattr(outfile, "isatty") and outfile.isatty() and \
  79. sys.version_info < (3,):
  80. self.encoding = outfile.encoding
  81. return Formatter.format(self, tokensource, outfile)
  82. def _write_lineno(self, outfile):
  83. self._lineno += 1
  84. outfile.write("%s%04d: " % (self._lineno != 1 and '\n' or '', self._lineno))
  85. def _get_color(self, ttype):
  86. # self.colorscheme is a dict containing usually generic types, so we
  87. # have to walk the tree of dots. The base Token type must be a key,
  88. # even if it's empty string, as in the default above.
  89. colors = self.colorscheme.get(ttype)
  90. while colors is None:
  91. ttype = ttype.parent
  92. colors = self.colorscheme.get(ttype)
  93. return colors[self.darkbg]
  94. def format_unencoded(self, tokensource, outfile):
  95. if self.linenos:
  96. self._write_lineno(outfile)
  97. for ttype, value in tokensource:
  98. color = self._get_color(ttype)
  99. for line in value.splitlines(True):
  100. if color:
  101. outfile.write(ansiformat(color, line.rstrip('\n')))
  102. else:
  103. outfile.write(line.rstrip('\n'))
  104. if line.endswith('\n'):
  105. if self.linenos:
  106. self._write_lineno(outfile)
  107. else:
  108. outfile.write('\n')
  109. if self.linenos:
  110. outfile.write("\n")