codekingpro/portable-devtools
114k
1import json2from typing import Optional, Type, Tuple3 4import pygments.formatters5import pygments.lexer6import pygments.lexers7import pygments.style8import pygments.styles9import pygments.token10from pygments.formatters.terminal import TerminalFormatter11from pygments.formatters.terminal256 import Terminal256Formatter12from pygments.lexer import Lexer13from pygments.lexers.data import JsonLexer14from pygments.lexers.special import TextLexer15from pygments.lexers.text import HttpLexer as PygmentsHttpLexer16from pygments.util import ClassNotFound17 18from ..lexers.json import EnhancedJsonLexer19from ..lexers.metadata import MetadataLexer20from ..ui.palette import AUTO_STYLE, SHADE_TO_PIE_STYLE, PieColor, ColorString, get_color21from ...context import Environment22from ...plugins import FormatterPlugin23 24 25DEFAULT_STYLE = AUTO_STYLE26SOLARIZED_STYLE = 'solarized' # Bundled here27PYGMENTS_BOLD = ColorString('bold')28PYGMENTS_ITALIC = ColorString('italic')29 30BUNDLED_STYLES = {31 SOLARIZED_STYLE,32 AUTO_STYLE33}34 35 36def get_available_styles():37 return sorted(BUNDLED_STYLES | set(pygments.styles.get_all_styles()))38 39 40class ColorFormatter(FormatterPlugin):41 """42 Colorize using Pygments43 44 This processor that applies syntax highlighting to the headers,45 and also to the body if its content type is recognized.46 47 """48 group_name = 'colors'49 metadata_lexer = MetadataLexer()50 51 def __init__(52 self,53 env: Environment,54 explicit_json=False,55 color_scheme=DEFAULT_STYLE,56 **kwargs57 ):58 super().__init__(**kwargs)59 60 if not env.colors:61 self.enabled = False62 return63 64 use_auto_style = color_scheme == AUTO_STYLE65 has_256_colors = env.colors == 25666 if use_auto_style or not has_256_colors:67 http_lexer = PygmentsHttpLexer()68 body_formatter = header_formatter = TerminalFormatter()69 precise = False70 else:71 from ..lexers.http import SimplifiedHTTPLexer72 header_formatter, body_formatter, precise = self.get_formatters(color_scheme)73 http_lexer = SimplifiedHTTPLexer(precise=precise)74 75 self.explicit_json = explicit_json # --json76 self.header_formatter = header_formatter77 self.body_formatter = body_formatter78 self.http_lexer = http_lexer79 self.metadata_lexer = MetadataLexer(precise=precise)80 81 def format_headers(self, headers: str) -> str:82 return pygments.highlight(83 code=headers,84 lexer=self.http_lexer,85 formatter=self.header_formatter,86 ).strip()87 88 def format_body(self, body: str, mime: str) -> str:89 lexer = self.get_lexer_for_body(mime, body)90 if lexer:91 body = pygments.highlight(92 code=body,93 lexer=lexer,94 formatter=self.body_formatter,95 )96 return body97 98 def format_metadata(self, metadata: str) -> str:99 return pygments.highlight(100 code=metadata,101 lexer=self.metadata_lexer,102 formatter=self.header_formatter,103 ).strip()104 105 def get_lexer_for_body(106 self, mime: str,107 body: str108 ) -> Optional[Type[Lexer]]:109 return get_lexer(110 mime=mime,111 explicit_json=self.explicit_json,112 body=body,113 )114 115 def get_formatters(self, color_scheme: str) -> Tuple[116 pygments.formatter.Formatter,117 pygments.formatter.Formatter,118 bool119 ]:120 if color_scheme in PIE_STYLES:121 header_style, body_style = PIE_STYLES[color_scheme]122 precise = True123 else:124 header_style = self.get_style_class(color_scheme)125 body_style = header_style126 precise = False127 128 return (129 Terminal256Formatter(style=header_style),130 Terminal256Formatter(style=body_style),131 precise132 )133 134 @staticmethod135 def get_style_class(color_scheme: str) -> Type[pygments.style.Style]:136 try:137 return pygments.styles.get_style_by_name(color_scheme)138 except ClassNotFound:139 return Solarized256Style140 141 142def get_lexer(143 mime: str,144 explicit_json=False,145 body=''146) -> Optional[Type[Lexer]]:147 # Build candidate mime type and lexer names.148 mime_types, lexer_names = [mime], []149 type_, subtype = mime.split('/', 1)150 if '+' not in subtype:151 lexer_names.append(subtype)152 else:153 subtype_name, subtype_suffix = subtype.split('+', 1)154 lexer_names.extend([subtype_name, subtype_suffix])155 mime_types.extend([156 f'{type_}/{subtype_name}',157 f'{type_}/{subtype_suffix}',158 ])159 160 # As a last resort, if no lexer feels responsible, and161 # the subtype contains 'json', take the JSON lexer162 if 'json' in subtype:163 lexer_names.append('json')164 165 # Try to resolve the right lexer.166 lexer = None167 for mime_type in mime_types:168 try:169 lexer = pygments.lexers.get_lexer_for_mimetype(mime_type)170 break171 except ClassNotFound:172 pass173 else:174 for name in lexer_names:175 try:176 lexer = pygments.lexers.get_lexer_by_name(name)177 except ClassNotFound:178 pass179 180 if explicit_json and body and (not lexer or isinstance(lexer, TextLexer)):181 # JSON response with an incorrect Content-Type?182 try:183 json.loads(body) # FIXME: the body also gets parsed in json.py184 except ValueError:185 pass # Nope186 else:187 lexer = pygments.lexers.get_lexer_by_name('json')188 189 # Use our own JSON lexer: it supports JSON bodies preceded by non-JSON data190 # as well as legit JSON bodies.191 if isinstance(lexer, JsonLexer):192 lexer = EnhancedJsonLexer()193 194 return lexer195 196 197class Solarized256Style(pygments.style.Style):198 """199 solarized256200 ------------201 202 A Pygments style inspired by Solarized's 256 color mode.203 204 :copyright: (c) 2011 by Hank Gay, (c) 2012 by John Mastro.205 :license: BSD, see LICENSE for more details.206 207 """208 BASE03 = "#1c1c1c"209 BASE02 = "#262626"210 BASE01 = "#4e4e4e"211 BASE00 = "#585858"212 BASE0 = "#808080"213 BASE1 = "#8a8a8a"214 BASE2 = "#d7d7af"215 BASE3 = "#ffffd7"216 YELLOW = "#af8700"217 ORANGE = "#d75f00"218 RED = "#af0000"219 MAGENTA = "#af005f"220 VIOLET = "#5f5faf"221 BLUE = "#0087ff"222 CYAN = "#00afaf"223 GREEN = "#5f8700"224 225 background_color = BASE03226 styles = {227 pygments.token.Keyword: GREEN,228 pygments.token.Keyword.Constant: ORANGE,229 pygments.token.Keyword.Declaration: BLUE,230 pygments.token.Keyword.Namespace: ORANGE,231 pygments.token.Keyword.Reserved: BLUE,232 pygments.token.Keyword.Type: RED,233 pygments.token.Name.Attribute: BASE1,234 pygments.token.Name.Builtin: BLUE,235 pygments.token.Name.Builtin.Pseudo: BLUE,236 pygments.token.Name.Class: BLUE,237 pygments.token.Name.Constant: ORANGE,238 pygments.token.Name.Decorator: BLUE,239 pygments.token.Name.Entity: ORANGE,240 pygments.token.Name.Exception: YELLOW,241 pygments.token.Name.Function: BLUE,242 pygments.token.Name.Tag: BLUE,243 pygments.token.Name.Variable: BLUE,244 pygments.token.String: CYAN,245 pygments.token.String.Backtick: BASE01,246 pygments.token.String.Char: CYAN,247 pygments.token.String.Doc: CYAN,248 pygments.token.String.Escape: RED,249 pygments.token.String.Heredoc: CYAN,250 pygments.token.String.Regex: RED,251 pygments.token.Number: CYAN,252 pygments.token.Operator: BASE1,253 pygments.token.Operator.Word: GREEN,254 pygments.token.Comment: BASE01,255 pygments.token.Comment.Preproc: GREEN,256 pygments.token.Comment.Special: GREEN,257 pygments.token.Generic.Deleted: CYAN,258 pygments.token.Generic.Emph: PYGMENTS_ITALIC,259 pygments.token.Generic.Error: RED,260 pygments.token.Generic.Heading: ORANGE,261 pygments.token.Generic.Inserted: GREEN,262 pygments.token.Generic.Strong: PYGMENTS_BOLD,263 pygments.token.Generic.Subheading: ORANGE,264 pygments.token.Token: BASE1,265 pygments.token.Token.Other: ORANGE,266 }267 268 269PIE_HEADER_STYLE = {270 # HTTP line / Headers / Etc.271 pygments.token.Name.Namespace: PYGMENTS_BOLD | PieColor.PRIMARY,272 pygments.token.Keyword.Reserved: PYGMENTS_BOLD | PieColor.GREY,273 pygments.token.Operator: PYGMENTS_BOLD | PieColor.GREY,274 pygments.token.Number: PYGMENTS_BOLD | PieColor.GREY,275 pygments.token.Name.Function.Magic: PYGMENTS_BOLD | PieColor.GREEN,276 pygments.token.Name.Exception: PYGMENTS_BOLD | PieColor.GREEN,277 pygments.token.Name.Attribute: PieColor.BLUE,278 pygments.token.String: PieColor.PRIMARY,279 280 # HTTP Methods281 pygments.token.Name.Function: PYGMENTS_BOLD | PieColor.GREY,282 pygments.token.Name.Function.HTTP.GET: PYGMENTS_BOLD | PieColor.GREEN,283 pygments.token.Name.Function.HTTP.HEAD: PYGMENTS_BOLD | PieColor.GREEN,284 pygments.token.Name.Function.HTTP.POST: PYGMENTS_BOLD | PieColor.YELLOW,285 pygments.token.Name.Function.HTTP.PUT: PYGMENTS_BOLD | PieColor.ORANGE,286 pygments.token.Name.Function.HTTP.PATCH: PYGMENTS_BOLD | PieColor.ORANGE,287 pygments.token.Name.Function.HTTP.DELETE: PYGMENTS_BOLD | PieColor.RED,288 289 # HTTP status codes290 pygments.token.Number.HTTP.INFO: PYGMENTS_BOLD | PieColor.AQUA,291 pygments.token.Number.HTTP.OK: PYGMENTS_BOLD | PieColor.GREEN,292 pygments.token.Number.HTTP.REDIRECT: PYGMENTS_BOLD | PieColor.YELLOW,293 pygments.token.Number.HTTP.CLIENT_ERR: PYGMENTS_BOLD | PieColor.ORANGE,294 pygments.token.Number.HTTP.SERVER_ERR: PYGMENTS_BOLD | PieColor.RED,295 296 # Metadata297 pygments.token.Name.Decorator: PieColor.GREY,298 pygments.token.Number.SPEED.FAST: PYGMENTS_BOLD | PieColor.GREEN,299 pygments.token.Number.SPEED.AVG: PYGMENTS_BOLD | PieColor.YELLOW,300 pygments.token.Number.SPEED.SLOW: PYGMENTS_BOLD | PieColor.ORANGE,301 pygments.token.Number.SPEED.VERY_SLOW: PYGMENTS_BOLD | PieColor.RED,302}303 304PIE_BODY_STYLE = {305 # {}[]:306 pygments.token.Punctuation: PieColor.GREY,307 308 # Keys309 pygments.token.Name.Tag: PieColor.PINK,310 311 # Values312 pygments.token.Literal.String: PieColor.GREEN,313 pygments.token.Literal.String.Double: PieColor.GREEN,314 pygments.token.Literal.Number: PieColor.AQUA,315 pygments.token.Keyword: PieColor.ORANGE,316 317 # Other stuff318 pygments.token.Text: PieColor.PRIMARY,319 pygments.token.Name.Attribute: PieColor.PRIMARY,320 pygments.token.Name.Builtin: PieColor.BLUE,321 pygments.token.Name.Builtin.Pseudo: PieColor.BLUE,322 pygments.token.Name.Class: PieColor.BLUE,323 pygments.token.Name.Constant: PieColor.ORANGE,324 pygments.token.Name.Decorator: PieColor.BLUE,325 pygments.token.Name.Entity: PieColor.ORANGE,326 pygments.token.Name.Exception: PieColor.YELLOW,327 pygments.token.Name.Function: PieColor.BLUE,328 pygments.token.Name.Variable: PieColor.BLUE,329 pygments.token.String: PieColor.AQUA,330 pygments.token.String.Backtick: PieColor.SECONDARY,331 pygments.token.String.Char: PieColor.AQUA,332 pygments.token.String.Doc: PieColor.AQUA,333 pygments.token.String.Escape: PieColor.RED,334 pygments.token.String.Heredoc: PieColor.AQUA,335 pygments.token.String.Regex: PieColor.RED,336 pygments.token.Number: PieColor.AQUA,337 pygments.token.Operator: PieColor.PRIMARY,338 pygments.token.Operator.Word: PieColor.GREEN,339 pygments.token.Comment: PieColor.SECONDARY,340 pygments.token.Comment.Preproc: PieColor.GREEN,341 pygments.token.Comment.Special: PieColor.GREEN,342 pygments.token.Generic.Deleted: PieColor.AQUA,343 pygments.token.Generic.Emph: PYGMENTS_ITALIC,344 pygments.token.Generic.Error: PieColor.RED,345 pygments.token.Generic.Heading: PieColor.ORANGE,346 pygments.token.Generic.Inserted: PieColor.GREEN,347 pygments.token.Generic.Strong: PYGMENTS_BOLD,348 pygments.token.Generic.Subheading: PieColor.ORANGE,349 pygments.token.Token: PieColor.PRIMARY,350 pygments.token.Token.Other: PieColor.ORANGE,351}352 353 354def make_style(name, raw_styles, shade):355 def format_value(value):356 return ' '.join(357 get_color(part, shade) or part358 for part in value.split()359 )360 361 bases = (pygments.style.Style,)362 data = {363 'styles': {364 key: format_value(value)365 for key, value in raw_styles.items()366 }367 }368 return type(name, bases, data)369 370 371def make_styles():372 styles = {}373 374 for shade, name in SHADE_TO_PIE_STYLE.items():375 styles[name] = [376 make_style(name, style_map, shade)377 for style_name, style_map in [378 (f'Pie{name}HeaderStyle', PIE_HEADER_STYLE),379 (f'Pie{name}BodyStyle', PIE_BODY_STYLE),380 ]381 ]382 383 return styles384 385 386PIE_STYLES = make_styles()387PIE_STYLE_NAMES = list(PIE_STYLES.keys())388BUNDLED_STYLES |= PIE_STYLES.keys()389 