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