]>
Commit | Line | Data |
---|---|---|
e0df8241 JR |
1 | """ |
2 | pygments.formatters.groff | |
3 | ~~~~~~~~~~~~~~~~~~~~~~~~~ | |
4 | ||
5 | Formatter for groff output. | |
6 | ||
7 | :copyright: Copyright 2006-2023 by the Pygments team, see AUTHORS. | |
8 | :license: BSD, see LICENSE for details. | |
9 | """ | |
10 | ||
11 | import math | |
12 | from pip._vendor.pygments.formatter import Formatter | |
13 | from pip._vendor.pygments.util import get_bool_opt, get_int_opt | |
14 | ||
15 | __all__ = ['GroffFormatter'] | |
16 | ||
17 | ||
18 | class GroffFormatter(Formatter): | |
19 | """ | |
20 | Format tokens with groff escapes to change their color and font style. | |
21 | ||
22 | .. versionadded:: 2.11 | |
23 | ||
24 | Additional options accepted: | |
25 | ||
26 | `style` | |
27 | The style to use, can be a string or a Style subclass (default: | |
28 | ``'default'``). | |
29 | ||
30 | `monospaced` | |
31 | If set to true, monospace font will be used (default: ``true``). | |
32 | ||
33 | `linenos` | |
34 | If set to true, print the line numbers (default: ``false``). | |
35 | ||
36 | `wrap` | |
37 | Wrap lines to the specified number of characters. Disabled if set to 0 | |
38 | (default: ``0``). | |
39 | """ | |
40 | ||
41 | name = 'groff' | |
42 | aliases = ['groff','troff','roff'] | |
43 | filenames = [] | |
44 | ||
45 | def __init__(self, **options): | |
46 | Formatter.__init__(self, **options) | |
47 | ||
48 | self.monospaced = get_bool_opt(options, 'monospaced', True) | |
49 | self.linenos = get_bool_opt(options, 'linenos', False) | |
50 | self._lineno = 0 | |
51 | self.wrap = get_int_opt(options, 'wrap', 0) | |
52 | self._linelen = 0 | |
53 | ||
54 | self.styles = {} | |
55 | self._make_styles() | |
56 | ||
57 | ||
58 | def _make_styles(self): | |
59 | regular = '\\f[CR]' if self.monospaced else '\\f[R]' | |
60 | bold = '\\f[CB]' if self.monospaced else '\\f[B]' | |
61 | italic = '\\f[CI]' if self.monospaced else '\\f[I]' | |
62 | ||
63 | for ttype, ndef in self.style: | |
64 | start = end = '' | |
65 | if ndef['color']: | |
66 | start += '\\m[%s]' % ndef['color'] | |
67 | end = '\\m[]' + end | |
68 | if ndef['bold']: | |
69 | start += bold | |
70 | end = regular + end | |
71 | if ndef['italic']: | |
72 | start += italic | |
73 | end = regular + end | |
74 | if ndef['bgcolor']: | |
75 | start += '\\M[%s]' % ndef['bgcolor'] | |
76 | end = '\\M[]' + end | |
77 | ||
78 | self.styles[ttype] = start, end | |
79 | ||
80 | ||
81 | def _define_colors(self, outfile): | |
82 | colors = set() | |
83 | for _, ndef in self.style: | |
84 | if ndef['color'] is not None: | |
85 | colors.add(ndef['color']) | |
86 | ||
87 | for color in sorted(colors): | |
88 | outfile.write('.defcolor ' + color + ' rgb #' + color + '\n') | |
89 | ||
90 | ||
91 | def _write_lineno(self, outfile): | |
92 | self._lineno += 1 | |
93 | outfile.write("%s% 4d " % (self._lineno != 1 and '\n' or '', self._lineno)) | |
94 | ||
95 | ||
96 | def _wrap_line(self, line): | |
97 | length = len(line.rstrip('\n')) | |
98 | space = ' ' if self.linenos else '' | |
99 | newline = '' | |
100 | ||
101 | if length > self.wrap: | |
102 | for i in range(0, math.floor(length / self.wrap)): | |
103 | chunk = line[i*self.wrap:i*self.wrap+self.wrap] | |
104 | newline += (chunk + '\n' + space) | |
105 | remainder = length % self.wrap | |
106 | if remainder > 0: | |
107 | newline += line[-remainder-1:] | |
108 | self._linelen = remainder | |
109 | elif self._linelen + length > self.wrap: | |
110 | newline = ('\n' + space) + line | |
111 | self._linelen = length | |
112 | else: | |
113 | newline = line | |
114 | self._linelen += length | |
115 | ||
116 | return newline | |
117 | ||
118 | ||
119 | def _escape_chars(self, text): | |
120 | text = text.replace('\\', '\\[u005C]'). \ | |
121 | replace('.', '\\[char46]'). \ | |
122 | replace('\'', '\\[u0027]'). \ | |
123 | replace('`', '\\[u0060]'). \ | |
124 | replace('~', '\\[u007E]') | |
125 | copy = text | |
126 | ||
127 | for char in copy: | |
128 | if len(char) != len(char.encode()): | |
129 | uni = char.encode('unicode_escape') \ | |
130 | .decode()[1:] \ | |
131 | .replace('x', 'u00') \ | |
132 | .upper() | |
133 | text = text.replace(char, '\\[u' + uni[1:] + ']') | |
134 | ||
135 | return text | |
136 | ||
137 | ||
138 | def format_unencoded(self, tokensource, outfile): | |
139 | self._define_colors(outfile) | |
140 | ||
141 | outfile.write('.nf\n\\f[CR]\n') | |
142 | ||
143 | if self.linenos: | |
144 | self._write_lineno(outfile) | |
145 | ||
146 | for ttype, value in tokensource: | |
147 | while ttype not in self.styles: | |
148 | ttype = ttype.parent | |
149 | start, end = self.styles[ttype] | |
150 | ||
151 | for line in value.splitlines(True): | |
152 | if self.wrap > 0: | |
153 | line = self._wrap_line(line) | |
154 | ||
155 | if start and end: | |
156 | text = self._escape_chars(line.rstrip('\n')) | |
157 | if text != '': | |
158 | outfile.write(''.join((start, text, end))) | |
159 | else: | |
160 | outfile.write(self._escape_chars(line.rstrip('\n'))) | |
161 | ||
162 | if line.endswith('\n'): | |
163 | if self.linenos: | |
164 | self._write_lineno(outfile) | |
165 | self._linelen = 0 | |
166 | else: | |
167 | outfile.write('\n') | |
168 | self._linelen = 0 | |
169 | ||
170 | outfile.write('\n.fi') |