소스 검색

refactor(tui): generate syntax from V2 theme (#38397)

James Long 3 주 전
부모
커밋
d86f732df3
2개의 변경된 파일95개의 추가작업 그리고 4개의 파일을 삭제
  1. 2 4
      packages/tui/src/context/theme.tsx
  2. 93 0
      packages/tui/src/theme/v2/syntax.ts

+ 2 - 4
packages/tui/src/context/theme.tsx

@@ -4,10 +4,8 @@ import {
   DEFAULT_THEMES,
   addTheme,
   allThemes,
-  generateSyntax,
   hasTheme,
   isTheme,
-  resolveTheme,
   selectedForeground,
   setCustomThemes,
   setSystemTheme,
@@ -16,6 +14,7 @@ import {
   type Theme,
   type ThemeJson,
 } from "../theme"
+import { generateSyntax } from "../theme/v2/syntax"
 import { generateSystem, terminalMode } from "../theme/system"
 import { discoverThemes, themeDirectories } from "../theme/discovery"
 import { createComponentTheme, type ComponentTheme } from "../theme/v2/component"
@@ -279,7 +278,6 @@ const themeContext = createSimpleContext({
       if (supported.includes(store.mode)) return store.mode
       return supported[0] ?? store.mode
     }
-    const legacySyntaxTheme = createMemo(() => resolveTheme(source(), mode()))
     const valuesV2 = createMemo(() => resolveThemeFile(file(), mode(), sourceName()))
     valuesV2()
     themePerformance.set("Init", `${(performance.now() - initStarted).toFixed(2)} ms`)
@@ -299,7 +297,7 @@ const themeContext = createSimpleContext({
 
     createEffect(() => renderer.setBackgroundColor(valuesV2().background.default))
 
-    const syntax = createSyntaxStyleMemo(() => generateSyntax(legacySyntaxTheme()))
+    const syntax = createSyntaxStyleMemo(() => generateSyntax(valuesV2(), mode()))
     function contextual(context: ContextName) {
       return contextualServices[context]
     }

+ 93 - 0
packages/tui/src/theme/v2/syntax.ts

@@ -0,0 +1,93 @@
+import { SyntaxStyle, type RGBA, type ThemeTokenStyle } from "@opentui/core"
+import type { Mode, ResolvedThemeView } from "./index"
+
+export function generateSyntax(theme: ResolvedThemeView, mode: Mode) {
+  const step = mode === "light" ? 800 : 200
+  const syntax = theme.syntax
+  const markdown = theme.markdown
+  const feedback = theme.text.feedback
+
+  return SyntaxStyle.fromTheme([
+    rule(["default"], theme.text.default),
+    rule(["prompt"], theme.hue.accent[step]),
+    rule(["extmark.file"], feedback.warning.default, { bold: true }),
+    rule(["extmark.agent"], theme.categorical[0][step], { bold: true }),
+    // V1 migration preserves its selected/inverse foreground in this action state.
+    rule(["extmark.paste"], theme.text.action.primary.focused, {
+      background: feedback.warning.default,
+      bold: true,
+    }),
+    rule(["comment", "comment.documentation"], syntax.comment, { italic: true }),
+    rule(["string", "symbol", "character.special", "character"], syntax.string),
+    rule(["number", "boolean", "constant", "float"], syntax.number),
+    rule(["keyword.return", "keyword.conditional", "keyword.repeat", "keyword.coroutine"], syntax.keyword, {
+      italic: true,
+    }),
+    rule(["keyword.type"], syntax.type, { bold: true, italic: true }),
+    rule(["keyword.function", "function.method"], syntax.function),
+    rule(["keyword"], syntax.keyword, { italic: true }),
+    rule(["keyword.import", "string.escape", "string.regexp", "tag.attribute", "keyword.export"], syntax.keyword),
+    rule(["operator", "keyword.operator", "punctuation.delimiter", "keyword.conditional.ternary"], syntax.operator),
+    rule(
+      ["variable", "variable.parameter", "function.method.call", "function.call", "property", "parameter", "field"],
+      syntax.variable,
+    ),
+    rule(["variable.member", "function", "constructor"], syntax.function),
+    rule(["type", "module", "class", "namespace"], syntax.type),
+    rule(["type.definition"], syntax.type, { bold: true }),
+    rule(["punctuation", "punctuation.bracket"], syntax.punctuation),
+    rule(
+      ["variable.builtin", "type.builtin", "function.builtin", "module.builtin", "constant.builtin", "variable.super"],
+      feedback.error.default,
+    ),
+    rule(["keyword.directive", "keyword.modifier", "keyword.exception"], syntax.keyword, { italic: true }),
+    rule(["punctuation.special", "tag.delimiter"], syntax.operator),
+    rule(
+      [
+        "markup.heading",
+        "markup.heading.2",
+        "markup.heading.3",
+        "markup.heading.4",
+        "markup.heading.5",
+        "markup.heading.6",
+      ],
+      markdown.heading,
+      { bold: true },
+    ),
+    rule(["markup.heading.1"], markdown.heading, { bold: true, underline: true }),
+    rule(["markup.bold", "markup.strong"], markdown.strong, { bold: true }),
+    rule(["markup.italic"], markdown.emphasis, { italic: true }),
+    rule(["markup.list"], markdown.listItem),
+    rule(["markup.quote"], markdown.blockQuote, { italic: true }),
+    rule(["markup.raw", "markup.raw.block"], markdown.code),
+    rule(["markup.raw.inline"], markdown.code, { background: theme.background.default }),
+    rule(["markup.link", "markup.link.url", "string.special", "string.special.url"], markdown.link, {
+      underline: true,
+    }),
+    rule(["markup.link.label"], markdown.linkText, { underline: true }),
+    rule(["label"], markdown.linkText),
+    rule(["spell", "nospell"], theme.text.default),
+    rule(["markup.underline"], theme.text.default, { underline: true }),
+    rule(["comment.error"], feedback.error.default, { italic: true, bold: true }),
+    rule(["comment.warning"], feedback.warning.default, { italic: true, bold: true }),
+    rule(["comment.todo", "comment.note"], feedback.info.default, { italic: true, bold: true }),
+    rule(["attribute", "annotation"], feedback.warning.default),
+    rule(["tag"], feedback.error.default),
+    rule(["markup.strikethrough", "markup.list.unchecked", "debug"], theme.text.subdued),
+    rule(["markup.list.checked"], feedback.success.default),
+    rule(["diff.plus"], theme.diff.text.added, { background: theme.diff.background.added }),
+    rule(["diff.minus"], theme.diff.text.removed, { background: theme.diff.background.removed }),
+    rule(["diff.delta"], theme.diff.text.context, { background: theme.diff.background.context }),
+    rule(["error"], feedback.error.default, { bold: true }),
+    rule(["warning"], feedback.warning.default, { bold: true }),
+    rule(["info"], feedback.info.default),
+  ])
+}
+
+function rule(
+  scope: string[],
+  foreground: RGBA,
+  style: Omit<ThemeTokenStyle["style"], "foreground"> = {},
+): ThemeTokenStyle {
+  return { scope, style: { foreground, ...style } }
+}