refactor single source of truth for language→scope mapping

Replace the parallel grammar-name list and scope_of_lang match expression with a single 'languages' table of (file, scope, aliases) triples. Both registry loading and scope lookup derive from it, eliminating the risk of drift between the two.

Commit
7782b0359f67e6ae8a432c5a747930e60f7b4379
Author
Claude Sonnet 4 <claude@anthropic.invalid>
Author date
Committer
Marius Peter <dev@marius-peter.com>
Committer date
lib/highlight/highlight_grammars.ml
index 5a47abd6..fb4ba5fd 100644..100644
@@ -2,13 +2,72 @@
2 2
3 3 Loads grammars embedded at build time via [ocaml-crunch] and registers them
4 4 with a shared {!TmLanguage.t} instance. Language lookup is by the canonical
5 Removed: name string produced by {!Syntax.detect}. *)
5 Added: name string produced by {!Syntax.detect}.
6 6
7 Added: The {!languages} table is the single source of truth for which grammars are
8 Added: shipped and what scope each language maps to. Both the registry loader and
9 Added: {!scope_of_lang} derive from it. *)
10 Added:
11 Added: (** Each entry is [(grammar_file, scope_name, aliases)] where [aliases] includes
12 Added: the canonical name and any accepted variants. *)
13 Added: let languages =
14 Added: [
15 Added: ("c", "source.c", [ "c" ]);
16 Added: ("clojure", "source.clojure", [ "clojure" ]);
17 Added: ("cpp", "source.cpp", [ "cpp" ]);
18 Added: ("csharp", "source.cs", [ "csharp" ]);
19 Added: ("css", "source.css", [ "css" ]);
20 Added: ("dart", "source.dart", [ "dart" ]);
21 Added: ("diff", "source.diff", [ "diff" ]);
22 Added: ("dockerfile", "source.dockerfile", [ "dockerfile" ]);
23 Added: ("dune", "source.dune", [ "dune" ]);
24 Added: ("elixir", "source.elixir", [ "elixir" ]);
25 Added: ("erlang", "source.erlang", [ "erlang" ]);
26 Added: ("fortran", "source.fortran.free", [ "fortran" ]);
27 Added: ("go", "source.go", [ "go" ]);
28 Added: ("groovy", "source.groovy", [ "groovy" ]);
29 Added: ("haskell", "source.haskell", [ "haskell" ]);
30 Added: ("html", "text.html.basic", [ "html" ]);
31 Added: ("java", "source.java", [ "java" ]);
32 Added: ("javascript", "source.js", [ "javascript" ]);
33 Added: ("json", "source.json", [ "json" ]);
34 Added: ("julia", "source.julia", [ "julia" ]);
35 Added: ("kotlin", "source.kotlin", [ "kotlin" ]);
36 Added: ("lisp", "source.lisp", [ "lisp" ]);
37 Added: ("lua", "source.lua", [ "lua" ]);
38 Added: ("makefile", "source.makefile", [ "makefile" ]);
39 Added: ("markdown", "text.html.markdown", [ "markdown" ]);
40 Added: ("nix", "source.nix", [ "nix" ]);
41 Added: ("objective-c", "source.objc", [ "objective-c"; "objc" ]);
42 Added: ("ocaml", "source.ocaml", [ "ocaml" ]);
43 Added: ("ocamldoc", "source.ocaml.ocamldoc", [ "ocamldoc"; "mld" ]);
44 Added: ("opam", "source.ocaml.opam", [ "opam" ]);
45 Added: ("org", "source.org", [ "org" ]);
46 Added: ("perl", "source.perl", [ "perl" ]);
47 Added: ("php", "source.php", [ "php" ]);
48 Added: ("powershell", "source.powershell", [ "powershell" ]);
49 Added: ("protobuf", "source.proto", [ "protobuf"; "proto" ]);
50 Added: ("python", "source.python", [ "python" ]);
51 Added: ("r", "source.r", [ "r" ]);
52 Added: ("ruby", "source.ruby", [ "ruby" ]);
53 Added: ("rust", "source.rust", [ "rust" ]);
54 Added: ("scala", "source.scala", [ "scala" ]);
55 Added: ("shell", "source.shell", [ "bash"; "shell"; "sh" ]);
56 Added: ("sql", "source.sql", [ "sql" ]);
57 Added: ("swift", "source.swift", [ "swift" ]);
58 Added: ("terraform", "source.hcl.terraform", [ "terraform"; "hcl" ]);
59 Added: ("toml", "source.toml", [ "toml"; "ini" ]);
60 Added: ("typescript", "source.ts", [ "typescript" ]);
61 Added: ("vue", "text.html.vue", [ "vue" ]);
62 Added: ("xml", "text.html.basic", [ "xml" ]);
63 Added: ("yaml", "source.yaml", [ "yaml" ]);
64 Added: ("zig", "source.zig", [ "zig" ]);
65 Added: ]
66 Added:
7 67 (** The shared grammar registry, lazily initialised. *)
8 68 let registry : TmLanguage.t Lazy.t =
9 69 lazy
10 70 (let t = TmLanguage.create () in
11 Removed: (* Each embedded file is a valid TextMate grammar JSON blob. *)
12 71 let load filename =
13 72 match Grammar_data.read filename with
14 73 | None -> ()
@@ -17,122 +76,20 @@
17 76 let json = Yojson.Basic.from_string data in
18 77 let grammar = TmLanguage.of_yojson_exn json in
19 78 TmLanguage.add_grammar t grammar
20 Removed: with _ ->
21 Removed: (* Silently skip malformed grammars *)
22 Removed: ())
79 Added: with _ -> ())
23 80 in
24 Removed: (* Register every grammar we ship. *)
25 Removed: List.iter
26 Removed: (fun name -> load (name ^ ".json"))
27 Removed: [
28 Removed: "c";
29 Removed: "clojure";
30 Removed: "cpp";
31 Removed: "csharp";
32 Removed: "css";
33 Removed: "dart";
34 Removed: "diff";
35 Removed: "dockerfile";
36 Removed: "dune";
37 Removed: "elixir";
38 Removed: "erlang";
39 Removed: "fortran";
40 Removed: "go";
41 Removed: "groovy";
42 Removed: "haskell";
43 Removed: "html";
44 Removed: "java";
45 Removed: "javascript";
46 Removed: "json";
47 Removed: "julia";
48 Removed: "kotlin";
49 Removed: "lisp";
50 Removed: "lua";
51 Removed: "makefile";
52 Removed: "markdown";
53 Removed: "nix";
54 Removed: "objective-c";
55 Removed: "ocaml";
56 Removed: "ocamldoc";
57 Removed: "opam";
58 Removed: "org";
59 Removed: "perl";
60 Removed: "php";
61 Removed: "powershell";
62 Removed: "protobuf";
63 Removed: "python";
64 Removed: "r";
65 Removed: "ruby";
66 Removed: "rust";
67 Removed: "scala";
68 Removed: "shell";
69 Removed: "sql";
70 Removed: "swift";
71 Removed: "terraform";
72 Removed: "toml";
73 Removed: "typescript";
74 Removed: "vue";
75 Removed: "xml";
76 Removed: "yaml";
77 Removed: "zig";
78 Removed: ];
81 Added: List.iter (fun (name, _, _) -> load (name ^ ".json")) languages;
79 82 t)
80 83
81 Removed: (** Map from the canonical language name (as returned by {!Syntax.detect}) to
82 Removed: the scopeName used by the grammar file. This bridges the naming gap.
84 Added: (** Scope lookup table, built once from {!languages}. *)
85 Added: let scope_table : (string, string) Hashtbl.t =
86 Added: let tbl = Hashtbl.create 128 in
87 Added: List.iter
88 Added: (fun (_, scope, aliases) ->
89 Added: List.iter (fun alias -> Hashtbl.replace tbl alias scope) aliases)
90 Added: languages;
91 Added: tbl
83 92
84 Removed: Only languages for which a grammar JSON is actually bundled appear here.
85 Removed: Languages detected by {!Syntax} but without a shipped grammar (e.g. elixir,
86 Removed: erlang, lisp, awk) gracefully degrade to plain text in {!Highlight}. *)
87 Removed: let scope_of_lang = function
88 Removed: | "c" -> Some "source.c"
89 Removed: | "clojure" -> Some "source.clojure"
90 Removed: | "cpp" -> Some "source.cpp"
91 Removed: | "csharp" -> Some "source.cs"
92 Removed: | "css" -> Some "source.css"
93 Removed: | "dart" -> Some "source.dart"
94 Removed: | "diff" -> Some "source.diff"
95 Removed: | "dockerfile" -> Some "source.dockerfile"
96 Removed: | "dune" -> Some "source.dune"
97 Removed: | "elixir" -> Some "source.elixir"
98 Removed: | "erlang" -> Some "source.erlang"
99 Removed: | "fortran" -> Some "source.fortran.free"
100 Removed: | "go" -> Some "source.go"
101 Removed: | "groovy" -> Some "source.groovy"
102 Removed: | "haskell" -> Some "source.haskell"
103 Removed: | "html" -> Some "text.html.basic"
104 Removed: | "java" -> Some "source.java"
105 Removed: | "javascript" -> Some "source.js"
106 Removed: | "json" -> Some "source.json"
107 Removed: | "julia" -> Some "source.julia"
108 Removed: | "kotlin" -> Some "source.kotlin"
109 Removed: | "lisp" -> Some "source.lisp"
110 Removed: | "lua" -> Some "source.lua"
111 Removed: | "makefile" -> Some "source.makefile"
112 Removed: | "markdown" -> Some "text.html.markdown"
113 Removed: | "nix" -> Some "source.nix"
114 Removed: | "objective-c" | "objc" -> Some "source.objc"
115 Removed: | "ocaml" -> Some "source.ocaml"
116 Removed: | "ocamldoc" | "mld" -> Some "source.ocaml.ocamldoc"
117 Removed: | "opam" -> Some "source.ocaml.opam"
118 Removed: | "org" -> Some "source.org"
119 Removed: | "perl" -> Some "source.perl"
120 Removed: | "php" -> Some "source.php"
121 Removed: | "powershell" -> Some "source.powershell"
122 Removed: | "protobuf" | "proto" -> Some "source.proto"
123 Removed: | "python" -> Some "source.python"
124 Removed: | "r" -> Some "source.r"
125 Removed: | "ruby" -> Some "source.ruby"
126 Removed: | "rust" -> Some "source.rust"
127 Removed: | "scala" -> Some "source.scala"
128 Removed: | "bash" | "shell" | "sh" -> Some "source.shell"
129 Removed: | "sql" -> Some "source.sql"
130 Removed: | "swift" -> Some "source.swift"
131 Removed: | "terraform" | "hcl" -> Some "source.hcl.terraform"
132 Removed: | "toml" | "ini" -> Some "source.toml"
133 Removed: | "typescript" -> Some "source.ts"
134 Removed: | "vue" -> Some "text.html.vue"
135 Removed: | "xml" -> Some "text.html.basic"
136 Removed: | "yaml" -> Some "source.yaml"
137 Removed: | "zig" -> Some "source.zig"
138 Removed: | _ -> None
93 Added: (** Map from the canonical language name (as returned by {!Syntax.detect}) to
94 Added: the scopeName used by the grammar file. *)
95 Added: let scope_of_lang name = Hashtbl.find_opt scope_table name