From 85f193dd09a099741cfb2c527c925cc20f0b9f6b Mon Sep 17 00:00:00 2001 From: Nate Butler Date: Thu, 27 Jul 2023 12:25:53 -0400 Subject: [PATCH] Extract syntax highlighting properties from tree-sitter highlight queries --- crates/zed/src/languages/lua/highlights.scm | 4 +- crates/zed/src/languages/php/highlights.scm | 4 +- .../src/languages/typescript/highlights.scm | 6 +- styles/src/style_tree/editor.ts | 30 +- styles/src/theme/create_theme.ts | 11 +- styles/src/theme/syntax.ts | 382 +++--------------- styles/src/theme/theme_config.ts | 4 +- styles/src/theme/tokens/theme.ts | 6 +- styles/src/themes/atelier/common.ts | 5 +- styles/src/themes/ayu/common.ts | 6 +- styles/src/themes/gruvbox/gruvbox-common.ts | 6 +- styles/src/themes/one/one-dark.ts | 4 +- styles/src/themes/one/one-light.ts | 2 - styles/src/themes/rose-pine/common.ts | 4 +- styles/src/types/extract_syntax_types.ts | 102 +++++ styles/src/types/syntax.ts | 203 ++++++++++ 16 files changed, 419 insertions(+), 360 deletions(-) create mode 100644 styles/src/types/extract_syntax_types.ts create mode 100644 styles/src/types/syntax.ts diff --git a/crates/zed/src/languages/lua/highlights.scm b/crates/zed/src/languages/lua/highlights.scm index f061bbf8f91651605a7bcf946dcb576a82045aa6..60ca9de36b15098e9c76eaddff20238d5056adea 100644 --- a/crates/zed/src/languages/lua/highlights.scm +++ b/crates/zed/src/languages/lua/highlights.scm @@ -158,7 +158,7 @@ [ "{" "}" -] @constructor) +] @method.constructor) ;; Functions @@ -195,4 +195,4 @@ (number) @number -(string) @string \ No newline at end of file +(string) @string diff --git a/crates/zed/src/languages/php/highlights.scm b/crates/zed/src/languages/php/highlights.scm index fcb087c47d14dbc036ed79a50be7ff1b57ebc4e8..cfb03cbccad037d207ff1a1388c12eec22644f48 100644 --- a/crates/zed/src/languages/php/highlights.scm +++ b/crates/zed/src/languages/php/highlights.scm @@ -47,8 +47,8 @@ ((name) @constant.builtin (#match? @constant.builtin "^__[A-Z][A-Z\d_]+__$")) -((name) @constructor - (#match? @constructor "^[A-Z]")) +((name) @method.constructor +(#match? @method.constructor "^[A-Z]")) ((name) @variable.builtin (#eq? @variable.builtin "this")) diff --git a/crates/zed/src/languages/typescript/highlights.scm b/crates/zed/src/languages/typescript/highlights.scm index bf086ea156f6ee9c2aca6bb9d2ebdd3f91997999..ba6b329e0e0def9c5d0c31d0df94525825ed2c45 100644 --- a/crates/zed/src/languages/typescript/highlights.scm +++ b/crates/zed/src/languages/typescript/highlights.scm @@ -43,8 +43,8 @@ ; Special identifiers -((identifier) @constructor - (#match? @constructor "^[A-Z]")) +((identifier) @method.constructor + (#match? @method.constructor "^[A-Z]")) ((identifier) @type (#match? @type "^[A-Z]")) @@ -218,4 +218,4 @@ "type" "readonly" "override" -] @keyword \ No newline at end of file +] @keyword diff --git a/styles/src/style_tree/editor.ts b/styles/src/style_tree/editor.ts index acf983e8bee13f24d2897191c283217e1ee2ee55..ccbb33e21dd8459f665531b9b4b19af8831946bd 100644 --- a/styles/src/style_tree/editor.ts +++ b/styles/src/style_tree/editor.ts @@ -9,9 +9,9 @@ import { } from "./components" import hover_popover from "./hover_popover" -import { build_syntax } from "../theme/syntax" import { interactive, toggleable } from "../element" import { useTheme } from "../theme" +import chroma from "chroma-js" export default function editor(): any { const theme = useTheme() @@ -48,16 +48,28 @@ export default function editor(): any { } } - const syntax = build_syntax() - return { - text_color: syntax.primary.color, + text_color: theme.syntax.primary.color, background: background(layer), active_line_background: with_opacity(background(layer, "on"), 0.75), highlighted_line_background: background(layer, "on"), // Inline autocomplete suggestions, Co-pilot suggestions, etc. - hint: syntax.hint, - suggestion: syntax.predictive, + hint: chroma + .mix( + theme.ramps.neutral(0.6).hex(), + theme.ramps.blue(0.4).hex(), + 0.45, + "lch" + ) + .hex(), + suggestion: chroma + .mix( + theme.ramps.neutral(0.4).hex(), + theme.ramps.blue(0.4).hex(), + 0.45, + "lch" + ) + .hex(), code_actions: { indicator: toggleable({ base: interactive({ @@ -255,8 +267,8 @@ export default function editor(): any { invalid_warning_diagnostic: diagnostic(theme.middle, "base"), hover_popover: hover_popover(), link_definition: { - color: syntax.link_uri.color, - underline: syntax.link_uri.underline, + color: theme.syntax.link_uri.color, + underline: theme.syntax.link_uri.underline, }, jump_icon: interactive({ base: { @@ -314,6 +326,6 @@ export default function editor(): any { color: border_color(layer), }, }, - syntax, + syntax: theme.syntax, } } diff --git a/styles/src/theme/create_theme.ts b/styles/src/theme/create_theme.ts index d2701f8341af973a3a4511c149c983cb221fa14d..e52c4dc95b361572d8cbdc388c04146cecc81e8c 100644 --- a/styles/src/theme/create_theme.ts +++ b/styles/src/theme/create_theme.ts @@ -1,12 +1,12 @@ import { Scale, Color } from "chroma-js" -import { Syntax, ThemeSyntax, SyntaxHighlightStyle } from "./syntax" -export { Syntax, ThemeSyntax, SyntaxHighlightStyle } import { ThemeConfig, ThemeAppearance, - ThemeConfigInputColors, + ThemeConfigInputColors } from "./theme_config" import { get_ramps } from "./ramps" +import { syntaxStyle } from "./syntax" +import { Syntax } from "../types/syntax" export interface Theme { name: string @@ -31,7 +31,7 @@ export interface Theme { modal_shadow: Shadow players: Players - syntax?: Partial + syntax: Syntax } export interface Meta { @@ -119,7 +119,6 @@ export function create_theme(theme: ThemeConfig): Theme { name, appearance, input_color, - override: { syntax }, } = theme const is_light = appearance === ThemeAppearance.Light @@ -162,6 +161,8 @@ export function create_theme(theme: ThemeConfig): Theme { "7": player(ramps.yellow), } + const syntax = syntaxStyle(ramps, theme.override.syntax ? theme.override.syntax : {}) + return { name, is_light, diff --git a/styles/src/theme/syntax.ts b/styles/src/theme/syntax.ts index 540a1d0ff9822d5a8c2cf292ddba5ace6839ac6d..d39496a412904647ea2deb8c74048c864ab31550 100644 --- a/styles/src/theme/syntax.ts +++ b/styles/src/theme/syntax.ts @@ -1,332 +1,80 @@ import deepmerge from "deepmerge" -import { FontWeight, font_weights, useTheme } from "../common" -import chroma from "chroma-js" +import { font_weights, ThemeConfigInputSyntax, RampSet } from "../common" +import { Syntax, SyntaxHighlightStyle, allSyntaxKeys } from "../types/syntax" -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 +// Apply defaults to any missing syntax properties that are not defined manually +function apply_defaults(ramps: RampSet, syntax_highlights: Partial): Syntax { + const restKeys: (keyof Syntax)[] = allSyntaxKeys.filter(key => !syntax_highlights[key]) -const default_syntax_highlight_style: Omit = { - weight: "normal", - underline: false, - italic: false, -} - -function build_default_syntax(): Syntax { - const theme = useTheme() + const completeSyntax: Syntax = {} as Syntax - // Make a temporary object that is allowed to be missing - // the "color" property for each style - const syntax: { - [key: string]: Omit - } = {} + const defaults: SyntaxHighlightStyle = { + color: ramps.neutral(1).hex(), + } - // then spread the default to each style - for (const key of Object.keys({} as Syntax)) { - syntax[key as keyof Syntax] = { - ...default_syntax_highlight_style, + for (const key of restKeys) { + { + completeSyntax[key] = { + ...defaults, + } } } - // 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 mergedBaseSyntax = Object.assign(completeSyntax, syntax_highlights) - 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 + return mergedBaseSyntax } -export function build_syntax(): Syntax { - const theme = useTheme() - - const default_syntax: Syntax = build_default_syntax() +// Merge the base syntax with the theme syntax overrides +// This is a deep merge, so any nested properties will be merged as well +// This allows for a theme to only override a single property of a syntax highlight style +const merge_syntax = (baseSyntax: Syntax, theme_syntax_overrides: ThemeConfigInputSyntax): Syntax => { + return deepmerge(baseSyntax, theme_syntax_overrides, { + arrayMerge: (destinationArray, sourceArray) => [ + ...destinationArray, + ...sourceArray, + ], + }) +} - if (!theme.syntax) { - return default_syntax +/** Returns a complete Syntax object of the combined styles of a theme's syntax overrides and the default syntax styles */ +export const syntaxStyle = (ramps: RampSet, theme_syntax_overrides: ThemeConfigInputSyntax): Syntax => { + const syntax_highlights: Partial = { + "comment": { color: ramps.neutral(0.71).hex() }, + "comment.doc": { color: ramps.neutral(0.71).hex() }, + primary: { color: ramps.neutral(1).hex() }, + emphasis: { color: ramps.blue(0.5).hex() }, + "emphasis.strong": { color: ramps.blue(0.5).hex(), weight: font_weights.bold }, + link_uri: { color: ramps.green(0.5).hex(), underline: true }, + link_text: { color: ramps.orange(0.5).hex(), italic: true }, + "text.literal": { color: ramps.orange(0.5).hex() }, + punctuation: { color: ramps.neutral(0.86).hex() }, + "punctuation.bracket": { color: ramps.neutral(0.86).hex() }, + "punctuation.special": { color: ramps.neutral(0.86).hex() }, + "punctuation.delimiter": { color: ramps.neutral(0.86).hex() }, + "punctuation.list_marker": { color: ramps.neutral(0.86).hex() }, + string: { color: ramps.orange(0.5).hex() }, + "string.special": { color: ramps.orange(0.5).hex() }, + "string.special.symbol": { color: ramps.orange(0.5).hex() }, + "string.escape": { color: ramps.neutral(0.71).hex() }, + "string.regex": { color: ramps.orange(0.5).hex() }, + "method.constructor": { color: ramps.blue(0.5).hex() }, + type: { color: ramps.cyan(0.5).hex() }, + variable: { color: ramps.neutral(1).hex() }, + label: { color: ramps.blue(0.5).hex() }, + attribute: { color: ramps.blue(0.5).hex() }, + property: { color: ramps.blue(0.5).hex() }, + constant: { color: ramps.green(0.5).hex() }, + keyword: { color: ramps.blue(0.5).hex() }, + operator: { color: ramps.orange(0.5).hex() }, + number: { color: ramps.green(0.5).hex() }, + boolean: { color: ramps.green(0.5).hex() }, + function: { color: ramps.yellow(0.5).hex() }, + preproc: { color: ramps.neutral(1).hex() }, + embedded: { color: ramps.neutral(1).hex() }, } - const syntax = deepmerge>( - default_syntax, - theme.syntax, - { - arrayMerge: (destinationArray, sourceArray) => [ - ...destinationArray, - ...sourceArray, - ], - } - ) - - return syntax + const baseSyntax = apply_defaults(ramps, syntax_highlights) + const mergedSyntax = merge_syntax(baseSyntax, theme_syntax_overrides) + return mergedSyntax } diff --git a/styles/src/theme/theme_config.ts b/styles/src/theme/theme_config.ts index bc8f07425f9e676a27d36ec929ea9a116006c694..8473bbb600608cac6157f894f3e9ac5d91c31a31 100644 --- a/styles/src/theme/theme_config.ts +++ b/styles/src/theme/theme_config.ts @@ -1,5 +1,5 @@ import { Scale, Color } from "chroma-js" -import { Syntax } from "./syntax" +import { SyntaxHighlightStyle, SyntaxProperty } from "../types/syntax" interface ThemeMeta { /** The name of the theme */ @@ -55,7 +55,7 @@ export type ThemeConfigInputColorsKeys = keyof ThemeConfigInputColors * } * ``` */ -export type ThemeConfigInputSyntax = Partial +export type ThemeConfigInputSyntax = Partial>> interface ThemeConfigOverrides { syntax: ThemeConfigInputSyntax diff --git a/styles/src/theme/tokens/theme.ts b/styles/src/theme/tokens/theme.ts index f759bc813910416d88a2097134dc95654d6ab3b3..f9e83e0512012e0c5d699ac432869a70520a9839 100644 --- a/styles/src/theme/tokens/theme.ts +++ b/styles/src/theme/tokens/theme.ts @@ -6,15 +6,13 @@ import { } from "@tokens-studio/types" import { Shadow, - SyntaxHighlightStyle, - ThemeSyntax, } from "../create_theme" import { LayerToken, layer_token } from "./layer" import { PlayersToken, players_token } from "./players" import { color_token } from "./token" -import { Syntax } from "../syntax" import editor from "../../style_tree/editor" import { useTheme } from "../../../src/common" +import { Syntax, SyntaxHighlightStyle } from "../../types/syntax" interface ThemeTokens { name: SingleOtherToken @@ -51,7 +49,7 @@ const modal_shadow_token = (): SingleBoxShadowToken => { return create_shadow_token(shadow, "modal_shadow") } -type ThemeSyntaxColorTokens = Record +type ThemeSyntaxColorTokens = Record function syntax_highlight_style_color_tokens( syntax: Syntax diff --git a/styles/src/themes/atelier/common.ts b/styles/src/themes/atelier/common.ts index b76ccc5b607a2f149a35114a519ddaaf2d1b254d..09226b336c0aae3d42a4e99130e1c46875d6065b 100644 --- a/styles/src/themes/atelier/common.ts +++ b/styles/src/themes/atelier/common.ts @@ -1,4 +1,4 @@ -import { ThemeLicenseType, ThemeSyntax, ThemeFamilyMeta } from "../../common" +import { ThemeLicenseType, ThemeFamilyMeta, ThemeConfigInputSyntax } from "../../common" export interface Variant { colors: { @@ -29,7 +29,7 @@ export const meta: ThemeFamilyMeta = { "https://atelierbram.github.io/syntax-highlighting/atelier-schemes/cave/", } -export const build_syntax = (variant: Variant): ThemeSyntax => { +export const build_syntax = (variant: Variant): ThemeConfigInputSyntax => { const { colors } = variant return { primary: { color: colors.base06 }, @@ -50,7 +50,6 @@ export const build_syntax = (variant: Variant): ThemeSyntax => { property: { color: colors.base08 }, variable: { color: colors.base06 }, "variable.special": { color: colors.base0E }, - variant: { color: colors.base0A }, keyword: { color: colors.base0E }, } } diff --git a/styles/src/themes/ayu/common.ts b/styles/src/themes/ayu/common.ts index 2bd0bbf259aef2d9fc6c084f1da3c72379927026..870445588646b57f347fe9e90e07b286b3b73790 100644 --- a/styles/src/themes/ayu/common.ts +++ b/styles/src/themes/ayu/common.ts @@ -3,8 +3,8 @@ import { chroma, color_ramp, ThemeLicenseType, - ThemeSyntax, ThemeFamilyMeta, + ThemeConfigInputSyntax, } from "../../common" export const ayu = { @@ -27,7 +27,7 @@ export const build_theme = (t: typeof dark, light: boolean) => { purple: t.syntax.constant.hex(), } - const syntax: ThemeSyntax = { + const syntax: ThemeConfigInputSyntax = { constant: { color: t.syntax.constant.hex() }, "string.regex": { color: t.syntax.regexp.hex() }, string: { color: t.syntax.string.hex() }, @@ -61,7 +61,7 @@ export const build_theme = (t: typeof dark, light: boolean) => { } } -export const build_syntax = (t: typeof dark): ThemeSyntax => { +export const build_syntax = (t: typeof dark): ThemeConfigInputSyntax => { return { constant: { color: t.syntax.constant.hex() }, "string.regex": { color: t.syntax.regexp.hex() }, diff --git a/styles/src/themes/gruvbox/gruvbox-common.ts b/styles/src/themes/gruvbox/gruvbox-common.ts index 2fa6b58faadb91b5689c5eac93baf706f6faa391..95e45efa95122ec3ca37d2dc57360ca55ec83e85 100644 --- a/styles/src/themes/gruvbox/gruvbox-common.ts +++ b/styles/src/themes/gruvbox/gruvbox-common.ts @@ -4,8 +4,8 @@ import { ThemeAppearance, ThemeLicenseType, ThemeConfig, - ThemeSyntax, ThemeFamilyMeta, + ThemeConfigInputSyntax, } from "../../common" const meta: ThemeFamilyMeta = { @@ -214,7 +214,7 @@ const build_variant = (variant: Variant): ThemeConfig => { magenta: color_ramp(chroma(variant.colors.gray)), } - const syntax: ThemeSyntax = { + const syntax: ThemeConfigInputSyntax = { primary: { color: neutral[is_light ? 0 : 8] }, "text.literal": { color: colors.blue }, comment: { color: colors.gray }, @@ -229,7 +229,7 @@ const build_variant = (variant: Variant): ThemeConfig => { "string.special.symbol": { color: colors.aqua }, "string.regex": { color: colors.orange }, type: { color: colors.yellow }, - enum: { color: colors.orange }, + // enum: { color: colors.orange }, tag: { color: colors.aqua }, constant: { color: colors.yellow }, keyword: { color: colors.red }, diff --git a/styles/src/themes/one/one-dark.ts b/styles/src/themes/one/one-dark.ts index f672b892ee040e60745f3d0f8bd6875c743ed46a..97f3922f36f57c7452c8face077a8b2bc8997858 100644 --- a/styles/src/themes/one/one-dark.ts +++ b/styles/src/themes/one/one-dark.ts @@ -54,7 +54,6 @@ export const theme: ThemeConfig = { syntax: { boolean: { color: color.orange }, comment: { color: color.grey }, - enum: { color: color.red }, "emphasis.strong": { color: color.orange }, function: { color: color.blue }, keyword: { color: color.purple }, @@ -73,8 +72,7 @@ export const theme: ThemeConfig = { "text.literal": { color: color.green }, type: { color: color.teal }, "variable.special": { color: color.orange }, - variant: { color: color.blue }, - constructor: { color: color.blue }, + "method.constructor": { color: color.blue }, }, }, } diff --git a/styles/src/themes/one/one-light.ts b/styles/src/themes/one/one-light.ts index c3de7826c96679fb1c1f2b1a9d81324687d919b9..655428757898a7f07f4524377721ab62ef1f5b53 100644 --- a/styles/src/themes/one/one-light.ts +++ b/styles/src/themes/one/one-light.ts @@ -55,7 +55,6 @@ export const theme: ThemeConfig = { syntax: { boolean: { color: color.orange }, comment: { color: color.grey }, - enum: { color: color.red }, "emphasis.strong": { color: color.orange }, function: { color: color.blue }, keyword: { color: color.purple }, @@ -73,7 +72,6 @@ export const theme: ThemeConfig = { "text.literal": { color: color.green }, type: { color: color.teal }, "variable.special": { color: color.orange }, - variant: { color: color.blue }, }, }, } diff --git a/styles/src/themes/rose-pine/common.ts b/styles/src/themes/rose-pine/common.ts index 5c5482a754a634bd2338af2613327554ec36d13c..decccc0a6dc8b1062ec8d5a88a699350a79bf322 100644 --- a/styles/src/themes/rose-pine/common.ts +++ b/styles/src/themes/rose-pine/common.ts @@ -1,4 +1,4 @@ -import { ThemeSyntax } from "../../common" +import { ThemeConfigInputSyntax } from "../../common" export const color = { default: { @@ -54,7 +54,7 @@ export const color = { }, } -export const syntax = (c: typeof color.default): Partial => { +export const syntax = (c: typeof color.default): ThemeConfigInputSyntax => { return { comment: { color: c.muted }, operator: { color: c.pine }, diff --git a/styles/src/types/extract_syntax_types.ts b/styles/src/types/extract_syntax_types.ts new file mode 100644 index 0000000000000000000000000000000000000000..3bf089518233a8b264e71853ad83e2634f9c1ab3 --- /dev/null +++ b/styles/src/types/extract_syntax_types.ts @@ -0,0 +1,102 @@ +import fs from 'fs' +import path from 'path' +import readline from 'readline' + +function escapeTypeName(name: string): string { + return `'${name.replace('@', '').toLowerCase()}'` +} + +const generatedNote = `// This file is generated by extract_syntax_types.ts +// Do not edit this file directly +// It is generated from the highlight.scm files in the zed crate + +// To regenerate this file manually: +// 'npm run extract-syntax-types' from ./styles` + +const defaultTextProperty = ` /** Default text color */ + | 'primary'` + +const main = async () => { + const pathFromRoot = 'crates/zed/src/languages' + const directoryPath = path.join(__dirname, '../../../', pathFromRoot) + const stylesMap: Record> = {} + const propertyLanguageMap: Record> = {} + + const processFile = async (filePath: string, language: string) => { + const fileStream = fs.createReadStream(filePath) + const rl = readline.createInterface({ + input: fileStream, + crlfDelay: Infinity, + }) + + for await (const line of rl) { + const cleanedLine = line.replace(/"@[a-zA-Z0-9_.]*"/g, "") + const match = cleanedLine.match(/@(\w+\.*)*/g) + if (match) { + match.forEach((property) => { + const formattedProperty = escapeTypeName(property) + // Only add non-empty properties + if (formattedProperty !== "''") { + if (!propertyLanguageMap[formattedProperty]) { + propertyLanguageMap[formattedProperty] = new Set() + } + propertyLanguageMap[formattedProperty].add(language) + } + }) + } + } + } + + const directories = fs.readdirSync(directoryPath, { withFileTypes: true }) + .filter(dirent => dirent.isDirectory()) + .map(dirent => dirent.name) + + for (const dir of directories) { + const highlightsFilePath = path.join(directoryPath, dir, 'highlights.scm') + if (fs.existsSync(highlightsFilePath)) { + await processFile(highlightsFilePath, dir) + } + } + + for (const [language, properties] of Object.entries(stylesMap)) { + console.log(`${language}: ${Array.from(properties).join(', ')}`) + } + + const sortedProperties = Object.entries(propertyLanguageMap).sort(([propA], [propB]) => propA.localeCompare(propB)) + + const outStream = fs.createWriteStream(path.join(__dirname, 'syntax.ts')) + let allProperties = "" + const syntaxKeys = [] + for (const [property, languages] of sortedProperties) { + let languagesArray = Array.from(languages) + const moreThanSeven = languagesArray.length > 7 + // Limit to the first 7 languages, append "..." if more than 7 + languagesArray = languagesArray.slice(0, 7) + if (moreThanSeven) { + languagesArray.push('...') + } + const languagesString = languagesArray.join(', ') + const comment = `/** ${languagesString} */` + allProperties += ` ${comment}\n | ${property} \n` + syntaxKeys.push(property) + } + outStream.write(`${generatedNote} + +export type SyntaxHighlightStyle = { + color: string, + fade_out?: number, + italic?: boolean, + underline?: boolean, + weight?: string, +} + +export type Syntax = Record +export type SyntaxOverride = Partial + +export type SyntaxProperty = \n${defaultTextProperty}\n\n${allProperties} + +export const allSyntaxKeys: SyntaxProperty[] = [\n ${syntaxKeys.join(',\n ')}\n]`) + outStream.end() +} + +main().catch(console.error) diff --git a/styles/src/types/syntax.ts b/styles/src/types/syntax.ts new file mode 100644 index 0000000000000000000000000000000000000000..b74edfdf8463e510c40fee84c35e5d22d6586d83 --- /dev/null +++ b/styles/src/types/syntax.ts @@ -0,0 +1,203 @@ +// This file is generated by extract_syntax_types.ts +// Do not edit this file directly +// It is generated from the highlight.scm files in the zed crate + +// To regenerate this file manually: +// 'npm run extract-syntax-types' from ./styles + +export type SyntaxHighlightStyle = { + color: string, + fade_out?: number, + italic?: boolean, + underline?: boolean, + weight?: string, +} + +export type Syntax = Record +export type SyntaxOverride = Partial + +export type SyntaxProperty = + /** Default text color */ + | 'primary' + + /** elixir */ + | '__attribute__' + /** elixir */ + | '__name__' + /** elixir */ + | '_sigil_name' + /** css, heex, lua */ + | 'attribute' + /** javascript, lua, tsx, typescript, yaml */ + | 'boolean' + /** elixir */ + | 'comment.doc' + /** elixir */ + | 'comment.unused' + /** bash, c, cpp, css, elixir, elm, erb, ... */ + | 'comment' + /** elixir, go, javascript, lua, php, python, racket, ... */ + | 'constant.builtin' + /** bash, c, cpp, elixir, elm, glsl, heex, ... */ + | 'constant' + /** glsl */ + | 'delimiter' + /** bash, elixir, javascript, python, ruby, tsx, typescript */ + | 'embedded' + /** markdown */ + | 'emphasis.strong' + /** markdown */ + | 'emphasis' + /** go, python, racket, ruby, scheme */ + | 'escape' + /** lua */ + | 'field' + /** lua, php, python */ + | 'function.builtin' + /** elm, lua, rust */ + | 'function.definition' + /** ruby */ + | 'function.method.builtin' + /** go, javascript, php, python, ruby, rust, tsx, ... */ + | 'function.method' + /** rust */ + | 'function.special.definition' + /** c, cpp, glsl, rust */ + | 'function.special' + /** bash, c, cpp, css, elixir, elm, glsl, ... */ + | 'function' + /** elm */ + | 'identifier' + /** glsl */ + | 'keyword.function' + /** bash, c, cpp, css, elixir, elm, erb, ... */ + | 'keyword' + /** c, cpp, glsl */ + | 'label' + /** markdown */ + | 'link_text' + /** markdown */ + | 'link_uri' + /** lua, php, tsx, typescript */ + | 'method.constructor' + /** lua */ + | 'method' + /** heex */ + | 'module' + /** svelte */ + | 'none' + /** bash, c, cpp, css, elixir, glsl, go, ... */ + | 'number' + /** bash, c, cpp, css, elixir, elm, glsl, ... */ + | 'operator' + /** lua */ + | 'parameter' + /** lua */ + | 'preproc' + /** bash, c, cpp, css, glsl, go, html, ... */ + | 'property' + /** c, cpp, elixir, elm, heex, html, javascript, ... */ + | 'punctuation.bracket' + /** c, cpp, css, elixir, elm, heex, javascript, ... */ + | 'punctuation.delimiter' + /** markdown */ + | 'punctuation.list_marker' + /** elixir, javascript, python, ruby, tsx, typescript, yaml */ + | 'punctuation.special' + /** elixir */ + | 'punctuation' + /** glsl */ + | 'storageclass' + /** elixir, elm, yaml */ + | 'string.escape' + /** elixir, javascript, racket, ruby, tsx, typescript */ + | 'string.regex' + /** elixir, ruby */ + | 'string.special.symbol' + /** css, elixir, toml */ + | 'string.special' + /** bash, c, cpp, css, elixir, elm, glsl, ... */ + | 'string' + /** svelte */ + | 'tag.delimiter' + /** css, heex, php, svelte */ + | 'tag' + /** markdown */ + | 'text.literal' + /** markdown */ + | 'title' + /** javascript, php, rust, tsx, typescript */ + | 'type.builtin' + /** glsl */ + | 'type.qualifier' + /** c, cpp, css, elixir, elm, glsl, go, ... */ + | 'type' + /** glsl, php */ + | 'variable.builtin' + /** cpp, css, javascript, lua, racket, ruby, rust, ... */ + | 'variable.special' + /** c, cpp, elm, glsl, go, javascript, lua, ... */ + | 'variable' + + +export const allSyntaxKeys: SyntaxProperty[] = [ + '__attribute__', + '__name__', + '_sigil_name', + 'attribute', + 'boolean', + 'comment.doc', + 'comment.unused', + 'comment', + 'constant.builtin', + 'constant', + 'delimiter', + 'embedded', + 'emphasis.strong', + 'emphasis', + 'escape', + 'field', + 'function.builtin', + 'function.definition', + 'function.method.builtin', + 'function.method', + 'function.special.definition', + 'function.special', + 'function', + 'identifier', + 'keyword.function', + 'keyword', + 'label', + 'link_text', + 'link_uri', + 'method.constructor', + 'method', + 'module', + 'none', + 'number', + 'operator', + 'parameter', + 'preproc', + 'property', + 'punctuation.bracket', + 'punctuation.delimiter', + 'punctuation.list_marker', + 'punctuation.special', + 'punctuation', + 'storageclass', + 'string.escape', + 'string.regex', + 'string.special.symbol', + 'string.special', + 'string', + 'tag.delimiter', + 'tag', + 'text.literal', + 'title', + 'type.builtin', + 'type.qualifier', + 'type', + 'variable.builtin', + 'variable.special', + 'variable' +] \ No newline at end of file