{"record":{"id":"f8abae869a8ab6d4","repo":"zed-industries/zed","slug":"grammar-name-grammar-name-must-be-written-in-s","errorCode":null,"errorMessage":"grammar name '{grammar_name}' must be written in snake_case: {snake_cased_grammar_name}","messagePattern":"grammar name '(.+?)' must be written in snake_case: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/extension/src/extension_builder.rs","lineNumber":147,"sourceCode":"                    log::info!(\"compiling Rust extension {extension_dir:?}\");\n                    self.compile_rust_extension(extension_dir, extension_manifest, tx, &options)\n                        .await\n                        .context(\"compiling Rust extension\")?;\n\n                    log::info!(\"compiled Rust extension {extension_dir:?}\");\n                    Ok(())\n                }\n                .boxed()\n            });\n\n        let grammar_compilation_tasks = extension_manifest\n            .grammars\n            .iter()\n            .zip(clang_path.into_iter().flatten())\n            .map(|((grammar_name, grammar_metadata), clang_path_task)| {\n                async move {\n                    let snake_cased_grammar_name = grammar_name.to_snake_case();\n                    anyhow::ensure!(\n                        grammar_name.as_ref() == snake_cased_grammar_name.as_str(),\n                        \"grammar name '{grammar_name}' must be \\\n                            written in snake_case: {snake_cased_grammar_name}\"\n                    );\n\n                    log::info!(\"compiling grammar {grammar_name} for extension {extension_dir:?}\");\n\n                    let clang_path = clang_path_task.await.context(\"resolving clang path\")?;\n\n                    self.compile_grammar(\n                        extension_dir,\n                        grammar_name.as_ref(),\n                        grammar_metadata,\n                        &clang_path,\n                    )\n                    .await\n                    .with_context(|| format!(\"compiling grammar '{grammar_name}'\"))?;\n                    log::info!(\"compiled grammar {grammar_name} for extension {extension_dir:?}\");","sourceCodeStart":129,"sourceCodeEnd":165,"githubUrl":"https://github.com/zed-industries/zed/blob/9d272b036335401f339d024ea94968fd51016c40/crates/extension/src/extension_builder.rs#L129-L165","documentation":"While compiling extension grammars, the builder converts each grammar name from extension.toml's [grammars] table with to_snake_case() and bails if the original name differs. Grammar keys must already be snake_case (lowercase words joined by underscores) because the key is used verbatim for the tree-sitter symbol export (tree_sitter_{grammar_name}) and cache directory naming.","triggerScenarios":"An extension.toml [grammars] entry whose key is not snake_case: hyphenated repo names like \"tree-sitter-my-lang\", CamelCase like \"MyLang\", or dotted/kebab names. The message tells you the expected form: {snake_cased_grammar_name}.","commonSituations":"Authors copying the GitHub repo name (tree-sitter-foo) or the grammar's CamelCase C symbol (TreeSitterFoo) as the manifest key instead of the plain snake_case grammar name Zed expects (e.g. \"foo\" or \"tree_sitter_foo\").","solutions":["Rename the [grammars] key in extension.toml to the snake_cased name shown in the error message","Use the bare grammar name (usually the language name, e.g. 'ruby', 'markdown-inline') that matches the tree-sitter grammar's declared name","Re-run 'zed extension build' to confirm the manifest parses and the key round-trips through to_snake_case unchanged"],"exampleFix":"# before\n[grammars]\n\"tree-sitter-my-lang\" = { git = \"https://github.com/me/tree-sitter-my-lang\", rev = \"...\" }\n\n# after\n[grammars]\nmy_lang = { git = \"https://github.com/me/tree-sitter-my-lang\", rev = \"...\" }","handlingStrategy":"validation","validationCode":"// Lint manifest grammar keys before building\nfn grammar_key_is_snake_case(key: &str) -> bool {\n    key == convert_case? // no crate? use a to_snake_case equivalent, e.g.:\n}\n\n// simplest: reject anything but [a-z0-9_]\nfn grammar_key_is_snake_case(key: &str) -> bool {\n    key.chars().all(|c| c.is_ascii_lowercase() || c.is_ascii_digit() || c == '_')\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Use bare snake_case language names as [grammars] keys from the first commit","Add a manifest lint to CI that rejects hyphens and capitals in grammar keys","When in doubt, check the name shown in the error's expected snake_cased form"],"tags":["zed","extension","grammar","tree-sitter","naming","manifest"],"backgroundTag":"identifier-naming-convention","analyzedSha":"9d272b036335401f339d024ea94968fd51016c40","analyzedAt":"2026-09-12T19:35:53.743Z","contentChangedAt":"2026-09-12T19:35:53.743Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}