Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

# -*- coding: utf-8 -*- 

""" 

    pygments.formatters.terminal256 

    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 

 

    Formatter for 256-color terminal output with ANSI sequences. 

 

    RGB-to-XTERM color conversion routines adapted from xterm256-conv 

    tool (http://frexx.de/xterm-256-notes/data/xterm256-conv2.tar.bz2) 

    by Wolfgang Frisch. 

 

    Formatter version 1. 

 

    :copyright: Copyright 2006-2014 by the Pygments team, see AUTHORS. 

    :license: BSD, see LICENSE for details. 

""" 

 

# TODO: 

#  - Options to map style's bold/underline/italic/border attributes 

#    to some ANSI attrbutes (something like 'italic=underline') 

#  - An option to output "style RGB to xterm RGB/index" conversion table 

#  - An option to indicate that we are running in "reverse background" 

#    xterm. This means that default colors are white-on-black, not 

#    black-on-while, so colors like "white background" need to be converted 

#    to "white background, black foreground", etc... 

 

import sys 

 

from pygments.formatter import Formatter 

 

 

__all__ = ['Terminal256Formatter'] 

 

 

class EscapeSequence: 

    def __init__(self, fg=None, bg=None, bold=False, underline=False): 

        self.fg = fg 

        self.bg = bg 

        self.bold = bold 

        self.underline = underline 

 

    def escape(self, attrs): 

        if len(attrs): 

            return "\x1b[" + ";".join(attrs) + "m" 

        return "" 

 

    def color_string(self): 

        attrs = [] 

        if self.fg is not None: 

            attrs.extend(("38", "5", "%i" % self.fg)) 

        if self.bg is not None: 

            attrs.extend(("48", "5", "%i" % self.bg)) 

        if self.bold: 

            attrs.append("01") 

        if self.underline: 

            attrs.append("04") 

        return self.escape(attrs) 

 

    def reset_string(self): 

        attrs = [] 

        if self.fg is not None: 

            attrs.append("39") 

        if self.bg is not None: 

            attrs.append("49") 

        if self.bold or self.underline: 

            attrs.append("00") 

        return self.escape(attrs) 

 

 

