diff --git a/docs/_plugins/twinbasic.rb b/docs/_plugins/twinbasic.rb index a479bf5..661099b 100644 --- a/docs/_plugins/twinbasic.rb +++ b/docs/_plugins/twinbasic.rb @@ -3,6 +3,22 @@ require "rouge" +# Register tB-specific token types so the highlighter can distinguish symbols +# the way the twinBASIC IDE's theme system does -- e.g. Boolean / Empty / +# Nothing / Null are each their own slot in tB's Symbol* palette, and the +# line-continuation '_' has its own SymbolContinuationCharacter colour. +module Rouge::Token::Tokens + [ + [Literal, :Boolean, 'lb'], + [Literal, :Empty, 'le'], + [Literal, :Nothing, 'ln'], + [Literal, :Null, 'lu'], + [Punctuation, :LineContinuation, 'lc'], + ].each do |parent, name, shortname| + parent.token(name, shortname) unless parent.const_defined?(name, false) + end +end + module Rouge module Lexers class TwinBasic < RegexLexer @@ -17,20 +33,30 @@ def self.keywords @keywords ||= Set.new %w( alias byref byval call case class close coclass const continue declare default delegate dim do each else elseif - empty end endif enum erase error event exit extends - false finally for friend function get global gosub + end endif enum erase error event exit extends + finally for friend function get global gosub goto handles if implements imports inherits input interface let lib line lock loop me mid module - namespace new next nothing null of on open option optional + namespace new next of on open option optional overloads paramarray preserve print private property public put raiseevent redim resume return select set shared static step stop structure sub - then throw to true try type unlock until + then throw to try type unlock until using wend when while width with withevents write ) end + def self.keyword_constants + @keyword_constants ||= { + 'true' => Literal::Boolean, + 'false' => Literal::Boolean, + 'empty' => Literal::Empty, + 'nothing' => Literal::Nothing, + 'null' => Literal::Null, + } + end + def self.keywords_type @keywords_type ||= Set.new %w( any boolean byte currency date decimal double integer long @@ -54,7 +80,7 @@ def self.builtins id = /[a-z_]\w*/i state :whitespace do - rule %r/_[ \t]*\n/, Keyword + rule %r/_[ \t]*\n/, Punctuation::LineContinuation rule %r/\n/, Text, :bol rule %r/[^\S\n]+/, Text rule %r/rem\b.*?$/i, Comment::Single @@ -72,12 +98,15 @@ def self.builtins rule %r( [#]If\b .*? \bThen | [#]ElseIf\b .*? \bThen + | [#]Else\b | [#]End \s+ If | [#]Const | [#]Region .*? \n | [#]End \s+ Region )xi, Comment::Preproc + rule %r/#\d[^#\n]*#/, Literal::Date + rule %r/\[/, Punctuation, :attribute rule %r/(\d+\.\d*|\d*\.\d+)(e[+-]?\d+)?[!#@]?/i, Num::Float @@ -103,7 +132,9 @@ def self.builtins rule id do |m| key = m[0].downcase - if self.class.keywords.include? key + if (kc = self.class.keyword_constants[key]) + token kc + elsif self.class.keywords.include? key token Keyword elsif self.class.keywords_type.include? key token Keyword::Type @@ -157,7 +188,9 @@ def self.builtins rule %r/\d+[%&!#@]?/, Num::Integer rule id do |m| key = m[0].downcase - if self.class.keywords.include? key + if (kc = self.class.keyword_constants[key]) + token kc + elsif self.class.keywords.include? key token Keyword elsif self.class.keywords_type.include? key token Keyword::Type diff --git a/docs/_sass/custom/_twinbasic-dark.scss b/docs/_sass/custom/_twinbasic-dark.scss new file mode 100644 index 0000000..4ec7528 --- /dev/null +++ b/docs/_sass/custom/_twinbasic-dark.scss @@ -0,0 +1,187 @@ +/* twinBASIC Dark theme - Rouge syntax highlighting */ +/* Selectors are the Rouge HTML formatter classes emitted by docs/_plugins/twinbasic.rb. */ +/* Scoped under .language-tb .highlight so they only repaint tB fenced code blocks. */ + +.language-tb .highlight { + .c1 { /* SymbolComment — ' comments and REM */ + color: #448a63; + font-style: normal; + font-weight: normal; + text-decoration: none; + } + + .cm { /* SymbolComment — C-style block comments */ + color: #448a63; + font-style: normal; + font-weight: normal; + text-decoration: none; + } + + .cp { /* SymbolConditionalCompilationDirective — #If / #ElseIf / #Else / #End If / #Const / #Region */ + color: #ad8c98; + font-style: normal; + font-weight: normal; + text-decoration: none; + } + + .k { /* SymbolKeyword — Dim, If, End, Sub, ... */ + color: #6c8eda; + font-style: normal; + font-weight: normal; + text-decoration: none; + } + + .kd { /* SymbolKeyword — Option Strict / Explicit / Compare / Base */ + color: #6c8eda; + font-style: normal; + font-weight: normal; + text-decoration: none; + } + + .kt { /* SymbolBuiltInDataType — Boolean, Integer, String, ... */ + color: #b1551f; + font-style: normal; + font-weight: normal; + text-decoration: none; + } + + .lb { /* SymbolLiteralBoolean — True, False */ + color: #c495d3; + font-style: normal; + font-weight: normal; + text-decoration: none; + } + + .lc { /* SymbolContinuationCharacter — '_' line-continuation marker */ + color: #808080; + font-style: normal; + font-weight: normal; + text-decoration: none; + } + + .ld { /* SymbolLiteralDate — #m/d/yyyy [h:mm:ss am/pm]# date-time literals */ + color: #c495d3; + font-style: normal; + font-weight: normal; + text-decoration: none; + } + + .le { /* SymbolLiteralEmpty — Empty */ + color: #c495d3; + font-style: normal; + font-weight: normal; + text-decoration: none; + } + + .ln { /* SymbolLiteralNothing — Nothing */ + color: #c495d3; + font-style: normal; + font-weight: normal; + text-decoration: none; + } + + .lu { /* SymbolLiteralNull — Null */ + color: #c495d3; + font-style: normal; + font-weight: normal; + text-decoration: none; + } + + .mi { /* SymbolLiteralNumeric — integer literals */ + color: #aeca89; + font-style: normal; + font-weight: normal; + text-decoration: none; + } + + .mf { /* SymbolLiteralNumeric — float literals */ + color: #aeca89; + font-style: normal; + font-weight: normal; + text-decoration: none; + } + + .s { /* SymbolLiteralString — string literals */ + color: #aeca89; + font-style: oblique; + font-weight: normal; + text-decoration: none; + } + + .se { /* SymbolLiteralString — "" escape inside string literals */ + color: #aeca89; + font-style: oblique; + font-weight: normal; + text-decoration: none; + } + + .o { /* SymbolOperator — +, -, =, <, >, &, ... */ + color: #80a1a5; + font-style: normal; + font-weight: normal; + text-decoration: none; + } + + .ow { /* SymbolNamedOperator — And, Or, Not, Is, Mod, ... */ + color: #80a1a5; + font-style: normal; + font-weight: normal; + text-decoration: none; + } + + .na { /* SymbolAttribute — [Documentation(...)] attribute names */ + color: #5c5c53; + font-style: normal; + font-weight: normal; + text-decoration: none; + } + + .nb { /* SymbolClass — Debug, Err */ + color: #e4c685; + font-style: normal; + font-weight: normal; + text-decoration: none; + } + + .nc { /* SymbolClass — Class / CoClass / Enum / Interface / Type / Structure names */ + color: #e4c685; + font-style: normal; + font-weight: normal; + text-decoration: none; + } + + .nf { /* SymbolFunction — Function / Sub / Property names */ + color: #cf9a5d; + font-style: normal; + font-weight: normal; + text-decoration: none; + } + + .nn { /* SymbolModule — Module / Namespace / Imports targets */ + color: #a8a887; + font-style: normal; + font-weight: normal; + text-decoration: none; + } + + .nv { /* SymbolVariable — Dim / Const / ReDim variable names */ + color: #8b8b52; + font-style: normal; + font-weight: normal; + text-decoration: none; + } +} + +/* tB CodePanelBackColor scoped to tB code-block containers (.language-tb). */ +/* The .language-tb class lives on the outer .highlighter-rouge div emitted */ +/* by kramdown for ```tb``` fenced blocks, so `.language-tb.highlighter-rouge` */ +/* (no space) hits the outer container and `.language-tb ` hits */ +/* the nested .highlight / pre / etc. The partial is imported inside */ +/* `html.dark-mode { ... }` by just-the-docs-combined.scss, so SCSS nesting */ +/* confines these rules to dark mode automatically. */ +.language-tb.highlighter-rouge, +.language-tb .highlight, +.language-tb pre.highlight, +.language-tb .highlight pre { + background-color: rgb(33, 33, 33); +} diff --git a/docs/_sass/custom/_twinbasic-light.scss b/docs/_sass/custom/_twinbasic-light.scss new file mode 100644 index 0000000..dd0d8d7 --- /dev/null +++ b/docs/_sass/custom/_twinbasic-light.scss @@ -0,0 +1,173 @@ +/* twinBASIC Light theme - Rouge syntax highlighting */ +/* Selectors are the Rouge HTML formatter classes emitted by docs/_plugins/twinbasic.rb. */ +/* Scoped under .language-tb .highlight so they only repaint tB fenced code blocks. */ + +.language-tb .highlight { + .c1 { /* SymbolComment — ' comments and REM */ + color: #448a63; + font-style: normal; + font-weight: normal; + text-decoration: none; + } + + .cm { /* SymbolComment — C-style block comments */ + color: #448a63; + font-style: normal; + font-weight: normal; + text-decoration: none; + } + + .cp { /* SymbolConditionalCompilationDirective — #If / #ElseIf / #Else / #End If / #Const / #Region */ + color: #ad8c98; + font-style: normal; + font-weight: normal; + text-decoration: none; + } + + .k { /* SymbolKeyword — Dim, If, End, Sub, ... */ + color: #385ba9; + font-style: normal; + font-weight: normal; + text-decoration: none; + } + + .kd { /* SymbolKeyword — Option Strict / Explicit / Compare / Base */ + color: #385ba9; + font-style: normal; + font-weight: normal; + text-decoration: none; + } + + .kt { /* SymbolBuiltInDataType — Boolean, Integer, String, ... */ + color: #b1551f; + font-style: normal; + font-weight: normal; + text-decoration: none; + } + + .lb { /* SymbolLiteralBoolean — True, False */ + color: #b877ce; + font-style: normal; + font-weight: normal; + text-decoration: none; + } + + .lc { /* SymbolContinuationCharacter — '_' line-continuation marker */ + color: #808080; + font-style: normal; + font-weight: normal; + text-decoration: none; + } + + .ld { /* SymbolLiteralDate — #m/d/yyyy [h:mm:ss am/pm]# date-time literals */ + color: #b877ce; + font-style: normal; + font-weight: normal; + text-decoration: none; + } + + .le { /* SymbolLiteralEmpty — Empty */ + color: #b877ce; + font-style: normal; + font-weight: normal; + text-decoration: none; + } + + .ln { /* SymbolLiteralNothing — Nothing */ + color: #b877ce; + font-style: normal; + font-weight: normal; + text-decoration: none; + } + + .lu { /* SymbolLiteralNull — Null */ + color: #b877ce; + font-style: normal; + font-weight: normal; + text-decoration: none; + } + + .mi { /* SymbolLiteralNumeric — integer literals */ + color: #457e12; + font-style: normal; + font-weight: normal; + text-decoration: none; + } + + .mf { /* SymbolLiteralNumeric — float literals */ + color: #457e12; + font-style: normal; + font-weight: normal; + text-decoration: none; + } + + .s { /* SymbolLiteralString — string literals */ + color: #679f1e; + font-style: normal; + font-weight: normal; + text-decoration: none; + } + + .se { /* SymbolLiteralString — "" escape inside string literals */ + color: #679f1e; + font-style: normal; + font-weight: normal; + text-decoration: none; + } + + .o { /* SymbolOperator — +, -, =, <, >, &, ... */ + color: #80a1a5; + font-style: normal; + font-weight: normal; + text-decoration: none; + } + + .ow { /* SymbolNamedOperator — And, Or, Not, Is, Mod, ... */ + color: #385ba9; + font-style: normal; + font-weight: normal; + text-decoration: none; + } + + .na { /* SymbolAttribute — [Documentation(...)] attribute names */ + color: #bfbfbf; + font-style: normal; + font-weight: normal; + text-decoration: none; + } + + .nb { /* SymbolClass — Debug, Err */ + color: #a87300; + font-style: normal; + font-weight: normal; + text-decoration: none; + } + + .nc { /* SymbolClass — Class / CoClass / Enum / Interface / Type / Structure names */ + color: #a87300; + font-style: normal; + font-weight: normal; + text-decoration: none; + } + + .nf { /* SymbolFunction — Function / Sub / Property names */ + color: #b96300; + font-style: normal; + font-weight: normal; + text-decoration: none; + } + + .nn { /* SymbolModule — Module / Namespace / Imports targets */ + color: #89894d; + font-style: normal; + font-weight: normal; + text-decoration: none; + } + + .nv { /* SymbolVariable — Dim / Const / ReDim variable names */ + color: #939000; + font-style: normal; + font-weight: normal; + text-decoration: none; + } +} diff --git a/docs/assets/css/just-the-docs-combined.scss b/docs/assets/css/just-the-docs-combined.scss index 3f77768..1c38b23 100644 --- a/docs/assets/css/just-the-docs-combined.scss +++ b/docs/assets/css/just-the-docs-combined.scss @@ -1,6 +1,7 @@ --- --- {% include css/just-the-docs.scss.liquid color_scheme="light" %} +@import "custom/twinbasic-light"; span { color: $link-color; @@ -9,6 +10,7 @@ span { html.dark-mode { $color-scheme: dark; {% include css/just-the-docs.scss.liquid color_scheme="dark" %} + @import "custom/twinbasic-dark"; // --------------------------------------------------------------- // Workaround for the dark-mode + .search-active interaction. diff --git a/scripts/__pycache__/extract_theme_colors.cpython-314.pyc b/scripts/__pycache__/extract_theme_colors.cpython-314.pyc new file mode 100644 index 0000000..0991bdd Binary files /dev/null and b/scripts/__pycache__/extract_theme_colors.cpython-314.pyc differ diff --git a/scripts/extract_theme_colors.py b/scripts/extract_theme_colors.py new file mode 100644 index 0000000..1270cd2 --- /dev/null +++ b/scripts/extract_theme_colors.py @@ -0,0 +1,246 @@ +"""Extract Symbol* syntax-highlighting properties from twinBASIC IDE .theme files +and emit Rouge-compatible CSS/SCSS. + +Two outputs per run: + +1. scripts/themes/twinbasic-{classic,dark,light}.css -- flat CSS for inspection. + One rule per Rouge HTML formatter class (e.g. .k, .nc, .cp) that + docs/_plugins/twinbasic.rb emits, with colors and font properties taken + from the corresponding tB theme Symbol. + +2. docs/_sass/custom/_twinbasic-{light,dark}.scss -- SCSS partials shipped in + the site, scoped under `.language-tb .highlight` so they only repaint + fenced ```tb``` code blocks and leave OneLight/OneDark untouched on every + other language. Classic is inspection-only (no SCSS). + +The mapping is many-to-one: several tB theme Symbols share a single Rouge +class because the lexer doesn't distinguish them (e.g. SymbolSub folds into +.nf alongside SymbolFunction). The mapping below picks the canonical tB +Symbol per Rouge class; the unmapped Symbols are listed at the bottom of +each emitted file for reference. + +The Classic theme inherits from Light and overrides a subset; the script +resolves the inheritance so the emitted CSS reflects the effective theme. +""" + +import os +import re +from pathlib import Path + +THEMES_DIR = Path(os.environ["USERPROFILE"]) / "Desktop" / "twinBASIC_IDE_BETA_982" / "themes" +REPO_ROOT = Path(__file__).resolve().parent.parent +CSS_OUT_DIR = REPO_ROOT / "scripts" / "themes" +SCSS_OUT_DIR = REPO_ROOT / "docs" / "_sass" / "custom" + +CSS_PROP = { + "Color": "color", + "FontStyle": "font-style", + "FontWeight": "font-weight", + "TextDecoration": "text-decoration", +} + +# Rouge HTML formatter class -> tB theme Symbol (canonical source for colors). +# Only includes Rouge classes that twinbasic.rb actually emits. Order here is +# the order the rules appear in the emitted CSS. +ROUGE_TO_SYMBOL: list[tuple[str, str, str]] = [ + ("c1", "Comment", "' comments and REM"), + ("cm", "Comment", "C-style block comments"), + ("cp", "ConditionalCompilationDirective", "#If / #ElseIf / #Else / #End If / #Const / #Region"), + ("k", "Keyword", "Dim, If, End, Sub, ..."), + ("kd", "Keyword", "Option Strict / Explicit / Compare / Base"), + ("kt", "BuiltInDataType", "Boolean, Integer, String, ..."), + ("lb", "LiteralBoolean", "True, False"), + ("lc", "ContinuationCharacter", "'_' line-continuation marker"), + ("ld", "LiteralDate", "#m/d/yyyy [h:mm:ss am/pm]# date-time literals"), + ("le", "LiteralEmpty", "Empty"), + ("ln", "LiteralNothing", "Nothing"), + ("lu", "LiteralNull", "Null"), + ("mi", "LiteralNumeric", "integer literals"), + ("mf", "LiteralNumeric", "float literals"), + ("s", "LiteralString", "string literals"), + ("se", "LiteralString", "\"\" escape inside string literals"), + ("o", "Operator", "+, -, =, <, >, &, ..."), + ("ow", "NamedOperator", "And, Or, Not, Is, Mod, ..."), + ("na", "Attribute", "[Documentation(...)] attribute names"), + ("nb", "Class", "Debug, Err"), + ("nc", "Class", "Class / CoClass / Enum / Interface / Type / Structure names"), + ("nf", "Function", "Function / Sub / Property names"), + ("nn", "Module", "Module / Namespace / Imports targets"), + ("nv", "Variable", "Dim / Const / ReDim variable names"), +] + +# tB Symbols that have no Rouge counterpart from twinbasic.rb. The lexer either +# doesn't tokenize them at all or folds them into a broader Rouge token (in which +# case the canonical Symbol in ROUGE_TO_SYMBOL wins). +UNMAPPED_SYMBOLS: list[tuple[str, str]] = [ + ("ConditionalCompilationExcludedCode", "not tokenized — would need preproc evaluation"), + ("Constant", "not distinguished from Name"), + ("DeclareFunction", "not distinguished from Keyword .k"), + ("DeclareSub", "not distinguished from Keyword .k"), + ("Enum", "folded into Name::Class .nc"), + ("EnumMember", "not distinguished from Name"), + ("Field", "not distinguished from Name"), + ("GenericDataType", "not tokenized"), + ("GenericValue", "not tokenized"), + ("GlobalVariablePrivate", "not distinguished from Name"), + ("GlobalVariablePublic", "not distinguished from Name"), + ("Interface", "folded into Name::Class .nc"), + ("LateBoundFunction", "not distinguished from Name"), + ("Library", "not distinguished from Name"), + ("LineLabel", "not tokenized"), + ("LineNumber", "not tokenized"), + ("Me", "folded into Keyword .k"), + ("MultiLineSeperator", "not tokenized"), + ("NamedArgument", "not tokenized"), + ("ParamByRef", "not tokenized"), + ("ParamByVal", "not tokenized"), + ("PropertyGet", "folded into Keyword .k"), + ("PropertyLet", "folded into Keyword .k"), + ("PropertySet", "folded into Keyword .k"), + ("ReturnValue", "not tokenized"), + ("Sub", "folded into Name::Function .nf"), + ("UDT", "folded into Name::Class .nc"), + ("VariableUndeclared", "not distinguished from Name"), +] + +PROPERTY_LINE = re.compile(r"^([A-Za-z][A-Za-z0-9_]*)\s*:\s*(.+?)\s*;?\s*$") +SYMBOL_PROP = re.compile(r"^Symbol([A-Za-z]+?)(Color|FontStyle|FontWeight|TextDecoration)$") + + +def parse_theme(path: Path) -> dict[str, str]: + """Parse a .theme file into a flat `property -> value` dict. Properties with + an empty value (the IDE theme's `Name: ;` fall-back-to-parent form) are + omitted.""" + result: dict[str, str] = {} + text = re.sub(r"/\*.*?\*/", "", path.read_text(encoding="utf-8"), flags=re.DOTALL) + for raw in text.splitlines(): + m = PROPERTY_LINE.match(raw.strip()) + if not m: + continue + name, value = m.group(1), m.group(2).strip().rstrip(";").strip() + if not value: + continue + result[name] = value + return result + + +def symbol_props(theme: dict[str, str]) -> dict[str, dict[str, str]]: + """Filter a flat theme dict down to its Symbol* entries, grouped by Symbol + name and keyed by the bare property suffix (Color / FontStyle / ...).""" + grouped: dict[str, dict[str, str]] = {} + for name, value in theme.items(): + m = SYMBOL_PROP.match(name) + if not m: + continue + sym, prop = m.group(1), m.group(2) + grouped.setdefault(sym, {})[prop] = value + return grouped + + +def render_css(theme: dict[str, dict[str, str]], header: str) -> str: + out = [f"/* {header} */\n"] + out.append("/* Selectors are the Rouge HTML formatter classes emitted by docs/_plugins/twinbasic.rb. */\n\n") + + for rouge, sym, comment in ROUGE_TO_SYMBOL: + props = theme.get(sym) + if not props: + continue + out.append(f".{rouge} {{ /* Symbol{sym} — {comment} */\n") + for key in ("Color", "FontStyle", "FontWeight", "TextDecoration"): + if key in props: + out.append(f" {CSS_PROP[key]}: {props[key]};\n") + out.append("}\n\n") + + out.append("/* tB Symbols with no dedicated Rouge class in twinbasic.rb: */\n") + for sym, why in UNMAPPED_SYMBOLS: + out.append(f"/* Symbol{sym} — {why} */\n") + + return "".join(out) + + +def render_scss(theme: dict[str, dict[str, str]], header: str, code_bg: str | None = None) -> str: + out = [f"/* {header} */\n"] + out.append("/* Selectors are the Rouge HTML formatter classes emitted by docs/_plugins/twinbasic.rb. */\n") + out.append("/* Scoped under .language-tb .highlight so they only repaint tB fenced code blocks. */\n\n") + out.append(".language-tb .highlight {\n") + + rules = [] + for rouge, sym, comment in ROUGE_TO_SYMBOL: + props = theme.get(sym) + if not props: + continue + rule = [f" .{rouge} {{ /* Symbol{sym} — {comment} */\n"] + for key in ("Color", "FontStyle", "FontWeight", "TextDecoration"): + if key in props: + rule.append(f" {CSS_PROP[key]}: {props[key]};\n") + rule.append(" }\n") + rules.append("".join(rule)) + out.append("\n".join(rules)) + out.append("}\n") + + if code_bg: + out.append("\n") + out.append("/* tB CodePanelBackColor scoped to tB code-block containers (.language-tb). */\n") + out.append("/* The .language-tb class lives on the outer .highlighter-rouge div emitted */\n") + out.append("/* by kramdown for ```tb``` fenced blocks, so `.language-tb.highlighter-rouge` */\n") + out.append("/* (no space) hits the outer container and `.language-tb ` hits */\n") + out.append("/* the nested .highlight / pre / etc. The partial is imported inside */\n") + out.append("/* `html.dark-mode { ... }` by just-the-docs-combined.scss, so SCSS nesting */\n") + out.append("/* confines these rules to dark mode automatically. */\n") + out.append(".language-tb.highlighter-rouge,\n") + out.append(".language-tb .highlight,\n") + out.append(".language-tb pre.highlight,\n") + out.append(".language-tb .highlight pre {\n") + out.append(f" background-color: {code_bg};\n") + out.append("}\n") + + return "".join(out) + + +def main() -> None: + light = parse_theme(THEMES_DIR / "Light.theme") + dark = parse_theme(THEMES_DIR / "Dark.theme") + classic = parse_theme(THEMES_DIR / "Classic.theme") + + light_syms = symbol_props(light) + dark_syms = symbol_props(dark) + classic_syms = {sym: dict(props) for sym, props in light_syms.items()} + for sym, props in symbol_props(classic).items(): + classic_syms.setdefault(sym, {}).update(props) + + # Flat CSS for inspection. + CSS_OUT_DIR.mkdir(parents=True, exist_ok=True) + (CSS_OUT_DIR / "twinbasic-light.css").write_text( + render_css(light_syms, "twinBASIC Light theme - Rouge syntax highlighting"), + encoding="utf-8", + ) + (CSS_OUT_DIR / "twinbasic-dark.css").write_text( + render_css(dark_syms, "twinBASIC Dark theme - Rouge syntax highlighting"), + encoding="utf-8", + ) + (CSS_OUT_DIR / "twinbasic-classic.css").write_text( + render_css( + classic_syms, + "twinBASIC Classic theme - Rouge syntax highlighting (Light + Classic overrides)", + ), + encoding="utf-8", + ) + + # SCSS partials shipped in the site (classic is inspection-only). + SCSS_OUT_DIR.mkdir(parents=True, exist_ok=True) + (SCSS_OUT_DIR / "_twinbasic-light.scss").write_text( + render_scss(light_syms, "twinBASIC Light theme - Rouge syntax highlighting"), + encoding="utf-8", + ) + (SCSS_OUT_DIR / "_twinbasic-dark.scss").write_text( + render_scss( + dark_syms, + "twinBASIC Dark theme - Rouge syntax highlighting", + code_bg=dark.get("CodePanelBackColor"), + ), + encoding="utf-8", + ) + + +if __name__ == "__main__": + main() diff --git a/scripts/themes/twinbasic-classic.css b/scripts/themes/twinbasic-classic.css new file mode 100644 index 0000000..a1cd7a5 --- /dev/null +++ b/scripts/themes/twinbasic-classic.css @@ -0,0 +1,200 @@ +/* twinBASIC Classic theme - Rouge syntax highlighting (Light + Classic overrides) */ +/* Selectors are the Rouge HTML formatter classes emitted by docs/_plugins/twinbasic.rb. */ + +.c1 { /* SymbolComment — ' comments and REM */ + color: #00801D; + font-style: normal; + font-weight: normal; + text-decoration: none; +} + +.cm { /* SymbolComment — C-style block comments */ + color: #00801D; + font-style: normal; + font-weight: normal; + text-decoration: none; +} + +.cp { /* SymbolConditionalCompilationDirective — #If / #ElseIf / #Else / #End If / #Const / #Region */ + color: #ad8c98; + font-style: normal; + font-weight: normal; + text-decoration: none; +} + +.k { /* SymbolKeyword — Dim, If, End, Sub, ... */ + color: #002DA6; + font-style: normal; + font-weight: normal; + text-decoration: none; +} + +.kd { /* SymbolKeyword — Option Strict / Explicit / Compare / Base */ + color: #002DA6; + font-style: normal; + font-weight: normal; + text-decoration: none; +} + +.kt { /* SymbolBuiltInDataType — Boolean, Integer, String, ... */ + color: #002DA6; + font-style: normal; + font-weight: normal; + text-decoration: none; +} + +.lb { /* SymbolLiteralBoolean — True, False */ + color: #002DA6; + font-style: normal; + font-weight: normal; + text-decoration: none; +} + +.lc { /* SymbolContinuationCharacter — '_' line-continuation marker */ + color: #808080; + font-style: normal; + font-weight: normal; + text-decoration: none; +} + +.ld { /* SymbolLiteralDate — #m/d/yyyy [h:mm:ss am/pm]# date-time literals */ + color: #002DA6; + font-style: normal; + font-weight: normal; + text-decoration: none; +} + +.le { /* SymbolLiteralEmpty — Empty */ + color: #002DA6; + font-style: normal; + font-weight: normal; + text-decoration: none; +} + +.ln { /* SymbolLiteralNothing — Nothing */ + color: #002DA6; + font-style: normal; + font-weight: normal; + text-decoration: none; +} + +.lu { /* SymbolLiteralNull — Null */ + color: #002DA6; + font-style: normal; + font-weight: normal; + text-decoration: none; +} + +.mi { /* SymbolLiteralNumeric — integer literals */ + color: #000000; + font-style: normal; + font-weight: normal; + text-decoration: none; +} + +.mf { /* SymbolLiteralNumeric — float literals */ + color: #000000; + font-style: normal; + font-weight: normal; + text-decoration: none; +} + +.s { /* SymbolLiteralString — string literals */ + color: #000000; + font-style: normal; + font-weight: normal; + text-decoration: none; +} + +.se { /* SymbolLiteralString — "" escape inside string literals */ + color: #000000; + font-style: normal; + font-weight: normal; + text-decoration: none; +} + +.o { /* SymbolOperator — +, -, =, <, >, &, ... */ + color: #000000; + font-style: normal; + font-weight: normal; + text-decoration: none; +} + +.ow { /* SymbolNamedOperator — And, Or, Not, Is, Mod, ... */ + color: #002DA6; + font-style: normal; + font-weight: normal; + text-decoration: none; +} + +.na { /* SymbolAttribute — [Documentation(...)] attribute names */ + color: #bfbfbf; + font-style: normal; + font-weight: normal; + text-decoration: none; +} + +.nb { /* SymbolClass — Debug, Err */ + color: #000000; + font-style: normal; + font-weight: normal; + text-decoration: none; +} + +.nc { /* SymbolClass — Class / CoClass / Enum / Interface / Type / Structure names */ + color: #000000; + font-style: normal; + font-weight: normal; + text-decoration: none; +} + +.nf { /* SymbolFunction — Function / Sub / Property names */ + color: #000000; + font-style: normal; + font-weight: normal; + text-decoration: none; +} + +.nn { /* SymbolModule — Module / Namespace / Imports targets */ + color: #000000; + font-style: normal; + font-weight: normal; + text-decoration: none; +} + +.nv { /* SymbolVariable — Dim / Const / ReDim variable names */ + color: #000000; + font-style: normal; + font-weight: normal; + text-decoration: none; +} + +/* tB Symbols with no dedicated Rouge class in twinbasic.rb: */ +/* SymbolConditionalCompilationExcludedCode — not tokenized — would need preproc evaluation */ +/* SymbolConstant — not distinguished from Name */ +/* SymbolDeclareFunction — not distinguished from Keyword .k */ +/* SymbolDeclareSub — not distinguished from Keyword .k */ +/* SymbolEnum — folded into Name::Class .nc */ +/* SymbolEnumMember — not distinguished from Name */ +/* SymbolField — not distinguished from Name */ +/* SymbolGenericDataType — not tokenized */ +/* SymbolGenericValue — not tokenized */ +/* SymbolGlobalVariablePrivate — not distinguished from Name */ +/* SymbolGlobalVariablePublic — not distinguished from Name */ +/* SymbolInterface — folded into Name::Class .nc */ +/* SymbolLateBoundFunction — not distinguished from Name */ +/* SymbolLibrary — not distinguished from Name */ +/* SymbolLineLabel — not tokenized */ +/* SymbolLineNumber — not tokenized */ +/* SymbolMe — folded into Keyword .k */ +/* SymbolMultiLineSeperator — not tokenized */ +/* SymbolNamedArgument — not tokenized */ +/* SymbolParamByRef — not tokenized */ +/* SymbolParamByVal — not tokenized */ +/* SymbolPropertyGet — folded into Keyword .k */ +/* SymbolPropertyLet — folded into Keyword .k */ +/* SymbolPropertySet — folded into Keyword .k */ +/* SymbolReturnValue — not tokenized */ +/* SymbolSub — folded into Name::Function .nf */ +/* SymbolUDT — folded into Name::Class .nc */ +/* SymbolVariableUndeclared — not distinguished from Name */ diff --git a/scripts/themes/twinbasic-dark.css b/scripts/themes/twinbasic-dark.css new file mode 100644 index 0000000..89bb56d --- /dev/null +++ b/scripts/themes/twinbasic-dark.css @@ -0,0 +1,200 @@ +/* twinBASIC Dark theme - Rouge syntax highlighting */ +/* Selectors are the Rouge HTML formatter classes emitted by docs/_plugins/twinbasic.rb. */ + +.c1 { /* SymbolComment — ' comments and REM */ + color: #448a63; + font-style: normal; + font-weight: normal; + text-decoration: none; +} + +.cm { /* SymbolComment — C-style block comments */ + color: #448a63; + font-style: normal; + font-weight: normal; + text-decoration: none; +} + +.cp { /* SymbolConditionalCompilationDirective — #If / #ElseIf / #Else / #End If / #Const / #Region */ + color: #ad8c98; + font-style: normal; + font-weight: normal; + text-decoration: none; +} + +.k { /* SymbolKeyword — Dim, If, End, Sub, ... */ + color: #6c8eda; + font-style: normal; + font-weight: normal; + text-decoration: none; +} + +.kd { /* SymbolKeyword — Option Strict / Explicit / Compare / Base */ + color: #6c8eda; + font-style: normal; + font-weight: normal; + text-decoration: none; +} + +.kt { /* SymbolBuiltInDataType — Boolean, Integer, String, ... */ + color: #b1551f; + font-style: normal; + font-weight: normal; + text-decoration: none; +} + +.lb { /* SymbolLiteralBoolean — True, False */ + color: #c495d3; + font-style: normal; + font-weight: normal; + text-decoration: none; +} + +.lc { /* SymbolContinuationCharacter — '_' line-continuation marker */ + color: #808080; + font-style: normal; + font-weight: normal; + text-decoration: none; +} + +.ld { /* SymbolLiteralDate — #m/d/yyyy [h:mm:ss am/pm]# date-time literals */ + color: #c495d3; + font-style: normal; + font-weight: normal; + text-decoration: none; +} + +.le { /* SymbolLiteralEmpty — Empty */ + color: #c495d3; + font-style: normal; + font-weight: normal; + text-decoration: none; +} + +.ln { /* SymbolLiteralNothing — Nothing */ + color: #c495d3; + font-style: normal; + font-weight: normal; + text-decoration: none; +} + +.lu { /* SymbolLiteralNull — Null */ + color: #c495d3; + font-style: normal; + font-weight: normal; + text-decoration: none; +} + +.mi { /* SymbolLiteralNumeric — integer literals */ + color: #aeca89; + font-style: normal; + font-weight: normal; + text-decoration: none; +} + +.mf { /* SymbolLiteralNumeric — float literals */ + color: #aeca89; + font-style: normal; + font-weight: normal; + text-decoration: none; +} + +.s { /* SymbolLiteralString — string literals */ + color: #aeca89; + font-style: oblique; + font-weight: normal; + text-decoration: none; +} + +.se { /* SymbolLiteralString — "" escape inside string literals */ + color: #aeca89; + font-style: oblique; + font-weight: normal; + text-decoration: none; +} + +.o { /* SymbolOperator — +, -, =, <, >, &, ... */ + color: #80a1a5; + font-style: normal; + font-weight: normal; + text-decoration: none; +} + +.ow { /* SymbolNamedOperator — And, Or, Not, Is, Mod, ... */ + color: #80a1a5; + font-style: normal; + font-weight: normal; + text-decoration: none; +} + +.na { /* SymbolAttribute — [Documentation(...)] attribute names */ + color: #5c5c53; + font-style: normal; + font-weight: normal; + text-decoration: none; +} + +.nb { /* SymbolClass — Debug, Err */ + color: #e4c685; + font-style: normal; + font-weight: normal; + text-decoration: none; +} + +.nc { /* SymbolClass — Class / CoClass / Enum / Interface / Type / Structure names */ + color: #e4c685; + font-style: normal; + font-weight: normal; + text-decoration: none; +} + +.nf { /* SymbolFunction — Function / Sub / Property names */ + color: #cf9a5d; + font-style: normal; + font-weight: normal; + text-decoration: none; +} + +.nn { /* SymbolModule — Module / Namespace / Imports targets */ + color: #a8a887; + font-style: normal; + font-weight: normal; + text-decoration: none; +} + +.nv { /* SymbolVariable — Dim / Const / ReDim variable names */ + color: #8b8b52; + font-style: normal; + font-weight: normal; + text-decoration: none; +} + +/* tB Symbols with no dedicated Rouge class in twinbasic.rb: */ +/* SymbolConditionalCompilationExcludedCode — not tokenized — would need preproc evaluation */ +/* SymbolConstant — not distinguished from Name */ +/* SymbolDeclareFunction — not distinguished from Keyword .k */ +/* SymbolDeclareSub — not distinguished from Keyword .k */ +/* SymbolEnum — folded into Name::Class .nc */ +/* SymbolEnumMember — not distinguished from Name */ +/* SymbolField — not distinguished from Name */ +/* SymbolGenericDataType — not tokenized */ +/* SymbolGenericValue — not tokenized */ +/* SymbolGlobalVariablePrivate — not distinguished from Name */ +/* SymbolGlobalVariablePublic — not distinguished from Name */ +/* SymbolInterface — folded into Name::Class .nc */ +/* SymbolLateBoundFunction — not distinguished from Name */ +/* SymbolLibrary — not distinguished from Name */ +/* SymbolLineLabel — not tokenized */ +/* SymbolLineNumber — not tokenized */ +/* SymbolMe — folded into Keyword .k */ +/* SymbolMultiLineSeperator — not tokenized */ +/* SymbolNamedArgument — not tokenized */ +/* SymbolParamByRef — not tokenized */ +/* SymbolParamByVal — not tokenized */ +/* SymbolPropertyGet — folded into Keyword .k */ +/* SymbolPropertyLet — folded into Keyword .k */ +/* SymbolPropertySet — folded into Keyword .k */ +/* SymbolReturnValue — not tokenized */ +/* SymbolSub — folded into Name::Function .nf */ +/* SymbolUDT — folded into Name::Class .nc */ +/* SymbolVariableUndeclared — not distinguished from Name */ diff --git a/scripts/themes/twinbasic-light.css b/scripts/themes/twinbasic-light.css new file mode 100644 index 0000000..ce01353 --- /dev/null +++ b/scripts/themes/twinbasic-light.css @@ -0,0 +1,200 @@ +/* twinBASIC Light theme - Rouge syntax highlighting */ +/* Selectors are the Rouge HTML formatter classes emitted by docs/_plugins/twinbasic.rb. */ + +.c1 { /* SymbolComment — ' comments and REM */ + color: #448a63; + font-style: normal; + font-weight: normal; + text-decoration: none; +} + +.cm { /* SymbolComment — C-style block comments */ + color: #448a63; + font-style: normal; + font-weight: normal; + text-decoration: none; +} + +.cp { /* SymbolConditionalCompilationDirective — #If / #ElseIf / #Else / #End If / #Const / #Region */ + color: #ad8c98; + font-style: normal; + font-weight: normal; + text-decoration: none; +} + +.k { /* SymbolKeyword — Dim, If, End, Sub, ... */ + color: #385ba9; + font-style: normal; + font-weight: normal; + text-decoration: none; +} + +.kd { /* SymbolKeyword — Option Strict / Explicit / Compare / Base */ + color: #385ba9; + font-style: normal; + font-weight: normal; + text-decoration: none; +} + +.kt { /* SymbolBuiltInDataType — Boolean, Integer, String, ... */ + color: #b1551f; + font-style: normal; + font-weight: normal; + text-decoration: none; +} + +.lb { /* SymbolLiteralBoolean — True, False */ + color: #b877ce; + font-style: normal; + font-weight: normal; + text-decoration: none; +} + +.lc { /* SymbolContinuationCharacter — '_' line-continuation marker */ + color: #808080; + font-style: normal; + font-weight: normal; + text-decoration: none; +} + +.ld { /* SymbolLiteralDate — #m/d/yyyy [h:mm:ss am/pm]# date-time literals */ + color: #b877ce; + font-style: normal; + font-weight: normal; + text-decoration: none; +} + +.le { /* SymbolLiteralEmpty — Empty */ + color: #b877ce; + font-style: normal; + font-weight: normal; + text-decoration: none; +} + +.ln { /* SymbolLiteralNothing — Nothing */ + color: #b877ce; + font-style: normal; + font-weight: normal; + text-decoration: none; +} + +.lu { /* SymbolLiteralNull — Null */ + color: #b877ce; + font-style: normal; + font-weight: normal; + text-decoration: none; +} + +.mi { /* SymbolLiteralNumeric — integer literals */ + color: #457e12; + font-style: normal; + font-weight: normal; + text-decoration: none; +} + +.mf { /* SymbolLiteralNumeric — float literals */ + color: #457e12; + font-style: normal; + font-weight: normal; + text-decoration: none; +} + +.s { /* SymbolLiteralString — string literals */ + color: #679f1e; + font-style: normal; + font-weight: normal; + text-decoration: none; +} + +.se { /* SymbolLiteralString — "" escape inside string literals */ + color: #679f1e; + font-style: normal; + font-weight: normal; + text-decoration: none; +} + +.o { /* SymbolOperator — +, -, =, <, >, &, ... */ + color: #80a1a5; + font-style: normal; + font-weight: normal; + text-decoration: none; +} + +.ow { /* SymbolNamedOperator — And, Or, Not, Is, Mod, ... */ + color: #385ba9; + font-style: normal; + font-weight: normal; + text-decoration: none; +} + +.na { /* SymbolAttribute — [Documentation(...)] attribute names */ + color: #bfbfbf; + font-style: normal; + font-weight: normal; + text-decoration: none; +} + +.nb { /* SymbolClass — Debug, Err */ + color: #a87300; + font-style: normal; + font-weight: normal; + text-decoration: none; +} + +.nc { /* SymbolClass — Class / CoClass / Enum / Interface / Type / Structure names */ + color: #a87300; + font-style: normal; + font-weight: normal; + text-decoration: none; +} + +.nf { /* SymbolFunction — Function / Sub / Property names */ + color: #b96300; + font-style: normal; + font-weight: normal; + text-decoration: none; +} + +.nn { /* SymbolModule — Module / Namespace / Imports targets */ + color: #89894d; + font-style: normal; + font-weight: normal; + text-decoration: none; +} + +.nv { /* SymbolVariable — Dim / Const / ReDim variable names */ + color: #939000; + font-style: normal; + font-weight: normal; + text-decoration: none; +} + +/* tB Symbols with no dedicated Rouge class in twinbasic.rb: */ +/* SymbolConditionalCompilationExcludedCode — not tokenized — would need preproc evaluation */ +/* SymbolConstant — not distinguished from Name */ +/* SymbolDeclareFunction — not distinguished from Keyword .k */ +/* SymbolDeclareSub — not distinguished from Keyword .k */ +/* SymbolEnum — folded into Name::Class .nc */ +/* SymbolEnumMember — not distinguished from Name */ +/* SymbolField — not distinguished from Name */ +/* SymbolGenericDataType — not tokenized */ +/* SymbolGenericValue — not tokenized */ +/* SymbolGlobalVariablePrivate — not distinguished from Name */ +/* SymbolGlobalVariablePublic — not distinguished from Name */ +/* SymbolInterface — folded into Name::Class .nc */ +/* SymbolLateBoundFunction — not distinguished from Name */ +/* SymbolLibrary — not distinguished from Name */ +/* SymbolLineLabel — not tokenized */ +/* SymbolLineNumber — not tokenized */ +/* SymbolMe — folded into Keyword .k */ +/* SymbolMultiLineSeperator — not tokenized */ +/* SymbolNamedArgument — not tokenized */ +/* SymbolParamByRef — not tokenized */ +/* SymbolParamByVal — not tokenized */ +/* SymbolPropertyGet — folded into Keyword .k */ +/* SymbolPropertyLet — folded into Keyword .k */ +/* SymbolPropertySet — folded into Keyword .k */ +/* SymbolReturnValue — not tokenized */ +/* SymbolSub — folded into Name::Function .nf */ +/* SymbolUDT — folded into Name::Class .nc */ +/* SymbolVariableUndeclared — not distinguished from Name */