From a4cc8d5c5793f3fa59082e9e5f95c159ddcf6572 Mon Sep 17 00:00:00 2001 From: Elijah Date: Tue, 1 Oct 2024 14:37:54 +0000 Subject: [PATCH] refactor --- .../datadisplay/shiki_code_block.py | 497 ++++++++++-------- 1 file changed, 292 insertions(+), 205 deletions(-) diff --git a/reflex/components/datadisplay/shiki_code_block.py b/reflex/components/datadisplay/shiki_code_block.py index d0bf80f92..6fd0c7e64 100644 --- a/reflex/components/datadisplay/shiki_code_block.py +++ b/reflex/components/datadisplay/shiki_code_block.py @@ -1,17 +1,18 @@ from collections import defaultdict from typing import Any, Literal, Optional, Union -from reflex.components.component import Component +from reflex.base import Base +from reflex.components.component import Component, ComponentNamespace from reflex.components.lucide.icon import Icon from reflex.components.radix.themes.components.button import Button from reflex.components.radix.themes.layout.box import Box from reflex.event import set_clipboard from reflex.style import Style from reflex.utils.imports import ImportDict, ImportVar -from reflex.vars.base import Var +from reflex.vars.base import LiteralVar, Var from reflex.vars.function import FunctionStringVar -COMMON_TRANSFORMERS = { +SHIKIJS_TRANSFORMER_FNS = { "transformerNotationDiff", "transformerNotationHighlight", "transformerNotationWordHighlight", @@ -25,224 +26,162 @@ COMMON_TRANSFORMERS = { "transformerRemoveNotationEscape", } LiteralCodeLanguage = Literal[ - "ts", "abap", - "abnf", - "actionscript", + "actionscript-3", "ada", - "agda", - "al", - "antlr4", - "apacheconf", + "angular-html", + "angular-ts", + "apache", "apex", "apl", "applescript", - "aql", - "arduino", - "arff", + "ara", "asciidoc", - "asm6502", - "asmatmel", - "aspnet", - "autohotkey", - "autoit", - "avisynth", - "avro-idl", - "bash", - "basic", - "batch", - "bbcode", + "asm", + "astro", + "awk", + "ballerina", + "bat", + "beancount", + "berry", + "bibtex", "bicep", - "birb", - "bison", - "bnf", - "brainfuck", - "brightscript", - "bro", - "bsl", + "blade", "c", - "cfscript", - "chaiscript", - "cil", - "clike", + "cadence", + "clarity", "clojure", "cmake", "cobol", - "coffeescript", - "concurnas", + "codeowners", + "codeql", + "coffee", + "common-lisp", "coq", - "core", "cpp", "crystal", "csharp", - "cshtml", - "csp", "css", - "css-extras", "csv", + "cue", "cypher", "d", "dart", - "dataweave", "dax", - "dhall", + "desktop", "diff", - "django", - "dns-zone-file", "docker", - "dot", - "ebnf", - "editorconfig", - "eiffel", - "ejs", + "dotenv", + "dream-maker", + "edge", "elixir", "elm", + "emacs-lisp", "erb", "erlang", - "etlua", - "excel-formula", - "factor", - "false", - "firestore-security-rules", - "flow", - "fortran", + "fennel", + "fish", + "fluent", + "fortran-fixed-form", + "fortran-free-form", "fsharp", - "ftl", - "gap", - "gcode", + "gdresource", "gdscript", - "gedcom", + "gdshader", + "genie", "gherkin", - "git", + "git-commit", + "git-rebase", + "gleam", + "glimmer-js", + "glimmer-ts", "glsl", - "gml", - "gn", + "gnuplot", "go", - "go-module", "graphql", "groovy", + "hack", "haml", "handlebars", "haskell", "haxe", "hcl", + "hjson", "hlsl", - "hoon", - "hpkp", - "hsts", + "html", + "html-derivative", "http", - "ichigojam", - "icon", - "icu-message-format", - "idris", - "iecst", - "ignore", - "index", - "inform7", + "hxml", + "hy", + "imba", "ini", - "io", - "j", "java", - "javadoc", - "javadoclike", "javascript", - "javastacktrace", - "jexl", - "jolie", - "jq", - "js-extras", - "js-templates", - "jsdoc", + "jinja", + "jison", "json", "json5", - "jsonp", - "jsstacktrace", + "jsonc", + "jsonl", + "jsonnet", + "jssm", "jsx", "julia", - "keepalived", - "keyman", "kotlin", - "kumir", "kusto", "latex", - "latte", + "lean", "less", - "lilypond", "liquid", - "lisp", - "livescript", - "llvm", "log", - "lolcode", + "logo", "lua", - "magma", - "makefile", + "luau", + "make", "markdown", - "markup", - "markup-templating", + "marko", "matlab", - "maxscript", - "mel", + "mdc", + "mdx", "mermaid", - "mizar", - "mongodb", - "monkey", - "moonscript", - "n1ql", - "n4js", - "nand2tetris-hdl", - "naniscript", - "nasm", - "neon", - "nevod", + "mojo", + "move", + "narrat", + "nextflow", "nginx", "nim", "nix", - "nsis", - "objectivec", + "nushell", + "objective-c", + "objective-cpp", "ocaml", - "opencl", - "openqasm", - "oz", - "parigp", - "parser", "pascal", - "pascaligo", - "pcaxis", - "peoplecode", "perl", "php", - "php-extras", - "phpdoc", "plsql", + "po", + "postcss", "powerquery", "powershell", - "processing", + "prisma", "prolog", - "promql", - "properties", - "protobuf", - "psl", + "proto", "pug", "puppet", - "pure", - "purebasic", "purescript", "python", - "q", "qml", - "qore", - "qsharp", + "qmldir", + "qss", "r", "racket", - "reason", - "regex", - "rego", - "renpy", - "rest", - "rip", - "roboconf", - "robotframework", + "raku", + "razor", + "reg", + "regexp", + "rel", + "riscv", + "rst", "ruby", "rust", "sas", @@ -250,73 +189,174 @@ LiteralCodeLanguage = Literal[ "scala", "scheme", "scss", - "shell-session", - "smali", + "shaderlab", + "shellscript", + "shellsession", "smalltalk", - "smarty", - "sml", "solidity", - "solution-file", "soy", "sparql", - "splunk-spl", - "sqf", + "splunk", "sql", - "squirrel", - "stan", + "ssh-config", + "stata", "stylus", + "svelte", "swift", + "system-verilog", "systemd", - "t4-cs", - "t4-templating", - "t4-vb", - "tap", + "tasl", "tcl", - "textile", + "templ", + "terraform", + "tex", "toml", - "tremor", + "ts-tags", + "tsv", "tsx", - "tt2", "turtle", "twig", "typescript", - "typoscript", - "unrealscript", - "uorazor", - "uri", + "typespec", + "typst", "v", "vala", - "vbnet", - "velocity", + "vb", "verilog", "vhdl", - "vim", - "visual-basic", - "warpscript", + "viml", + "vue", + "vue-html", + "vyper", "wasm", - "web-idl", - "wiki", + "wenyan", + "wgsl", + "wikitext", "wolfram", - "wren", - "xeora", - "xml-doc", - "xojo", - "xquery", + "xml", + "xsl", "yaml", - "yang", + "zenscript", "zig", ] +LiteralCodeTheme = Literal[ + "andromeeda", + "aurora-x", + "ayu-dark", + "catppuccin-frappe", + "catppuccin-latte", + "catppuccin-macchiato", + "catppuccin-mocha", + "dark-plus", + "dracula", + "dracula-soft", + "everforest-dark", + "everforest-light", + "github-dark", + "github-dark-default", + "github-dark-dimmed", + "github-dark-high-contrast", + "github-light", + "github-light-default", + "github-light-high-contrast", + "houston", + "laserwave", + "light-plus", + "material-theme", + "material-theme-darker", + "material-theme-lighter", + "material-theme-ocean", + "material-theme-palenight", + "min-dark", + "min-light", + "monokai", + "night-owl", + "nord", + "one-dark-pro", + "one-light", + "plastic", + "poimandres", + "red", + "rose-pine", + "rose-pine-dawn", + "rose-pine-moon", + "slack-dark", + "slack-ochin", + "snazzy-light", + "solarized-dark", + "solarized-light", + "synthwave-84", + "tokyo-night", + "vesper", + "vitesse-black", + "vitesse-dark", + "vitesse-light", +] + + +class ShikiBaseTransformers(Base): + library: str + fns: list[FunctionStringVar] + style: Style | None + + +class ShikiJsTransformer(ShikiBaseTransformers): + library: str = "@shikijs/transformers" + fns: list[FunctionStringVar] = [ + FunctionStringVar.create(x) for x in SHIKIJS_TRANSFORMER_FNS + ] + style: Style | None = Style( + { + ".line": {"display": "inline", "padding-bottom": "0"}, + ".diff": { + "display": "inline-block", + "width": "100vw", + "margin": "0 -12px", + "padding": "0 12px", + }, + ".diff.add": {"background-color": "#0505"}, + ".diff.remove": {"background-color": "#8005"}, + ".diff:before": {"position": "absolute", "left": "40px"}, + ".has-focused .line": {"filter": "blur(0.095rem)"}, + ".has-focused .focused": {"filter": "blur(0)"}, + "code": {"counter-reset": "step", "counter-increment": "step 0"}, + "code .line::before": { + "content": "counter(step)", + "counter-increment": "step", + "width": "1rem", + "margin-right": "1.5rem", + "display": "inline-block", + "text-align": "right", + "color": "rgba(115,138,148,.4)", + }, + } + ) + + def __init__(self, **kwargs): + fns = kwargs.pop("fns", None) + style = kwargs.pop("style", None) + if fns: + kwargs["fns"] = [ + FunctionStringVar.create(x) + if not isinstance(x, FunctionStringVar) + else x + for x in fns + ] + + if style: + kwargs["style"] = Style(style) + super().__init__(**kwargs) class ShikiCodeBlock(Component): library = "/utils/code" tag = "Code" alias = "ShikiCode" - language: Var[LiteralCodeLanguage] = "python" - theme: Var[str] = "min-dark" + language: Var[LiteralCodeLanguage] = Var.create("python") + theme: Var[LiteralCodeTheme] = Var.create("github-dark") themes: Var[list[dict[str, Any]] | dict[str, str]] code: Var[str] - transformers: Var[list] = [] + transformers: Var[list[ShikiBaseTransformers | dict[str, Any]]] = [] @classmethod def create( @@ -342,40 +382,87 @@ class ShikiCodeBlock(Component): else: copy_button = None - transformers = props.pop("transformers", []) - trans_final = [] - for transformer in transformers: - if transformer in COMMON_TRANSFORMERS: - trans_final.append(FunctionStringVar(f"{transformer}()")) - else: - trans_final.append(transformer) - - if trans_final: - props["transformers"] = trans_final - code_block = super().create(**props) + transformer_styles = {} + for transformer in code_block.transformers._var_value: + if isinstance(transformer, ShikiBaseTransformers) and transformer.style: + transformer_styles.update(transformer.style) if copy_button: - return Box.create(code_block, copy_button, position="relative") + return Box.create( + code_block, + copy_button, + position="relative", + style=Style(transformer_styles), + ) else: - return code_block + return Box.create(code_block, style=Style(transformer_styles)) def add_imports(self) -> ImportDict | list[ImportDict]: imports = defaultdict(list) for transformer in self.transformers._var_value: - if ( - isinstance(transformer, FunctionStringVar) - and (transformer_import_str := str(transformer).strip("()")) - in COMMON_TRANSFORMERS - ): - imports["@shikijs/transformers"].append( - ImportVar(tag=transformer_import_str) + if isinstance(transformer, ShikiBaseTransformers): + imports[transformer.library].extend( + [ImportVar(tag=str(fn)) for fn in transformer.fns] ) self.lib_dependencies.append( - "@shikijs/transformers" - ) if "@shikijs/transformers" not in self.lib_dependencies else None - + transformer.library + ) if transformer.library not in self.lib_dependencies else None return imports + @classmethod + def create_transformer(cls, library: str, fns: list[str]) -> ShikiBaseTransformers: + return ShikiBaseTransformers( + library=library, fns=[FunctionStringVar.create(fn) for fn in fns] + ) -code_block = ShikiCodeBlock.create + def _render(self, props: dict[str, Any] | None = None): + """Renders the component with the given properties, processing transformers if present. + + Args: + props: Optional properties to pass to the render function. + + Returns: + Rendered component output. + """ + # Ensure props is initialized from class attributes if not provided + props = props or { + attr.rstrip("_"): getattr(self, attr) for attr in self.get_props() + } + + # Extract transformers and apply transformations + transformers = props.get("transformers") + if transformers is not None: + transformed_values = self._process_transformers(transformers._var_value) + props["transformers"] = LiteralVar.create(transformed_values) + + return super()._render(props) + + def _process_transformers(self, transformer_list: list) -> list: + """Processes a list of transformers, applying transformations where necessary. + + Args: + transformer_list: List of transformer objects or values. + + Returns: + list: A list of transformed values. + """ + processed = [] + + for transformer in transformer_list: + if isinstance(transformer, ShikiBaseTransformers): + processed.extend(fn.call() for fn in transformer.fns) + else: + processed.append(transformer) + + return processed + + +class CodeblockNamespace(ComponentNamespace): + """Namespace for the CodeBlock component.""" + + create_transformer = ShikiCodeBlock.create_transformer + __call__ = ShikiCodeBlock.create + + +code_block = CodeblockNamespace()