class Terminal256Formatter(Formatter): 

    r""" 

    Format tokens with ANSI color sequences, for output in a 256-color 

    terminal or console. Like in `TerminalFormatter` color sequences 

    are terminated at newlines, so that paging the output works correctly. 

 

    The formatter takes colors from a style defined by the `style` option 

    and converts them to nearest ANSI 256-color escape sequences. Bold and 

    underline attributes from the style are preserved (and displayed). 

 

    .. versionadded:: 0.9 

 

    Options accepted: 

 

    `style` 

        The style to use, can be a string or a Style subclass (default: 

        ``'default'``). 

    """ 

    name = 'Terminal256' 

    aliases = ['terminal256', 'console256', '256'] 

    filenames = [] 

 

    def __init__(self, **options): 

        Formatter.__init__(self, **options) 

 

        self.xterm_colors = [] 

        self.best_match = {} 

        self.style_string = {} 

 

        self.usebold = 'nobold' not in options 

        self.useunderline = 'nounderline' not in options 

 

        self._build_color_table()  # build an RGB-to-256 color conversion table 

        self._setup_styles()  # convert selected style's colors to term. colors 

 

    def _build_color_table(self): 

        # colors 0..15: 16 basic colors 

 

        self.xterm_colors.append((0x00, 0x00, 0x00))  # 0 

        self.xterm_colors.append((0xcd, 0x00, 0x00))  # 1 

        self.xterm_colors.append((0x00, 0xcd, 0x00))  # 2 

        self.xterm_colors.append((0xcd, 0xcd, 0x00))  # 3 

        self.xterm_colors.append((0x00, 0x00, 0xee))  # 4 

        self.xterm_colors.append((0xcd, 0x00, 0xcd))  # 5 

        self.xterm_colors.append((0x00, 0xcd, 0xcd))  # 6 

        self.xterm_colors.append((0xe5, 0xe5, 0xe5))  # 7 

        self.xterm_colors.append((0x7f, 0x7f, 0x7f))  # 8 

        self.xterm_colors.append((0xff, 0x00, 0x00))  # 9 

        self.xterm_colors.append((0x00, 0xff, 0x00))  # 10 

        self.xterm_colors.append((0xff, 0xff, 0x00))  # 11 

        self.xterm_colors.append((0x5c, 0x5c, 0xff))  # 12 

        self.xterm_colors.append((0xff, 0x00, 0xff))  # 13 

        self.xterm_colors.append((0x00, 0xff, 0xff))  # 14 

        self.xterm_colors.append((0xff, 0xff, 0xff))  # 15 

 

        # colors 16..232: the 6x6x6 color cube 

 

        valuerange = (0x00, 0x5f, 0x87, 0xaf, 0xd7, 0xff) 

 

        for i in range(217): 

            r = valuerange[(i // 36) % 6] 

            g = valuerange[(i // 6) % 6] 

            b = valuerange[i % 6] 

            self.xterm_colors.append((r, g, b)) 

 

        # colors 233..253: grayscale 

 

        for i in range(1, 22): 

            v = 8 + i * 10 

            self.xterm_colors.append((v, v, v)) 

 

    def _closest_color(self, r, g, b): 

        distance = 257*257*3  # "infinity" (>distance from #000000 to #ffffff) 

        match = 0 

 

        for i in range(0, 254): 

            values = self.xterm_colors[i] 

 

            rd = r - values[0] 

            gd = g - values[1] 

            bd = b - values[2] 

            d = rd*rd + gd*gd + bd*bd 

 

            if d < distance: 

                match = i 

                distance = d 

        return match 

 

    def _color_index(self, color): 

        index = self.best_match.get(color, None) 

        if index is None: 

            try: 

                rgb = int(str(color), 16) 

            except ValueError: 

                rgb = 0 

 

            r = (rgb >> 16) & 0xff 

            g = (rgb >> 8) & 0xff 

            b = rgb & 0xff 

            index = self._closest_color(r, g, b) 

            self.best_match[color] = index 

        return index 

 

    def _setup_styles(self): 

        for ttype, ndef in self.style: 

            escape = EscapeSequence() 

            if ndef['color']: 

                escape.fg = self._color_index(ndef['color']) 

            if ndef['bgcolor']: 

                escape.bg = self._color_index(ndef['bgcolor']) 

            if self.usebold and ndef['bold']: 

                escape.bold = True 

            if self.useunderline and ndef['underline']: 

                escape.underline = True 

            self.style_string[str(ttype)] = (escape.color_string(), 

                                             escape.reset_string()) 

 

    def format(self, tokensource, outfile): 

        # hack: if the output is a terminal and has an encoding set, 

        # use that to avoid unicode encode problems 

        if not self.encoding and hasattr(outfile, "encoding") and \ 

           hasattr(outfile, "isatty") and outfile.isatty() and \ 

           sys.version_info < (3,): 

            self.encoding = outfile.encoding 

        return Formatter.format(self, tokensource, outfile) 

 

    def format_unencoded(self, tokensource, outfile): 

        for ttype, value in tokensource: 

            not_found = True 

            while ttype and not_found: 

                try: 

                    # outfile.write( "<" + str(ttype) + ">" ) 

                    on, off = self.style_string[str(ttype)] 

 

                    # Like TerminalFormatter, add "reset colors" escape sequence 

                    # on newline. 

                    spl = value.split('\n') 

                    for line in spl[:-1]: 

                        if line: 

                            outfile.write(on + line + off) 

                        outfile.write('\n') 

                    if spl[-1]: 

                        outfile.write(on + spl[-1] + off) 

 

                    not_found = False 

                    # outfile.write( '#' + str(ttype) + '#' ) 

 

                except KeyError: 

                    # ottype = ttype 

                    ttype = ttype[:-1] 

                    # outfile.write( '!' + str(ottype) + '->' + str(ttype) + '!' ) 

 

            if not_found: 

                outfile.write(value)