1export const CODE_BLOCK_LANGUAGES = [
2 { id: "", label: "Plain text" },
3 { id: "asm", label: "Assembly" },
4 { id: "bash", label: "Bash" },
5 { id: "c", label: "C" },
6 { id: "cpp", label: "C++" },
7 { id: "csharp", label: "C#" },
8 { id: "css", label: "CSS" },
9 { id: "dart", label: "Dart" },
10 { id: "diff", label: "Diff" },
11 { id: "dockerfile", label: "Dockerfile" },
12 { id: "elixir", label: "Elixir" },
13 { id: "go", label: "Go" },
14 { id: "graphql", label: "GraphQL" },
15 { id: "haskell", label: "Haskell" },
16 { id: "html", label: "HTML" },
17 { id: "java", label: "Java" },
18 { id: "javascript", label: "JavaScript" },
19 { id: "json", label: "JSON" },
20 { id: "jsx", label: "JSX" },
21 { id: "kotlin", label: "Kotlin" },
22 { id: "lua", label: "Lua" },
23 { id: "markdown", label: "Markdown" },
24 { id: "php", label: "PHP" },
25 { id: "powershell", label: "PowerShell" },
26 { id: "python", label: "Python" },
27 { id: "r", label: "R" },
28 { id: "ruby", label: "Ruby" },
29 { id: "rust", label: "Rust" },
30 { id: "scala", label: "Scala" },
31 { id: "scss", label: "SCSS" },
32 { id: "sql", label: "SQL" },
33 { id: "svelte", label: "Svelte" },
34 { id: "swift", label: "Swift" },
35 { id: "toml", label: "TOML" },
36 { id: "tsx", label: "TSX" },
37 { id: "typescript", label: "TypeScript" },
38 { id: "vue", label: "Vue" },
39 { id: "xml", label: "XML" },
40 { id: "yaml", label: "YAML" },
41 { id: "zig", label: "Zig" },
42] as const;
43
44export type CodeBlockLanguage = { readonly id: string; readonly label: string };
45
46const LANGUAGE_ALIASES: Record<string, string> = {
47 assembly: "asm",
48 cs: "csharp",
49 "c++": "cpp",
50 js: "javascript",
51 md: "markdown",
52 plaintext: "",
53 py: "python",
54 rb: "ruby",
55 rs: "rust",
56 s: "asm",
57 sh: "bash",
58 shell: "bash",
59 text: "",
60 ts: "typescript",
61 txt: "",
62 yml: "yaml",
63};
64
65export function languageOfInfoString(info: string): string {
66 return info.trim().split(/\s+/, 1)[0] ?? "";
67}
68
69/** Plain text clears the language unless trailing metadata would be parsed as one. */
70export function withInfoStringLanguage(info: string, language: string): string {
71 const leading = /^\s*/.exec(info)![0];
72 const rest = info.slice(leading.length + languageOfInfoString(info).length);
73 if (language) return `${leading}${language}${rest}`;
74 return rest.trim() ? `${leading}text${rest}` : "";
75}
76
77export function codeLanguageEntry(language: string): CodeBlockLanguage | null {
78 const id = LANGUAGE_ALIASES[language.toLowerCase()] ?? language;
79 return CODE_BLOCK_LANGUAGES.find((entry) => entry.id === id.toLowerCase()) ?? null;
80}
81
82export function codeLanguageLabel(language: string): string {
83 return codeLanguageEntry(language)?.label ?? language;
84}