Module refinery.units.sinks.hl
Expand source code Browse git
from __future__ import annotations
import pathlib
import typing
import colorama
if True:
colorama.init()
from refinery.lib.id import get_text_format
from refinery.lib.meta import metavars
from refinery.lib.types import Param
from refinery.units import Arg, Unit
if typing.TYPE_CHECKING:
from io import TextIOBase
from pygments.token import _TokenType as TokenType
class hl(Unit):
"""
This unit uses the Pygments library for syntax highlighting.
It expects plain text code as input and outputs ANSI-colored text.
"""
_pygments_name_completion = {
'js' : 'JavaScript',
'py' : 'Python',
'vb' : 'VisualBasic',
'vba' : 'VisualBasic',
'vbs' : 'VisualBasic',
}
def __init__(
self,
language: Param[str | None, Arg.String(
help='Optionally specify the input language to be highlighted.')] = None,
style: Param[str | None, Arg.String('-s', group='STYLE',
help='Optionally specify a color style supported by Pygments.')] = None,
github: Param[bool, Arg.Switch('-G', group='STYLE',
help='Use github-flavored styling.')] = False,
solarized: Param[bool, Arg.Switch('-S', group='STYLE',
help='Use solarized-flavored styling.')] = False,
gruvbox: Param[bool, Arg.Switch('-B', group='STYLE',
help='Use gruvbox-flavored styling.')] = False,
dark: Param[bool, Arg.Switch('-d',
help='Assume a dark brackground.')] = False,
light: Param[bool, Arg.Switch('-l',
help='Assume a light background.')] = False,
):
if dark and light:
raise ValueError('The "dark" and "light" options cannot simultaneously be set.')
if sum(1 for opt in (github, solarized, gruvbox, style) if opt) > 1:
raise ValueError('More than one styling option was set.')
return super().__init__(
language=language,
style=style,
dark=dark,
light=light,
github=github,
solarized=solarized,
gruvbox=gruvbox,
)
@Unit.Requires('Pygments', 3)
def _pygments():
import pygments
import pygments.formatter
import pygments.formatters
import pygments.lexers
import pygments.token
return pygments
def _style_variant(self):
return 'light' if self.args.light else 'dark'
def process(self, data):
lib = self._pygments
language: str = self.args.language
if language:
lexer = lib.lexers.get_lexer_by_name(
self._pygments_name_completion.get(language, language))
else:
guesses = []
meta = metavars(data)
if format := get_text_format(data):
guesses.append(format.extension)
guesses.append(str(meta['ext']))
if path := meta.get('path'):
guesses.append(pathlib.Path(str(path)).suffix.lstrip('.'))
for guess in guesses:
try:
lexer = lib.lexers.get_lexer_by_name(guess)
except Exception:
pass
else:
break
else:
lexer = lib.lexers.guess_lexer(data)
self.log_info(F'using lexer for {lexer.name.lower()}')
t256 = lib.formatters.Terminal256Formatter
if self.args.github:
tf = t256(style=F'github-{self._style_variant()}')
elif self.args.solarized:
tf = t256(style=F'solarized-{self._style_variant()}')
elif self.args.gruvbox:
tf = t256(style=F'gruvbox-{self._style_variant()}')
elif self.args.light:
tf = lib.formatters.TerminalFormatter(bg='light')
elif self.args.dark:
tf = lib.formatters.TerminalFormatter(bg='dark')
elif (style := self.args.style):
tf = t256(colorscheme=style)
else:
FG = colorama.Fore
ST = colorama.Style
t = lib.token
R = FG.LIGHTRED_EX
C = FG.CYAN
W = FG.WHITE
B = FG.LIGHTBLACK_EX
ANSI_MAP = {
# Base layer
t.Text : W,
# Comments (background noise)
t.Comment : B,
t.Comment.Preproc : B,
# Structure (dominant visual signal)
t.Keyword : R + ST.BRIGHT,
t.Keyword.Constant : R,
t.Keyword.Type : R,
t.Operator : R,
t.Operator.Word : R,
# Names (keep calm and readable)
t.Name : W,
t.Name.Variable : W,
t.Name.Function : W,
# Rare highlight (just enough contrast)
t.Name.Class : W + ST.BRIGHT,
# Keep palette tight (no extra hues)
t.Name.Namespace : B,
t.Name.Attribute : B,
t.Name.Property : B,
# Structural accents
t.Name.Builtin : R,
t.Name.Decorator : R,
t.Name.Tag : R,
# Data (controlled green usage)
t.String : C,
t.String.Doc : C,
t.String.Escape : C,
t.String.Interpol : C,
t.Number : C,
t.Literal : C,
t.Name.Constant : C,
# Background structure
t.Punctuation : B,
# Errors (high visibility)
t.Error : R + ST.BRIGHT,
}
class ColoramaFormatter(lib.formatter.Formatter):
def format(self, tokensource: list[tuple[TokenType, str]], outfile: TextIOBase):
for tt, value in tokensource:
cc = None
while tt and cc is None:
cc = ANSI_MAP.get(tt)
tt = tt.parent
outfile.write(cc or FG.WHITE)
outfile.write(value)
outfile.write(ST.RESET_ALL)
tf = ColoramaFormatter()
out = lib.highlight(data, lexer, tf)
out = F'{out}{colorama.Style.RESET_ALL}'
return out.encode(self.codec)
class hlg(hl, docs='{0}{s}This variant uses GitHub styling.'):
def __init__(self, lexer: str | None = None, dark: bool = False, light: bool = False):
super().__init__(lexer, dark=dark, light=light, github=True)
class hls(hl, docs='{0}{s}This variant uses solarized styling.'):
def __init__(self, lexer: str | None = None, dark: bool = False, light: bool = False):
super().__init__(lexer, dark=dark, light=light, solarized=True)
class hlb(hl, docs='{0}{s}This variant uses gruvbox styling.'):
def __init__(self, lexer: str | None = None, dark: bool = False, light: bool = False):
super().__init__(lexer, dark=dark, light=light, gruvbox=True)
Classes
class hl (language=None, style=None, github=False, solarized=False, gruvbox=False, dark=False, light=False)-
This unit uses the Pygments library for syntax highlighting.
It expects plain text code as input and outputs ANSI-colored text.
Expand source code Browse git
class hl(Unit): """ This unit uses the Pygments library for syntax highlighting. It expects plain text code as input and outputs ANSI-colored text. """ _pygments_name_completion = { 'js' : 'JavaScript', 'py' : 'Python', 'vb' : 'VisualBasic', 'vba' : 'VisualBasic', 'vbs' : 'VisualBasic', } def __init__( self, language: Param[str | None, Arg.String( help='Optionally specify the input language to be highlighted.')] = None, style: Param[str | None, Arg.String('-s', group='STYLE', help='Optionally specify a color style supported by Pygments.')] = None, github: Param[bool, Arg.Switch('-G', group='STYLE', help='Use github-flavored styling.')] = False, solarized: Param[bool, Arg.Switch('-S', group='STYLE', help='Use solarized-flavored styling.')] = False, gruvbox: Param[bool, Arg.Switch('-B', group='STYLE', help='Use gruvbox-flavored styling.')] = False, dark: Param[bool, Arg.Switch('-d', help='Assume a dark brackground.')] = False, light: Param[bool, Arg.Switch('-l', help='Assume a light background.')] = False, ): if dark and light: raise ValueError('The "dark" and "light" options cannot simultaneously be set.') if sum(1 for opt in (github, solarized, gruvbox, style) if opt) > 1: raise ValueError('More than one styling option was set.') return super().__init__( language=language, style=style, dark=dark, light=light, github=github, solarized=solarized, gruvbox=gruvbox, ) @Unit.Requires('Pygments', 3) def _pygments(): import pygments import pygments.formatter import pygments.formatters import pygments.lexers import pygments.token return pygments def _style_variant(self): return 'light' if self.args.light else 'dark' def process(self, data): lib = self._pygments language: str = self.args.language if language: lexer = lib.lexers.get_lexer_by_name( self._pygments_name_completion.get(language, language)) else: guesses = [] meta = metavars(data) if format := get_text_format(data): guesses.append(format.extension) guesses.append(str(meta['ext'])) if path := meta.get('path'): guesses.append(pathlib.Path(str(path)).suffix.lstrip('.')) for guess in guesses: try: lexer = lib.lexers.get_lexer_by_name(guess) except Exception: pass else: break else: lexer = lib.lexers.guess_lexer(data) self.log_info(F'using lexer for {lexer.name.lower()}') t256 = lib.formatters.Terminal256Formatter if self.args.github: tf = t256(style=F'github-{self._style_variant()}') elif self.args.solarized: tf = t256(style=F'solarized-{self._style_variant()}') elif self.args.gruvbox: tf = t256(style=F'gruvbox-{self._style_variant()}') elif self.args.light: tf = lib.formatters.TerminalFormatter(bg='light') elif self.args.dark: tf = lib.formatters.TerminalFormatter(bg='dark') elif (style := self.args.style): tf = t256(colorscheme=style) else: FG = colorama.Fore ST = colorama.Style t = lib.token R = FG.LIGHTRED_EX C = FG.CYAN W = FG.WHITE B = FG.LIGHTBLACK_EX ANSI_MAP = { # Base layer t.Text : W, # Comments (background noise) t.Comment : B, t.Comment.Preproc : B, # Structure (dominant visual signal) t.Keyword : R + ST.BRIGHT, t.Keyword.Constant : R, t.Keyword.Type : R, t.Operator : R, t.Operator.Word : R, # Names (keep calm and readable) t.Name : W, t.Name.Variable : W, t.Name.Function : W, # Rare highlight (just enough contrast) t.Name.Class : W + ST.BRIGHT, # Keep palette tight (no extra hues) t.Name.Namespace : B, t.Name.Attribute : B, t.Name.Property : B, # Structural accents t.Name.Builtin : R, t.Name.Decorator : R, t.Name.Tag : R, # Data (controlled green usage) t.String : C, t.String.Doc : C, t.String.Escape : C, t.String.Interpol : C, t.Number : C, t.Literal : C, t.Name.Constant : C, # Background structure t.Punctuation : B, # Errors (high visibility) t.Error : R + ST.BRIGHT, } class ColoramaFormatter(lib.formatter.Formatter): def format(self, tokensource: list[tuple[TokenType, str]], outfile: TextIOBase): for tt, value in tokensource: cc = None while tt and cc is None: cc = ANSI_MAP.get(tt) tt = tt.parent outfile.write(cc or FG.WHITE) outfile.write(value) outfile.write(ST.RESET_ALL) tf = ColoramaFormatter() out = lib.highlight(data, lexer, tf) out = F'{out}{colorama.Style.RESET_ALL}' return out.encode(self.codec)Ancestors
Subclasses
Class variables
var reverse-
The type of the None singleton.
Inherited members
class hlg (lexer=None, dark=False, light=False)-
This unit uses the Pygments library for syntax highlighting.
It expects plain text code as input and outputs ANSI-colored text. This variant uses GitHub styling.
Expand source code Browse git
class hlg(hl, docs='{0}{s}This variant uses GitHub styling.'): def __init__(self, lexer: str | None = None, dark: bool = False, light: bool = False): super().__init__(lexer, dark=dark, light=light, github=True)Ancestors
Subclasses
Inherited members
hl:FilterEverythingRequiresactassemblecodecconsolefilterfinishhandlesis_quietis_reversibleisattylabelledleniencylog_alwayslog_debuglog_detachlog_faillog_infolog_levellog_warnloggernamenozzleoptional_dependenciesreadread1required_dependenciesresetreverserunsourcesuperinit
UnitBase:
class hls (lexer=None, dark=False, light=False)-
This unit uses the Pygments library for syntax highlighting.
It expects plain text code as input and outputs ANSI-colored text. This variant uses solarized styling.
Expand source code Browse git
class hls(hl, docs='{0}{s}This variant uses solarized styling.'): def __init__(self, lexer: str | None = None, dark: bool = False, light: bool = False): super().__init__(lexer, dark=dark, light=light, solarized=True)Ancestors
Subclasses
Inherited members
hl:FilterEverythingRequiresactassemblecodecconsolefilterfinishhandlesis_quietis_reversibleisattylabelledleniencylog_alwayslog_debuglog_detachlog_faillog_infolog_levellog_warnloggernamenozzleoptional_dependenciesreadread1required_dependenciesresetreverserunsourcesuperinit
UnitBase:
class hlb (lexer=None, dark=False, light=False)-
This unit uses the Pygments library for syntax highlighting.
It expects plain text code as input and outputs ANSI-colored text. This variant uses gruvbox styling.
Expand source code Browse git
class hlb(hl, docs='{0}{s}This variant uses gruvbox styling.'): def __init__(self, lexer: str | None = None, dark: bool = False, light: bool = False): super().__init__(lexer, dark=dark, light=light, gruvbox=True)Ancestors
Subclasses
Inherited members
hl:FilterEverythingRequiresactassemblecodecconsolefilterfinishhandlesis_quietis_reversibleisattylabelledleniencylog_alwayslog_debuglog_detachlog_faillog_infolog_levellog_warnloggernamenozzleoptional_dependenciesreadread1required_dependenciesresetreverserunsourcesuperinit
UnitBase: