332 lines · 9.5 KB · TypeScript
RawDownload
import deepmerge from "deepmerge"import { FontWeight, font_weights, useTheme } from "../common"import chroma from "chroma-js" export interface SyntaxHighlightStyle {    color?: string    weight?: FontWeight    underline?: boolean    italic?: boolean} export interface Syntax {    // == Text Styles ====== /    comment: SyntaxHighlightStyle    // elixir: doc comment    "comment.doc": SyntaxHighlightStyle    primary: SyntaxHighlightStyle    predictive: SyntaxHighlightStyle    hint: SyntaxHighlightStyle     // === Formatted Text ====== /    emphasis: SyntaxHighlightStyle    "emphasis.strong": SyntaxHighlightStyle    title: SyntaxHighlightStyle    link_uri: SyntaxHighlightStyle    link_text: SyntaxHighlightStyle    /** md: indented_code_block, fenced_code_block, code_span */    "text.literal": SyntaxHighlightStyle     // == Punctuation ====== /    punctuation: SyntaxHighlightStyle    /** Example: `(`, `[`, `{`...*/    "punctuation.bracket": SyntaxHighlightStyle    /**., ;*/    "punctuation.delimiter": SyntaxHighlightStyle    // js, ts: ${, } in a template literal    // yaml: *, &, ---, ...    "punctuation.special": SyntaxHighlightStyle    // md: list_marker_plus, list_marker_dot, etc    "punctuation.list_marker": SyntaxHighlightStyle     // == Strings ====== /     string: SyntaxHighlightStyle    // css: color_value    // js: this, super    // toml: offset_date_time, local_date_time...    "string.special": SyntaxHighlightStyle    // elixir: atom, quoted_atom, keyword, quoted_keyword    // ruby: simple_symbol, delimited_symbol...    "string.special.symbol"?: SyntaxHighlightStyle    // elixir, python, yaml...: escape_sequence    "string.escape"?: SyntaxHighlightStyle    // Regular expressions    "string.regex"?: SyntaxHighlightStyle     // == Types ====== /    // We allow Function here because all JS objects literals have this property    constructor: SyntaxHighlightStyle | Function // eslint-disable-line  @typescript-eslint/ban-types    variant: SyntaxHighlightStyle    type: SyntaxHighlightStyle    // js: predefined_type    "type.builtin"?: SyntaxHighlightStyle     // == Values    variable: SyntaxHighlightStyle    // this, ...    // css: -- (var(--foo))    // lua: self    "variable.special"?: SyntaxHighlightStyle    // c: statement_identifier,    label: SyntaxHighlightStyle    // css: tag_name, nesting_selector, universal_selector...    tag: SyntaxHighlightStyle    // css: attribute, pseudo_element_selector (tag_name),    attribute: SyntaxHighlightStyle    // css: class_name, property_name, namespace_name...    property: SyntaxHighlightStyle    // true, false, null, nullptr    constant: SyntaxHighlightStyle    // css: @media, @import, @supports...    // js: declare, implements, interface, keyof, public...    keyword: SyntaxHighlightStyle    // note: js enum is currently defined as a keyword    enum: SyntaxHighlightStyle    // -, --, ->, !=, &&, ||, <=...    operator: SyntaxHighlightStyle    number: SyntaxHighlightStyle    boolean: SyntaxHighlightStyle    // elixir: __MODULE__, __DIR__, __ENV__, etc    // go: nil, iota    "constant.builtin"?: SyntaxHighlightStyle     // == Functions ====== /     function: SyntaxHighlightStyle    // lua: assert, error, loadfile, tostring, unpack...    "function.builtin"?: SyntaxHighlightStyle    // go: call_expression, method_declaration    // js: call_expression, method_definition, pair (key, arrow function)    // rust: function_item name: (identifier)    "function.definition"?: SyntaxHighlightStyle    // rust: macro_definition name: (identifier)    "function.special.definition"?: SyntaxHighlightStyle    "function.method"?: SyntaxHighlightStyle    // ruby: identifier/"defined?" // Nate note: I don't fully understand this one.    "function.method.builtin"?: SyntaxHighlightStyle     // == Unsorted ====== /    // lua: hash_bang_line    preproc: SyntaxHighlightStyle    // elixir, python: interpolation (ex: foo in ${foo})    // js: template_substitution    embedded: SyntaxHighlightStyle} export type ThemeSyntax = Partial<Syntax> const default_syntax_highlight_style: Omit<SyntaxHighlightStyle, "color"> = {    weight: "normal",    underline: false,    italic: false,} function build_default_syntax(): Syntax {    const theme = useTheme()     // Make a temporary object that is allowed to be missing    // the "color" property for each style    const syntax: {        [key: string]: Omit<SyntaxHighlightStyle, "color">    } = {}     // then spread the default to each style    for (const key of Object.keys({} as Syntax)) {        syntax[key as keyof Syntax] = {            ...default_syntax_highlight_style,        }    }     // Mix the neutral and blue colors to get a    // predictive color distinct from any other color in the theme    const predictive = chroma        .mix(            theme.ramps.neutral(0.4).hex(),            theme.ramps.blue(0.4).hex(),            0.45,            "lch"        )        .hex()    // Mix the neutral and green colors to get a    // hint color distinct from any other color in the theme    const hint = chroma        .mix(            theme.ramps.neutral(0.6).hex(),            theme.ramps.blue(0.4).hex(),            0.45,            "lch"        )        .hex()     const color = {        primary: theme.ramps.neutral(1).hex(),        comment: theme.ramps.neutral(0.71).hex(),        punctuation: theme.ramps.neutral(0.86).hex(),        predictive: predictive,        hint: hint,        emphasis: theme.ramps.blue(0.5).hex(),        string: theme.ramps.orange(0.5).hex(),        function: theme.ramps.yellow(0.5).hex(),        type: theme.ramps.cyan(0.5).hex(),        constructor: theme.ramps.blue(0.5).hex(),        variant: theme.ramps.blue(0.5).hex(),        property: theme.ramps.blue(0.5).hex(),        enum: theme.ramps.orange(0.5).hex(),        operator: theme.ramps.orange(0.5).hex(),        number: theme.ramps.green(0.5).hex(),        boolean: theme.ramps.green(0.5).hex(),        constant: theme.ramps.green(0.5).hex(),        keyword: theme.ramps.blue(0.5).hex(),    }     // Then assign colors and use Syntax to enforce each style getting it's own color    const default_syntax: Syntax = {        ...syntax,        comment: {            color: color.comment,        },        "comment.doc": {            color: color.comment,        },        primary: {            color: color.primary,        },        predictive: {            color: color.predictive,            italic: true,        },        hint: {            color: color.hint,            weight: font_weights.bold,        },        emphasis: {            color: color.emphasis,        },        "emphasis.strong": {            color: color.emphasis,            weight: font_weights.bold,        },        title: {            color: color.primary,            weight: font_weights.bold,        },        link_uri: {            color: theme.ramps.green(0.5).hex(),            underline: true,        },        link_text: {            color: theme.ramps.orange(0.5).hex(),            italic: true,        },        "text.literal": {            color: color.string,        },        punctuation: {            color: color.punctuation,        },        "punctuation.bracket": {            color: color.punctuation,        },        "punctuation.delimiter": {            color: color.punctuation,        },        "punctuation.special": {            color: theme.ramps.neutral(0.86).hex(),        },        "punctuation.list_marker": {            color: color.punctuation,        },        string: {            color: color.string,        },        "string.special": {            color: color.string,        },        "string.special.symbol": {            color: color.string,        },        "string.escape": {            color: color.comment,        },        "string.regex": {            color: color.string,        },        constructor: {            color: theme.ramps.blue(0.5).hex(),        },        variant: {            color: theme.ramps.blue(0.5).hex(),        },        type: {            color: color.type,        },        variable: {            color: color.primary,        },        label: {            color: theme.ramps.blue(0.5).hex(),        },        tag: {            color: theme.ramps.blue(0.5).hex(),        },        attribute: {            color: theme.ramps.blue(0.5).hex(),        },        property: {            color: theme.ramps.blue(0.5).hex(),        },        constant: {            color: color.constant,        },        keyword: {            color: color.keyword,        },        enum: {            color: color.enum,        },        operator: {            color: color.operator,        },        number: {            color: color.number,        },        boolean: {            color: color.boolean,        },        function: {            color: color.function,        },        preproc: {            color: color.primary,        },        embedded: {            color: color.primary,        },    }     return default_syntax} export function build_syntax(): Syntax {    const theme = useTheme()     const default_syntax: Syntax = build_default_syntax()     if (!theme.syntax) {        return default_syntax    }     const syntax = deepmerge<Syntax, Partial<ThemeSyntax>>(        default_syntax,        theme.syntax,        {            arrayMerge: (destinationArray, sourceArray) => [                ...destinationArray,                ...sourceArray,            ],        }    )     return syntax}