Skip to content

Commit 28d5f94

Browse files
committed
Add grouped data language highlighting
1 parent d692a5d commit 28d5f94

9 files changed

Lines changed: 190 additions & 33 deletions

File tree

src/lib/editor/code-block-highlight.ts

Lines changed: 53 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,56 @@ const keywordSets: Partial<Record<DcLanguageId, readonly string[]>> = {
353353
"while",
354354
],
355355
json: ["false", "null", "true"],
356+
yaml: ["false", "null", "off", "on", "true", "yes", "no"],
357+
toml: ["false", "true"],
358+
sql: [
359+
"add",
360+
"alter",
361+
"and",
362+
"as",
363+
"asc",
364+
"between",
365+
"by",
366+
"case",
367+
"create",
368+
"delete",
369+
"desc",
370+
"distinct",
371+
"drop",
372+
"else",
373+
"end",
374+
"exists",
375+
"false",
376+
"from",
377+
"group",
378+
"having",
379+
"in",
380+
"inner",
381+
"insert",
382+
"into",
383+
"is",
384+
"join",
385+
"left",
386+
"like",
387+
"limit",
388+
"not",
389+
"null",
390+
"on",
391+
"or",
392+
"order",
393+
"outer",
394+
"right",
395+
"select",
396+
"set",
397+
"table",
398+
"then",
399+
"true",
400+
"union",
401+
"update",
402+
"values",
403+
"when",
404+
"where",
405+
],
356406
asm: [
357407
"add",
358408
"and",
@@ -416,7 +466,7 @@ const cFamilyLanguages = new Set<DcLanguageId>([
416466
"mojo",
417467
]);
418468

419-
const hashCommentLanguages = new Set<DcLanguageId>(["bash", "python"]);
469+
const hashCommentLanguages = new Set<DcLanguageId>(["bash", "python", "yaml", "toml"]);
420470

421471
const htmlLikeLanguages = new Set<DcLanguageId>(["html", "svelte", "astro", "tailwind", "unocss"]);
422472

@@ -466,7 +516,7 @@ function keywordRegexFor(language: DcLanguageId): RegExp | undefined {
466516
return undefined;
467517
}
468518

469-
return new RegExp(`\\b(${languageKeywords.join("|")})\\b`, "g");
519+
return new RegExp(`\\b(${languageKeywords.join("|")})\\b`, language === "sql" ? "gi" : "g");
470520
}
471521

