import os
import colorful
from pygments import token, styles
from pygments.style import Style
from pygments.token import Keyword, Name, Comment, String, Error, Text, \
Number, Operator, Generic, Whitespace, Punctuation, Other, Literal
from .sdoctypes import (
SLine,
SAnnotationPush,
SAnnotationPop,
)
from .syntax import Token
from .render import as_lines
from .utils import rfind_idx
_SYNTAX_TOKEN_TO_PYGMENTS_TOKEN = {
Token.KEYWORD_CONSTANT: token.Keyword.Constant,
Token.NAME_BUILTIN: token.Name.Builtin,
Token.NAME_ENTITY: token.Name.Entity,
Token.NAME_FUNCTION: token.Name.Function,
Token.NAME_VARIABLE: token.Name.Variable,
Token.LITERAL_STRING: token.String,
Token.STRING_AFFIX: token.String.Affix,
Token.STRING_ESCAPE: token.String.Escape,
Token.NUMBER_INT: token.Number,
Token.NUMBER_BINARY: token.Number.Bin,
Token.NUMBER_INT: token.Number.Integer,
Token.NUMBER_FLOAT: token.Number.Float,
Token.OPERATOR: token.Operator,
Token.PUNCTUATION: token.Punctuation,
Token.COMMENT_SINGLE: token.Comment.Single,
}
# From https://github.com/primer/github-syntax-theme-generator/blob/master/lib/themes/light.json # noqa
# GitHub has MIT licenesed the theme, see
# https://github.com/primer/github-syntax-theme-generator/blob/master/LICENSE
class GitHubLightStyle(Style):
background_color = "#ffffff" # done
highlight_color = "#fafbfc" # done
styles = {
# No corresponding class for the following:
Text: "#24292e",
Whitespace: "",
Error: "bold #b31d28",
Other: "",
Comment: "#6a737d", # done
Comment.Multiline: "",
Comment.Preproc: "",
Comment.Single: "",
Comment.Special: "",
Keyword: "#d73a49", # class: 'k'
Keyword.Constant: "#005cc5", # done
Keyword.Declaration: "#d73a49",
Keyword.Namespace: "#d73a49",
Keyword.Pseudo: "",
Keyword.Reserved: "",
Keyword.Type: "",
Operator: "#d73a49", # class: 'o'
Operator.Word: "", # class: 'ow' - like keywords
Punctuation: "", # class: 'p'
Name: "#6f42c1", # class: 'n'
Name.Attribute: "#24292e", # class: 'na' - to be revised
Name.Builtin: "#005cc5", # class: 'nb'
Name.Builtin.Pseudo: "#005cc5", # class: 'bp'
Name.Class: "#6f42c1", # class: 'nc' - to be revised
Name.Constant: "#005cc5", # class: 'no' - to be revised
Name.Decorator: "#6f42c1", # done
Name.Entity: "#6f42c1", # done
Name.Exception: "#005cc5", # done
Name.Function: "#6f42c1", # done
Name.Function.Magic: "#005cc5", # done
Name.Property: "", # class: 'py'
Name.Label: "", # class: 'nl'
Name.Namespace: "", # class: 'nn' - to be revised
Name.Other: "#005cc5", # class: 'nx'
Name.Tag: "#22863a", # done
Name.Variable: "#e36209", # class: 'nv' - to be revised
Name.Variable.Class: "", # class: 'vc' - to be revised
Name.Variable.Global: "", # class: 'vg' - to be revised
Name.Variable.Instance: "", # class: 'vi' - to be revised
Number: "#005cc5", # class: 'm'
Number.Float: "", # class: 'mf'
Number.Hex: "", # class: 'mh'
Number.Integer: "", # class: 'mi'
Number.Integer.Long: "", # class: 'il'
Number.Oct: "", # class: 'mo'
Literal: "#005cc5", # class: 'l'
Literal.Date: "#005cc5", # class: 'ld'
String: "#032f62", # done
String.Backtick: "", # class: 'sb'
String.Char: "", # class: 'sc'
String.Doc: "", # class: 'sd' - like a comment
String.Double: "", # class: 's2'
String.Escape: "#22863a", # done
String.Heredoc: "", # class: 'sh'
String.Interpol: "#005cc5", # done
String.Other: "", # class: 'sx'
String.Regex: "", # class: 'sr'
String.Single: "", # class: 's1'
String.Symbol: "", # class: 'ss'
Generic: "", # class: 'g'
Generic.Deleted: "#f92672", # class: 'gd',
Generic.Emph: "italic", # class: 'ge'
Generic.Error: "", # class: 'gr'
Generic.Heading: "", # class: 'gh'
Generic.Inserted: "#22863a bg: #f0fff4", # class: 'gi'
Generic.Output: "", # class: 'go'
Generic.Prompt: "", # class: 'gp'
Generic.Strong: "bold", # class: 'gs'
Generic.Subheading: "bold #005cc5", # class: 'gu'
Generic.Traceback: "", # class: 'gt'
}
default_dark_style = styles.get_style_by_name('monokai')
default_light_style = GitHubLightStyle
is_light_bg = None
colorfgbg = os.environ.get('COLORFGBG', '')
try:
fg, bg = map(int, colorfgbg.split(';', 1))
if bg > fg:
is_light_bg = True
if fg > bg:
is_light_bg = False
except ValueError:
pass
bg_override = os.environ.get('PYPRETTYPRINTER_LIGHT_BACKGROUND')
if bg_override is not None:
is_light_bg = bool(bg_override)
if bg_override == '0' or bg_override.lower() == 'false':
is_light_bg = False
default_style = default_light_style if is_light_bg else default_dark_style
[docs]def set_default_style(style):
"""Sets default global style to be used by ``prettyprinter.cpprint``.
:param style: the style to set, either subclass of
``pygments.styles.Style`` or one of ``'dark'``, ``'light'``
"""
global default_style
if style == 'dark':
style = default_dark_style
elif style == 'light':
style = default_light_style
if not issubclass(style, Style):
raise TypeError(
"style must be a subclass of pygments.styles.Style or "
"one of 'dark', 'light'. Got {}".format(repr(style))
)
default_style = style
def styleattrs_to_colorful(attrs):
c = colorful.reset
if attrs['color'] or attrs['bgcolor']:
# Colorful doesn't have a way to directly set Hex/RGB
# colors- until I find a better way, we do it like this :)
accessor = ''
if attrs['color']:
colorful.update_palette({'prettyprinterCurrFg': attrs['color']})
accessor = 'prettyprinterCurrFg'
if attrs['bgcolor']:
colorful.update_palette({'prettyprinterCurrBg': attrs['bgcolor']})
accessor += '_on_prettyprinterCurrBg'
c &= getattr(colorful, accessor)
if attrs['bold']:
c &= colorful.bold
if attrs['italic']:
c &= colorful.italic
if attrs['underline']:
c &= colorful.underline
return c
def colored_render_to_stream(
stream,
sdocs,
style,
newline='\n',
separator=' '
):
if style is None:
style = default_style
evald = list(sdocs)
if not evald:
return
color_cache = {}
colorstack = []
sdoc_lines = as_lines(evald)
for sdoc_line in sdoc_lines:
last_text_sdoc_idx = rfind_idx(
lambda sdoc: isinstance(sdoc, str),
sdoc_line
)
# Edge case: trailing whitespace on a line.
# Currently happens on multiline str value in a dict:
# there's a trailing whitespace after the colon that's
# hard to eliminate at the doc level.
if last_text_sdoc_idx != -1:
last_text_sdoc = sdoc_line[last_text_sdoc_idx]
sdoc_line[last_text_sdoc_idx] = last_text_sdoc.rstrip()
for sdoc in sdoc_line:
if isinstance(sdoc, str):
stream.write(sdoc)
elif isinstance(sdoc, SLine):
stream.write(newline + separator * sdoc.indent)
elif isinstance(sdoc, SAnnotationPush):
if isinstance(sdoc.value, Token):
try:
color = color_cache[sdoc.value]
except KeyError:
pygments_token = _SYNTAX_TOKEN_TO_PYGMENTS_TOKEN[
sdoc.value
]
tokenattrs = style.style_for_token(pygments_token)
color = styleattrs_to_colorful(tokenattrs)
color_cache[sdoc.value] = color
colorstack.append(color)
stream.write(str(color))
elif isinstance(sdoc, SAnnotationPop):
try:
colorstack.pop()
except IndexError:
continue
if colorstack:
stream.write(str(colorstack[-1]))
else:
stream.write(str(colorful.reset))
if colorstack:
stream.write(str(colorful.reset))