472522
function keywordSetFor(language: DcLanguageId): ReadonlySet<string> {
@@ -487,7 +537,7 @@ function lineCommentPrefixes(language: DcLanguageId): readonly string[] {
487537
return ["#"];
488538
}
489539

490-
if (language === "haskell") {
540+
if (language === "haskell" || language === "sql") {
491541
return ["--"];
492542
}
493543

src/lib/editor/markdown-import.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,14 @@ function normalizeLanguage(value: string | undefined, fallback: DcLanguageId): D
8282
mjs: "javascript",
8383
ts: "typescript",
8484
mts: "typescript",
85+
jsonc: "json",
86+
yml: "yaml",
87+
mysql: "sql",
88+
postgres: "sql",
89+
postgresql: "sql",
90+
sqlite: "sql",
91+
sqlite3: "sql",
92+
mssql: "sql",
8593
udiff: "diff",
8694
patch: "patch",
8795
shell: "bash",

src/lib/highlighter/catalog.ts

Lines changed: 51 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,63 @@
11
type SupportedLanguage = {
22
readonly id: string;
33
readonly label: string;
4+
readonly group: SupportedLanguageGroupId;
45
readonly shikiLanguage?: string;
56
};
67

8+
type SupportedLanguageGroupId = "native" | "web" | "data" | "diff" | "script" | "general";
9+
10+
export const supportedLanguageGroupLabels = {
11+
native: "시스템/네이티브",
12+
web: "웹/프론트엔드",
13+
data: "데이터/설정",
14+
diff: "변경/패치",
15+
script: "스크립트",
16+
general: "일반 언어",
17+
} as const satisfies Record<SupportedLanguageGroupId, string>;
18+
719
export const supportedLanguages = [
8-
{ id: "c", label: "C" },
9-
{ id: "cpp", label: "C++" },
10-
{ id: "csharp", label: "C#" },
11-
{ id: "asm", label: "Assembly" },
12-
{ id: "javascript", label: "JavaScript" },
13-
{ id: "typescript", label: "TypeScript" },
14-
{ id: "jsx", label: "React JSX" },
15-
{ id: "tsx", label: "React TSX" },
16-
{ id: "svelte", label: "Svelte" },
17-
{ id: "astro", label: "Astro" },
18-
{ id: "html", label: "HTML" },
19-
{ id: "css", label: "CSS" },
20-
{ id: "tailwind", label: "Tailwind CSS", shikiLanguage: "html" },
21-
{ id: "unocss", label: "UnoCSS", shikiLanguage: "html" },
22-
{ id: "php", label: "PHP" },
23-
{ id: "json", label: "JSON" },
24-
{ id: "diff", label: "Diff" },
25-
{ id: "patch", label: "Patch", shikiLanguage: "diff" },
26-
{ id: "bash", label: "Bash" },
27-
{ id: "python", label: "Python" },
28-
{ id: "java", label: "Java" },
29-
{ id: "go", label: "Go" },
30-
{ id: "rust", label: "Rust" },
31-
{ id: "haskell", label: "Haskell" },
32-
{ id: "scala", label: "Scala" },
33-
{ id: "zig", label: "Zig" },
34-
{ id: "julia", label: "Julia" },
35-
{ id: "mojo", label: "Mojo" },
20+
{ id: "c", label: "C", group: "native" },
21+
{ id: "cpp", label: "C++", group: "native" },
22+
{ id: "csharp", label: "C#", group: "native" },
23+
{ id: "asm", label: "Assembly", group: "native" },
24+
{ id: "rust", label: "Rust", group: "native" },
25+
{ id: "zig", label: "Zig", group: "native" },
26+
{ id: "javascript", label: "JavaScript", group: "web" },
27+
{ id: "typescript", label: "TypeScript", group: "web" },
28+
{ id: "jsx", label: "React JSX", group: "web" },
29+
{ id: "tsx", label: "React TSX", group: "web" },
30+
{ id: "svelte", label: "Svelte", group: "web" },
31+
{ id: "astro", label: "Astro", group: "web" },
32+
{ id: "html", label: "HTML", group: "web" },
33+
{ id: "css", label: "CSS", group: "web" },
34+
{ id: "tailwind", label: "Tailwind CSS", group: "web", shikiLanguage: "html" },
35+
{ id: "unocss", label: "UnoCSS", group: "web", shikiLanguage: "html" },
36+
{ id: "php", label: "PHP", group: "web" },
37+
{ id: "json", label: "JSON", group: "data" },
38+
{ id: "yaml", label: "YAML", group: "data" },
39+
{ id: "toml", label: "TOML", group: "data" },
40+
{ id: "sql", label: "SQL", group: "data" },
41+
{ id: "diff", label: "Diff", group: "diff" },
42+
{ id: "patch", label: "Patch", group: "diff", shikiLanguage: "diff" },
43+
{ id: "bash", label: "Bash", group: "script" },
44+
{ id: "python", label: "Python", group: "script" },
45+
{ id: "java", label: "Java", group: "general" },
46+
{ id: "go", label: "Go", group: "general" },
47+
{ id: "haskell", label: "Haskell", group: "general" },
48+
{ id: "scala", label: "Scala", group: "general" },
49+
{ id: "julia", label: "Julia", group: "general" },
50+
{ id: "mojo", label: "Mojo", group: "general" },
3651
] as const satisfies readonly SupportedLanguage[];
3752

53+
export const supportedLanguageGroups = Object.entries(supportedLanguageGroupLabels)
54+
.map(([id, label]) => ({
55+
id: id as SupportedLanguageGroupId,
56+
label,
57+
languages: supportedLanguages.filter((language) => language.group === id),
58+
}))
59+
.filter((group) => group.languages.length > 0);
60+
3861
export const supportedThemes = [
3962
{ id: "github-dark", label: "GitHub Dark" },
4063
{ id: "github-light", label: "GitHub Light" },

src/lib/highlighter/shiki-client.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ const languageInputs: Record<DcLanguageId, LanguageInput> = {
2626
unocss: () => import("@shikijs/langs/html").then((module) => module.default),
2727
php: () => import("@shikijs/langs/php").then((module) => module.default),
2828
json: () => import("@shikijs/langs/json").then((module) => module.default),
29+
yaml: () => import("@shikijs/langs/yaml").then((module) => module.default),
30+
toml: () => import("@shikijs/langs/toml").then((module) => module.default),
31+
sql: () => import("@shikijs/langs/sql").then((module) => module.default),
2932
diff: () => import("@shikijs/langs/diff").then((module) => module.default),
3033
patch: () => import("@shikijs/langs/diff").then((module) => module.default),
3134
bash: () => import("@shikijs/langs/bash").then((module) => module.default),

src/routes/+page.svelte

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
defaultLanguage,
4848
defaultTheme,
4949
isSupportedLanguage,
50+
supportedLanguageGroups,
5051
supportedLanguages,
5152
supportedThemes,
5253
type DcLanguageId,
@@ -2988,8 +2989,12 @@
29882989
.run(),
29892990
)}
29902991
>
2991-
{#each supportedLanguages as item}
2992-
<option value={item.id}>{item.label}</option>
2992+
{#each supportedLanguageGroups as group}
2993+
<optgroup label={group.label}>
2994+
{#each group.languages as item}
2995+
<option value={item.id}>{item.label}</option>
2996+
{/each}
2997+
</optgroup>
29932998
{/each}
29942999
</select>
29953000
</label>

tests/unit/code-block-highlight.test.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,18 @@ describe("editor code block highlighting", () => {
4545
expect(tokenTexts(code, "comment", "asm")).toEqual(["; exit"]);
4646
});
4747

48+
it("highlights data and query code block tokens", () => {
49+
expect(tokenTexts("enabled: true # comment", "keyword", "yaml")).toEqual(["true"]);
50+
expect(tokenTexts("enabled: true # comment", "comment", "yaml")).toEqual(["# comment"]);
51+
expect(tokenTexts('name = "dc"\nenabled = false', "keyword", "toml")).toEqual(["false"]);
52+
expect(tokenTexts("SELECT title FROM posts WHERE id = 1;", "keyword", "sql")).toEqual([
53+
"SELECT",
54+
"FROM",
55+
"WHERE",
56+
]);
57+
expect(tokenTexts("select * from posts -- latest", "comment", "sql")).toEqual(["-- latest"]);
58+
});
59+
4860
it("uses CSS block comments without treating double slashes as comments", () => {
4961
const code = [
5062
"/* color token should stay protected */",
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { describe, expect, it } from "vitest";
2+
import {
3+
isSupportedLanguage,
4+
supportedLanguageGroups,
5+
supportedLanguages,
6+
} from "../../src/lib/highlighter/catalog";
7+
8+
describe("highlighter catalog", () => {
9+
it("groups data and query languages together", () => {
10+
const dataGroup = supportedLanguageGroups.find((group) => group.id === "data");
11+
12+
expect(dataGroup?.label).toBe("데이터/설정");
13+
expect(dataGroup?.languages.map((language) => language.id)).toEqual([
14+
"json",
15+
"yaml",
16+
"toml",
17+
"sql",
18+
]);
19+
});
20+
21+
it("keeps every listed language selectable and grouped", () => {
22+
const groupedLanguageIds = new Set(
23+
supportedLanguageGroups.flatMap((group) => group.languages.map((language) => language.id)),
24+
);
25+
26+
for (const language of supportedLanguages) {
27+
expect(isSupportedLanguage(language.id)).toBe(true);
28+
expect(groupedLanguageIds.has(language.id)).toBe(true);
29+
}
30+
});
31+
});

tests/unit/markdown-import.test.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,27 @@ describe("markdown import", () => {
178178
}
179179
});
180180

181+
it("normalizes data and query code fence aliases", () => {
182+
expect(
183+
parseMarkdownToDocument(["```yml", "enabled: true", "```"].join("\n")).content?.[0],
184+
).toEqual({
185+
type: "codeBlock",
186+
attrs: { language: "yaml" },
187+
content: [{ type: "text", text: "enabled: true" }],
188+
});
189+
190+
for (const fence of ["mysql", "postgres", "sqlite"]) {
191+
expect(
192+
parseMarkdownToDocument(["```" + fence, "select * from posts", "```"].join("\n"))
193+
.content?.[0],
194+
).toEqual({
195+
type: "codeBlock",
196+
attrs: { language: "sql" },
197+
content: [{ type: "text", text: "select * from posts" }],
198+
});
199+
}
200+
});
201+
181202
it("keeps fenced code line highlight metadata", () => {
182203
expect(
183204
parseMarkdownToDocument(

tests/unit/shiki-client.test.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ const requestedLanguageSamples = [
2626
token: "i-carbon-send",
2727
},
2828
{ language: "php", code: "<?php echo strlen('dc');", token: "strlen" },
29+
{ language: "json", code: '{ "name": "dc", "enabled": true }', token: "enabled" },
30+
{ language: "yaml", code: "name: dc\nenabled: true", token: "enabled" },
31+
{ language: "toml", code: 'name = "dc"\nenabled = true', token: "enabled" },
32+
{ language: "sql", code: "SELECT title FROM posts WHERE id = 1;", token: "SELECT" },
2933
{
3034
language: "diff",
3135
code: "-const oldValue = 1;\n+const newValue = 2;",

0 commit comments

Comments
 (0)