Files
codebase-memory-mcp/src/discover/language.c
T
Andrew Hundt 92f3b86f7a merge(upstream/main): integrate DeusData main into api-consolidation
Deep --no-ff merge of upstream DeusData/main (597 commits, merge-base 988d975)
into fork api-consolidation (HEAD 3018b22), producing a superset that combines
upstream breadth with the fork's surviving depth.

Upstream additions integrated: semantic/embedding search (vendored/nomic),
minhash/SimHash, cross-repo + cross-file LSP + route + pkgmap passes, artifact
import/export, path_alias, git_context, hook_augment, Windows support
(compat_fs/compat_thread), the UI layer, and ~140 expanded tree-sitter grammars.

Fork depth retained: PageRank/LinkRank ranking (src/pagerank/; sort_by=
relevance/linkrank/degree/calls), dependency indexing (src/depindex/;
CBM_INDEX_MODE=DEP), token reduction, and HTTP-endpoint discovery
(src/pipeline/httplink.c, pass_httplinks.c).

Conflict resolution (27 regions across the 39-file intersection):
- src/mcp/mcp.c: kept the fork superset handler pattern (resolve_project_store +
  expand_project_param + inject_context_once) as the single path; UNIONed the
  tool dispatch table; emit status:"indexed", excluded[], cross_service trace.
- src/store/store.c: upstream base + fork pagerank/linkrank/node_degree tables
  and sort_by plumbing; cbm_store_node_degree now excludes INHERITS
  (WHERE type != 'INHERITS'); removed the duplicate cbm_store_get_db.
- src/pipeline/pipeline.c: took upstream run_extraction_phase; grafted the fork
  passes (httplinks, normalize, flush_store dump branch); call
  cbm_pipeline_set_committed_counts before cbm_gbuf_dump_to_sqlite.
- internal/cbm/lang_specs.c: 158 entries combining the fork section_node_types
  with upstream ts_factory/embedded_imports; wired 13 orphaned type-arrays.
- src/depindex/depindex.h: renamed CBM_PATH_MAX -> CBM_DEP_PATH_MAX (it collided
  with constants.h enum CBM_PATH_MAX=1024) + 11 call sites in depindex.c.
- Makefile.cbm: upstream grammar/nomic units + fork CGO targets; added
  httplink.c + pass_httplinks.c to PIPELINE_SRCS; removed mongoose NOSAN
  machinery (mongoose was deleted upstream).
- httplinks restored (the merge silently deleted them because the fork had left
  them unchanged since the base): httplink.c/.h, pass_httplinks.c,
  tests/test_httplink.c; registered RUN_SUITE(httplink) in tests/test_main.c.
- CBM_INDEX_MODE enum unified to FULL=0, MODERATE=1, FAST=2, DEP=3.

Completeness (dual-parent check): 0 upstream files dropped from the merge tree;
6 fork files dropped, all intentional (upstream-superseded): CHANGELOG.md,
vendored/mimalloc/src/{arena-abandon,segment-map,segment}.c,
vendored/mongoose/{mongoose.c,mongoose.h}. Fork features (pagerank, depindex,
token_reduction, httplink) verified present and registered in tests/test_main.c.
src/pipeline/pass_parallel.c has an identical function set to upstream (49=49,
none dropped); the large line-deltas vs upstream are within-function divergence
of the fork extractor, not dropped functions.

Gate (run manually; hooks skip on merge commits): build green
(make -f Makefile.cbm cbm, exit 0); full C test suite 5980 passed / 2 failed /
0 crashes. The fork's pre-existing ASan crash in build_project_list_error_srv is
fixed by upstream's mcp.c rewrite, so the merge is strictly better than the fork
baseline on the crash axis.

Known issue, tracked separately (NOT a merge defect): 2 nondeterministic
failures in tests/test_incremental.c — incr_db_deleted_recovery:772 (diff_pct)
and incr_accuracy_vs_full:803 (full_nodes vs incr_nodes). Cause: the fork's
extractor (internal/cbm/extract_*.c) and parallel-resolve Route-node creation
are not thread-safe under the concurrent use of upstream's parallel pipeline
(run_parallel_pipeline), producing nondeterministic route/infra HANDLES edges
(~234k edge swing between runs). Sequential extraction (CBM_WORKERS=1) is
deterministic and correct. Root cause, bisect, and repro are documented in
notes/2026-06-24-fork-vs-upstream-divergence-assessment.md (section 8f); the fix
is deferred to a dedicated thread-safety audit (the make test-tsan target is
currently a stub and needs wiring).
2026-06-24 16:12:00 -04:00

1028 lines
26 KiB
C

/*
* language.c — Language detection from filename and extension.
*
* Maps file extensions and special filenames to CBMLanguage enum values.
* Handles .m disambiguation (Objective-C vs Magma vs MATLAB).
* Consults the process-global user config (set via cbm_set_user_lang_config)
* before the built-in tables, allowing custom extension→language mappings.
*/
#include "discover/discover.h"
#include "discover/userconfig.h"
#include "cbm.h" // CBMLanguage, CBM_LANG_*
#include "foundation/constants.h"
enum { LANG_SCAN_PASSES = 2 };
#define SLEN(s) (sizeof(s) - 1)
#include <ctype.h>
#include <stdio.h>
#include <string.h>
/* ── Extension → Language lookup table ───────────────────────────── */
typedef struct {
const char *ext; /* including dot, e.g. ".go" */
CBMLanguage language;
} ext_entry_t;
/* Sorted by extension for binary search (but linear scan is fine for ~120 entries) */
static const ext_entry_t EXT_TABLE[] = {
/* Bash */
{".bash", CBM_LANG_BASH},
{".sh", CBM_LANG_BASH},
/* C */
{".c", CBM_LANG_C},
/* C++ */
{".cc", CBM_LANG_CPP},
{".ccm", CBM_LANG_CPP},
{".cpp", CBM_LANG_CPP},
{".cppm", CBM_LANG_CPP},
{".cxx", CBM_LANG_CPP},
{".h", CBM_LANG_CPP},
{".hh", CBM_LANG_CPP},
{".hpp", CBM_LANG_CPP},
{".hxx", CBM_LANG_CPP},
{".ixx", CBM_LANG_CPP},
/* C# */
{".cs", CBM_LANG_CSHARP},
/* Clojure */
{".clj", CBM_LANG_CLOJURE},
{".cljc", CBM_LANG_CLOJURE},
{".cljs", CBM_LANG_CLOJURE},
/* CMake */
{".cmake", CBM_LANG_CMAKE},
/* COBOL */
{".cbl", CBM_LANG_COBOL},
{".cob", CBM_LANG_COBOL},
/* Common Lisp */
{".cl", CBM_LANG_COMMONLISP},
{".lisp", CBM_LANG_COMMONLISP},
{".lsp", CBM_LANG_COMMONLISP},
/* CSS */
{".css", CBM_LANG_CSS},
/* CUDA */
{".cu", CBM_LANG_CUDA},
{".cuh", CBM_LANG_CUDA},
/* Dart */
{".dart", CBM_LANG_DART},
/* Dockerfile */
{".dockerfile", CBM_LANG_DOCKERFILE},
/* Elixir */
{".ex", CBM_LANG_ELIXIR},
{".exs", CBM_LANG_ELIXIR},
/* DotEnv */
{".env", CBM_LANG_DOTENV},
/* Elm */
{".elm", CBM_LANG_ELM},
/* Emacs Lisp */
{".el", CBM_LANG_EMACSLISP},
/* Erlang */
{".erl", CBM_LANG_ERLANG},
/* F# */
{".fs", CBM_LANG_FSHARP},
{".fsi", CBM_LANG_FSHARP},
{".fsx", CBM_LANG_FSHARP},
/* FORM */
{".frm", CBM_LANG_FORM},
{".prc", CBM_LANG_FORM},
/* Fortran */
{".f03", CBM_LANG_FORTRAN},
{".f08", CBM_LANG_FORTRAN},
{".f90", CBM_LANG_FORTRAN},
{".f95", CBM_LANG_FORTRAN},
/* GLSL */
{".frag", CBM_LANG_GLSL},
{".glsl", CBM_LANG_GLSL},
{".vert", CBM_LANG_GLSL},
/* Go */
{".go", CBM_LANG_GO},
/* GraphQL */
{".gql", CBM_LANG_GRAPHQL},
{".graphql", CBM_LANG_GRAPHQL},
/* Groovy */
{".gradle", CBM_LANG_GROOVY},
{".groovy", CBM_LANG_GROOVY},
/* Haskell */
{".hs", CBM_LANG_HASKELL},
/* HCL / Terraform */
{".hcl", CBM_LANG_HCL},
{".tf", CBM_LANG_HCL},
/* HTML */
{".htm", CBM_LANG_HTML},
{".html", CBM_LANG_HTML},
/* INI */
{".cfg", CBM_LANG_INI},
{".conf", CBM_LANG_INI},
{".ini", CBM_LANG_INI},
/* Java */
{".java", CBM_LANG_JAVA},
/* JavaScript */
{".js", CBM_LANG_JAVASCRIPT},
{".jsx", CBM_LANG_JAVASCRIPT},
{".mjs", CBM_LANG_JAVASCRIPT}, /* ES modules (#197) */
{".cjs", CBM_LANG_JAVASCRIPT}, /* CommonJS modules */
/* JSON */
{".json", CBM_LANG_JSON},
/* Julia */
{".jl", CBM_LANG_JULIA},
/* Kotlin */
{".kt", CBM_LANG_KOTLIN},
{".kts", CBM_LANG_KOTLIN},
/* Lean */
{".lean", CBM_LANG_LEAN},
/* Lua */
{".lua", CBM_LANG_LUA},
/* Magma */
{".mag", CBM_LANG_MAGMA},
{".magma", CBM_LANG_MAGMA},
/* Makefile */
{".mk", CBM_LANG_MAKEFILE},
/* Markdown */
{".md", CBM_LANG_MARKDOWN},
{".mdx", CBM_LANG_MARKDOWN},
/* MATLAB */
{".m", CBM_LANG_MATLAB},
{".matlab", CBM_LANG_MATLAB},
{".mlx", CBM_LANG_MATLAB},
/* Meson */
{".meson", CBM_LANG_MESON},
/* Nix */
{".nix", CBM_LANG_NIX},
/* OCaml */
{".ml", CBM_LANG_OCAML},
{".mli", CBM_LANG_OCAML},
/* Perl */
{".pl", CBM_LANG_PERL},
{".pm", CBM_LANG_PERL},
/* PHP */
{".php", CBM_LANG_PHP},
/* Protobuf */
{".proto", CBM_LANG_PROTOBUF},
/* Python */
{".py", CBM_LANG_PYTHON},
/* R — case insensitive handled separately */
{".R", CBM_LANG_R},
{".r", CBM_LANG_R},
/* Ruby */
{".gemspec", CBM_LANG_RUBY},
{".rake", CBM_LANG_RUBY},
{".rb", CBM_LANG_RUBY},
/* Rust */
{".rs", CBM_LANG_RUST},
/* Scala */
{".sc", CBM_LANG_SCALA},
{".scala", CBM_LANG_SCALA},
/* SCSS */
{".scss", CBM_LANG_SCSS},
/* SQL */
{".sql", CBM_LANG_SQL},
/* Svelte */
{".svelte", CBM_LANG_SVELTE},
/* Swift */
{".swift", CBM_LANG_SWIFT},
/* SystemVerilog + Verilog */
{".sv", CBM_LANG_VERILOG},
{".v", CBM_LANG_VERILOG},
/* TOML */
{".toml", CBM_LANG_TOML},
/* TSX */
{".tsx", CBM_LANG_TSX},
/* TypeScript */
{".ts", CBM_LANG_TYPESCRIPT},
{".mts", CBM_LANG_TYPESCRIPT}, /* TS ES modules */
{".cts", CBM_LANG_TYPESCRIPT}, /* TS CommonJS modules */
/* VimScript */
{".vim", CBM_LANG_VIMSCRIPT},
{".vimrc", CBM_LANG_VIMSCRIPT},
{"justfile", CBM_LANG_JUST},
{"Justfile", CBM_LANG_JUST},
{".justfile", CBM_LANG_JUST},
{".just", CBM_LANG_JUST}, /* `import 'common.just'` target files */
{"hyprland.conf", CBM_LANG_HYPRLANG},
{"ssh_config", CBM_LANG_SSHCONFIG},
{"sshd_config", CBM_LANG_SSHCONFIG},
{"BUILD", CBM_LANG_STARLARK},
{"BUILD.bazel", CBM_LANG_STARLARK},
{"WORKSPACE", CBM_LANG_STARLARK},
{"WORKSPACE.bazel", CBM_LANG_STARLARK},
/* BitBake include fragments — `require/include foo.inc` target files. */
{".inc", CBM_LANG_BITBAKE},
/* Vue */
{".vue", CBM_LANG_VUE},
/* Wolfram */
{".wl", CBM_LANG_WOLFRAM},
{".wls", CBM_LANG_WOLFRAM},
/* XML */
{".xml", CBM_LANG_XML},
{".xsd", CBM_LANG_XML},
{".xsl", CBM_LANG_XML},
{".svg", CBM_LANG_XML},
/* YAML */
{".yaml", CBM_LANG_YAML},
{".yml", CBM_LANG_YAML},
/* Ada */
{".adb", CBM_LANG_ADA},
/* Ada */
{".ads", CBM_LANG_ADA},
/* Agda */
{".agda", CBM_LANG_AGDA},
/* Astro */
{".astro", CBM_LANG_ASTRO},
/* AWK */
{".awk", CBM_LANG_AWK},
/* BitBake */
{".bb", CBM_LANG_BITBAKE},
/* BitBake */
{".bbappend", CBM_LANG_BITBAKE},
/* BitBake */
{".bbclass", CBM_LANG_BITBAKE},
/* Beancount */
{".beancount", CBM_LANG_BEANCOUNT},
/* BibTeX */
{".bib", CBM_LANG_BIBTEX},
/* Bicep */
{".bicep", CBM_LANG_BICEP},
/* Blade */
/* .blade.php handled by userconfig compound extensions, not EXT_TABLE */
/* Starlark */
{".bzl", CBM_LANG_STARLARK},
/* Cairo */
{".cairo", CBM_LANG_CAIRO},
/* Cap'n Proto */
{".capnp", CBM_LANG_CAPNP},
/* Apex */
{".cls", CBM_LANG_APEX},
/* Crystal */
{".cr", CBM_LANG_CRYSTAL},
/* CSV */
{".csv", CBM_LANG_CSV},
/* D */
{".d", CBM_LANG_DLANG},
/* Diff */
{".diff", CBM_LANG_DIFF},
/* Pascal */
{".dpr", CBM_LANG_PASCAL},
/* DeviceTree */
{".dts", CBM_LANG_DEVICETREE},
/* DeviceTree */
{".dtsi", CBM_LANG_DEVICETREE},
/* FunC */
{".fc", CBM_LANG_FUNC},
/* Fish */
{".fish", CBM_LANG_FISH},
/* Fennel */
{".fnl", CBM_LANG_FENNEL},
/* HLSL */
{".fx", CBM_LANG_HLSL},
/* GDScript */
{".gd", CBM_LANG_GDSCRIPT},
/* Gleam */
{".gleam", CBM_LANG_GLEAM},
/* GN */
{".gn", CBM_LANG_GN},
/* GN */
{".gni", CBM_LANG_GN},
/* Go Template */
{".gotmpl", CBM_LANG_GOTEMPLATE},
{".tpl", CBM_LANG_GOTEMPLATE}, /* Helm _helpers.tpl named-template definitions */
/* Hare */
{".ha", CBM_LANG_HARE},
/* Hyprlang */
{".hl", CBM_LANG_HYPRLANG},
/* HLSL */
{".hlsl", CBM_LANG_HLSL},
/* HLSL */
{".hlsli", CBM_LANG_HLSL},
/* ISPC */
{".ispc", CBM_LANG_ISPC},
/* Jinja2 */
{".j2", CBM_LANG_JINJA2},
/* Janet */
{".janet", CBM_LANG_JANET},
/* Jinja2 */
{".jinja", CBM_LANG_JINJA2},
/* Jinja2 */
{".jinja2", CBM_LANG_JINJA2},
/* JSON5 */
{".json5", CBM_LANG_JSON5},
/* Jsonnet */
{".jsonnet", CBM_LANG_JSONNET},
/* KDL */
{".kdl", CBM_LANG_KDL},
/* Linker Script */
{".ld", CBM_LANG_LINKERSCRIPT},
/* Linker Script */
{".lds", CBM_LANG_LINKERSCRIPT},
/* Jsonnet */
{".libsonnet", CBM_LANG_JSONNET},
/* Liquid */
{".liquid", CBM_LANG_LIQUID},
/* LLVM IR */
{".ll", CBM_LANG_LLVM_IR},
/* Pascal */
{".lpr", CBM_LANG_PASCAL},
/* Luau */
{".luau", CBM_LANG_LUAU},
/* Qt QML */
{".qml", CBM_LANG_QML},
/* CFML / ColdFusion — .cfc components are script-dialect; .cfm are tag templates */
{".cfc", CBM_LANG_CFSCRIPT},
{".cfm", CBM_LANG_CFML},
/* Mermaid */
{".mermaid", CBM_LANG_MERMAID},
/* Mermaid */
{".mmd", CBM_LANG_MERMAID},
/* Move */
{".move", CBM_LANG_MOVE},
/* NASM */
{".nasm", CBM_LANG_NASM},
/* Nickel */
{".ncl", CBM_LANG_NICKEL},
/* Nim */
/* Nim */
/* Squirrel */
{".nut", CBM_LANG_SQUIRREL},
/* Odin */
{".odin", CBM_LANG_ODIN},
/* DeviceTree */
{".overlay", CBM_LANG_DEVICETREE},
/* Pascal */
{".pas", CBM_LANG_PASCAL},
/* Diff */
{".patch", CBM_LANG_DIFF},
/* Pine Script */
{".pine", CBM_LANG_PINE},
/* Pkl */
{".pkl", CBM_LANG_PKL},
/* PO */
{".po", CBM_LANG_PO},
/* Pony */
{".pony", CBM_LANG_PONY},
/* PO */
{".pot", CBM_LANG_PO},
/* Puppet */
{".pp", CBM_LANG_PUPPET},
/* Prisma */
{".prisma", CBM_LANG_PRISMA},
/* Properties */
{".properties", CBM_LANG_PROPERTIES},
/* PowerShell */
{".ps1", CBM_LANG_POWERSHELL},
/* PowerShell */
{".psd1", CBM_LANG_POWERSHELL},
/* PowerShell */
{".psm1", CBM_LANG_POWERSHELL},
/* PureScript */
{".purs", CBM_LANG_PURESCRIPT},
/* ReScript */
{".res", CBM_LANG_RESCRIPT},
/* ReScript */
{".resi", CBM_LANG_RESCRIPT},
/* Regex */
{".re", CBM_LANG_REGEX},
/* Racket */
{".rkt", CBM_LANG_RACKET},
/* RON */
{".ron", CBM_LANG_RON},
/* reStructuredText */
{".rst", CBM_LANG_RST},
/* Assembly */
{".s", CBM_LANG_ASSEMBLY},
/* Assembly */
{".S", CBM_LANG_ASSEMBLY},
/* Scheme */
{".scm", CBM_LANG_SCHEME},
/* Slang */
{".slang", CBM_LANG_SLANG},
/* Smali */
{".smali", CBM_LANG_SMALI},
/* Smithy */
{".smithy", CBM_LANG_SMITHY},
/* Solidity */
{".sol", CBM_LANG_SOLIDITY},
/* SOQL */
{".soql", CBM_LANG_SOQL},
/* SOSL */
{".sosl", CBM_LANG_SOSL},
/* Scheme */
{".ss", CBM_LANG_SCHEME},
/* Starlark */
{".star", CBM_LANG_STARLARK},
/* SystemVerilog */
/* SystemVerilog */
/* Sway */
{".sw", CBM_LANG_SWAY},
/* Tcl */
{".tcl", CBM_LANG_TCL},
/* TableGen */
{".td", CBM_LANG_TABLEGEN},
/* Templ */
{".templ", CBM_LANG_TEMPL},
/* Thrift */
{".thrift", CBM_LANG_THRIFT},
/* Teal */
{".tl", CBM_LANG_TEAL},
/* TLA+ */
{".tla", CBM_LANG_TLAPLUS},
/* Go Template */
{".tmpl", CBM_LANG_GOTEMPLATE},
/* Apex */
{".trigger", CBM_LANG_APEX},
/* Typst */
{".typ", CBM_LANG_TYPST},
/* VHDL */
{".vhd", CBM_LANG_VHDL},
/* VHDL */
{".vhdl", CBM_LANG_VHDL},
/* WGSL */
{".wgsl", CBM_LANG_WGSL},
/* WIT */
{".wit", CBM_LANG_WIT},
/* Zsh */
{".zsh", CBM_LANG_ZSH},
/* Zig */
{".zig", CBM_LANG_ZIG},
};
#define EXT_TABLE_SIZE (sizeof(EXT_TABLE) / sizeof(EXT_TABLE[0]))
/* ── Special filename → Language lookup ──────────────────────────── */
typedef struct {
const char *filename;
CBMLanguage language;
} filename_entry_t;
static const filename_entry_t FILENAME_TABLE[] = {
{"CMakeLists.txt", CBM_LANG_CMAKE},
{"Dockerfile", CBM_LANG_DOCKERFILE},
{"GNUmakefile", CBM_LANG_MAKEFILE},
{"Makefile", CBM_LANG_MAKEFILE},
{"makefile", CBM_LANG_MAKEFILE},
{"meson.build", CBM_LANG_MESON},
{"meson.options", CBM_LANG_MESON},
{"meson_options.txt", CBM_LANG_MESON},
{"kustomization.yaml", CBM_LANG_KUSTOMIZE},
{"kustomization.yml", CBM_LANG_KUSTOMIZE},
/* Note: FILENAME_TABLE uses case-sensitive strcmp, so mixed-case variants
* (e.g. "Kustomization.yaml") are not matched here. They fall through to
* CBM_LANG_YAML and are re-classified by cbm_is_kustomize_file() in
* pass_k8s.c, which performs a case-insensitive comparison. This is the
* intended behaviour — no additional entries are needed. */
{".vimrc", CBM_LANG_VIMSCRIPT},
{".zshrc", CBM_LANG_ZSH},
{".zshenv", CBM_LANG_ZSH},
{".zprofile", CBM_LANG_ZSH},
{"justfile", CBM_LANG_JUST},
{"Justfile", CBM_LANG_JUST},
{".justfile", CBM_LANG_JUST},
{"hyprland.conf", CBM_LANG_HYPRLANG},
{"ssh_config", CBM_LANG_SSHCONFIG},
{"sshd_config", CBM_LANG_SSHCONFIG},
{".ssh/config", CBM_LANG_SSHCONFIG},
{"BUILD", CBM_LANG_STARLARK},
{"BUILD.bazel", CBM_LANG_STARLARK},
{"WORKSPACE", CBM_LANG_STARLARK},
{"WORKSPACE.bazel", CBM_LANG_STARLARK},
{"requirements.txt", CBM_LANG_REQUIREMENTS},
{"requirements-dev.txt", CBM_LANG_REQUIREMENTS},
{"requirements-test.txt", CBM_LANG_REQUIREMENTS},
{"Kconfig", CBM_LANG_KCONFIG},
{"go.mod", CBM_LANG_GOMOD},
{".env", CBM_LANG_DOTENV},
{".env.local", CBM_LANG_DOTENV},
{".gitattributes", CBM_LANG_GITATTRIBUTES},
};
#define FILENAME_TABLE_SIZE (sizeof(FILENAME_TABLE) / sizeof(FILENAME_TABLE[0]))
/* ── Language names ──────────────────────────────────────────────── */
static const char *LANG_NAMES[CBM_LANG_COUNT] = {
[CBM_LANG_GO] = "Go",
[CBM_LANG_PYTHON] = "Python",
[CBM_LANG_JAVASCRIPT] = "JavaScript",
[CBM_LANG_TYPESCRIPT] = "TypeScript",
[CBM_LANG_TSX] = "TSX",
[CBM_LANG_RUST] = "Rust",
[CBM_LANG_JAVA] = "Java",
[CBM_LANG_CPP] = "C++",
[CBM_LANG_CSHARP] = "C#",
[CBM_LANG_PHP] = "PHP",
[CBM_LANG_LUA] = "Lua",
[CBM_LANG_SCALA] = "Scala",
[CBM_LANG_KOTLIN] = "Kotlin",
[CBM_LANG_RUBY] = "Ruby",
[CBM_LANG_C] = "C",
[CBM_LANG_BASH] = "Bash",
[CBM_LANG_ZIG] = "Zig",
[CBM_LANG_ELIXIR] = "Elixir",
[CBM_LANG_HASKELL] = "Haskell",
[CBM_LANG_OCAML] = "OCaml",
[CBM_LANG_OBJC] = "Objective-C",
[CBM_LANG_SWIFT] = "Swift",
[CBM_LANG_DART] = "Dart",
[CBM_LANG_PERL] = "Perl",
[CBM_LANG_GROOVY] = "Groovy",
[CBM_LANG_ERLANG] = "Erlang",
[CBM_LANG_R] = "R",
[CBM_LANG_HTML] = "HTML",
[CBM_LANG_CSS] = "CSS",
[CBM_LANG_SCSS] = "SCSS",
[CBM_LANG_YAML] = "YAML",
[CBM_LANG_TOML] = "TOML",
[CBM_LANG_HCL] = "HCL",
[CBM_LANG_SQL] = "SQL",
[CBM_LANG_DOCKERFILE] = "Dockerfile",
[CBM_LANG_CLOJURE] = "Clojure",
[CBM_LANG_FSHARP] = "F#",
[CBM_LANG_JULIA] = "Julia",
[CBM_LANG_VIMSCRIPT] = "VimScript",
[CBM_LANG_NIX] = "Nix",
[CBM_LANG_COMMONLISP] = "Common Lisp",
[CBM_LANG_ELM] = "Elm",
[CBM_LANG_FORTRAN] = "Fortran",
[CBM_LANG_CUDA] = "CUDA",
[CBM_LANG_COBOL] = "COBOL",
[CBM_LANG_VERILOG] = "Verilog",
[CBM_LANG_EMACSLISP] = "Emacs Lisp",
[CBM_LANG_JSON] = "JSON",
[CBM_LANG_XML] = "XML",
[CBM_LANG_MARKDOWN] = "Markdown",
[CBM_LANG_MAKEFILE] = "Makefile",
[CBM_LANG_CMAKE] = "CMake",
[CBM_LANG_PROTOBUF] = "Protobuf",
[CBM_LANG_GRAPHQL] = "GraphQL",
[CBM_LANG_VUE] = "Vue",
[CBM_LANG_SVELTE] = "Svelte",
[CBM_LANG_MESON] = "Meson",
[CBM_LANG_GLSL] = "GLSL",
[CBM_LANG_INI] = "INI",
[CBM_LANG_MATLAB] = "MATLAB",
[CBM_LANG_LEAN] = "Lean",
[CBM_LANG_FORM] = "FORM",
[CBM_LANG_MAGMA] = "Magma",
[CBM_LANG_WOLFRAM] = "Wolfram",
[CBM_LANG_KUSTOMIZE] = "Kustomize",
[CBM_LANG_K8S] = "Kubernetes",
[CBM_LANG_PINE] = "PineScript",
[CBM_LANG_SOLIDITY] = "Solidity",
[CBM_LANG_TYPST] = "Typst",
[CBM_LANG_GDSCRIPT] = "GDScript",
[CBM_LANG_GLEAM] = "Gleam",
[CBM_LANG_POWERSHELL] = "PowerShell",
[CBM_LANG_PASCAL] = "Pascal",
[CBM_LANG_DLANG] = "D",
[CBM_LANG_NIM] = "Nim",
[CBM_LANG_SCHEME] = "Scheme",
[CBM_LANG_FENNEL] = "Fennel",
[CBM_LANG_FISH] = "Fish",
[CBM_LANG_AWK] = "AWK",
[CBM_LANG_ZSH] = "Zsh",
[CBM_LANG_TCL] = "Tcl",
[CBM_LANG_ADA] = "Ada",
[CBM_LANG_AGDA] = "Agda",
[CBM_LANG_RACKET] = "Racket",
[CBM_LANG_ODIN] = "Odin",
[CBM_LANG_RESCRIPT] = "ReScript",
[CBM_LANG_PURESCRIPT] = "PureScript",
[CBM_LANG_NICKEL] = "Nickel",
[CBM_LANG_CRYSTAL] = "Crystal",
[CBM_LANG_TEAL] = "Teal",
[CBM_LANG_HARE] = "Hare",
[CBM_LANG_PONY] = "Pony",
[CBM_LANG_LUAU] = "Luau",
[CBM_LANG_QML] = "QML",
[CBM_LANG_CFSCRIPT] = "CFML",
[CBM_LANG_CFML] = "CFML",
[CBM_LANG_JANET] = "Janet",
[CBM_LANG_SWAY] = "Sway",
[CBM_LANG_NASM] = "NASM",
[CBM_LANG_ASSEMBLY] = "Assembly",
[CBM_LANG_ASTRO] = "Astro",
[CBM_LANG_BLADE] = "Blade",
[CBM_LANG_JUST] = "Just",
[CBM_LANG_GOTEMPLATE] = "Go Template",
[CBM_LANG_TEMPL] = "Templ",
[CBM_LANG_LIQUID] = "Liquid",
[CBM_LANG_JINJA2] = "Jinja2",
[CBM_LANG_PRISMA] = "Prisma",
[CBM_LANG_HYPRLANG] = "Hyprlang",
[CBM_LANG_DOTENV] = "DotEnv",
[CBM_LANG_SYSTEMVERILOG] = "SystemVerilog",
[CBM_LANG_DIFF] = "Diff",
[CBM_LANG_WGSL] = "WGSL",
[CBM_LANG_KDL] = "KDL",
[CBM_LANG_JSON5] = "JSON5",
[CBM_LANG_JSONNET] = "Jsonnet",
[CBM_LANG_RON] = "RON",
[CBM_LANG_THRIFT] = "Thrift",
[CBM_LANG_CAPNP] = "Cap'n Proto",
[CBM_LANG_PROPERTIES] = "Properties",
[CBM_LANG_SSHCONFIG] = "SSH Config",
[CBM_LANG_BIBTEX] = "BibTeX",
[CBM_LANG_STARLARK] = "Starlark",
[CBM_LANG_BICEP] = "Bicep",
[CBM_LANG_CSV] = "CSV",
[CBM_LANG_REQUIREMENTS] = "Requirements",
[CBM_LANG_HLSL] = "HLSL",
[CBM_LANG_VHDL] = "VHDL",
[CBM_LANG_DEVICETREE] = "DeviceTree",
[CBM_LANG_LINKERSCRIPT] = "Linker Script",
[CBM_LANG_GN] = "GN",
[CBM_LANG_KCONFIG] = "Kconfig",
[CBM_LANG_BITBAKE] = "BitBake",
[CBM_LANG_SMALI] = "Smali",
[CBM_LANG_TABLEGEN] = "TableGen",
[CBM_LANG_ISPC] = "ISPC",
[CBM_LANG_CAIRO] = "Cairo",
[CBM_LANG_MOVE] = "Move",
[CBM_LANG_SQUIRREL] = "Squirrel",
[CBM_LANG_FUNC] = "FunC",
[CBM_LANG_REGEX] = "Regex",
[CBM_LANG_JSDOC] = "JSDoc",
[CBM_LANG_RST] = "reStructuredText",
[CBM_LANG_BEANCOUNT] = "Beancount",
[CBM_LANG_MERMAID] = "Mermaid",
[CBM_LANG_PUPPET] = "Puppet",
[CBM_LANG_PO] = "PO",
[CBM_LANG_GITATTRIBUTES] = "gitattributes",
[CBM_LANG_GITIGNORE] = "gitignore",
[CBM_LANG_SLANG] = "Slang",
[CBM_LANG_LLVM_IR] = "LLVM IR",
[CBM_LANG_SMITHY] = "Smithy",
[CBM_LANG_WIT] = "WIT",
[CBM_LANG_TLAPLUS] = "TLA+",
[CBM_LANG_PKL] = "Pkl",
[CBM_LANG_GOMOD] = "Go Mod",
[CBM_LANG_APEX] = "Apex",
[CBM_LANG_SOQL] = "SOQL",
[CBM_LANG_SOSL] = "SOSL",
};
/* ── Public API ──────────────────────────────────────────────────── */
CBMLanguage cbm_language_for_extension(const char *ext) {
if (!ext || !ext[0]) {
return CBM_LANG_COUNT;
}
/* Check user-defined overrides first */
const cbm_userconfig_t *ucfg = cbm_get_user_lang_config();
if (ucfg) {
CBMLanguage ulang = cbm_userconfig_lookup(ucfg, ext);
if (ulang != CBM_LANG_COUNT) {
return ulang;
}
}
for (size_t i = 0; i < EXT_TABLE_SIZE; i++) {
if (strcmp(EXT_TABLE[i].ext, ext) == 0) {
return EXT_TABLE[i].language;
}
}
return CBM_LANG_COUNT;
}
CBMLanguage cbm_language_for_filename(const char *filename) {
if (!filename || !filename[0]) {
return CBM_LANG_COUNT;
}
/* Check special filenames first */
for (size_t i = 0; i < FILENAME_TABLE_SIZE; i++) {
if (strcmp(FILENAME_TABLE[i].filename, filename) == 0) {
return FILENAME_TABLE[i].language;
}
}
/* DotEnv variant filenames (".env.local", ".env.production", …): the
* filename starts with ".env." but its last "extension" (e.g. ".local")
* is not a real language extension. Match the dotenv convention used by
* pass_envscan/pass_infrascan (".env" exact, ".env." prefix, "*.env"
* suffix) so file-index routing agrees with direct extraction. */
if (strncmp(filename, ".env.", SLEN(".env.")) == 0) {
return CBM_LANG_DOTENV;
}
/* Fall back to extension-based lookup.
* For compound extensions (e.g. ".blade.php") defined in the user config,
* scan from the first dot in the basename toward the last, checking user
* config at each position. Built-in extensions use the last dot only. */
const char *last_dot = strrchr(filename, '.');
if (!last_dot) {
return CBM_LANG_COUNT;
}
/* Probe compound extensions (e.g. ".blade.php") from the first dot toward
* the last. Built-in compounds are checked first so e.g. Laravel Blade
* templates map to Blade rather than the single-extension fallback (PHP);
* user config can still add more (#258). */
static const struct {
const char *ext;
CBMLanguage lang;
} COMPOUND_EXT_TABLE[] = {
{".blade.php", CBM_LANG_BLADE},
};
const cbm_userconfig_t *ucfg = cbm_get_user_lang_config();
const char *p = strchr(filename, '.');
while (p && p < last_dot) {
for (size_t i = 0; i < sizeof(COMPOUND_EXT_TABLE) / sizeof(COMPOUND_EXT_TABLE[0]); i++) {
if (strcmp(p, COMPOUND_EXT_TABLE[i].ext) == 0) {
return COMPOUND_EXT_TABLE[i].lang;
}
}
if (ucfg) {
CBMLanguage lang = cbm_userconfig_lookup(ucfg, p);
if (lang != CBM_LANG_COUNT) {
return lang;
}
}
p = strchr(p + SKIP_ONE, '.');
}
/* Standard single-extension lookup (built-ins + user overrides). */
return cbm_language_for_extension(last_dot);
}
const char *cbm_language_name(CBMLanguage lang) {
if (lang < 0 || lang >= CBM_LANG_COUNT) {
return "Unknown";
}
return LANG_NAMES[lang] ? LANG_NAMES[lang] : "Unknown";
}
/* ── .m file disambiguation ──────────────────────────────────────── */
/* Simple substring search helper */
static bool str_contains(const char *haystack, const char *needle) {
return strstr(haystack, needle) != NULL;
}
static bool has_objc_markers(const char *buf) {
return str_contains(buf, "@interface") || str_contains(buf, "@implementation") ||
str_contains(buf, "@protocol") || str_contains(buf, "@property") ||
str_contains(buf, "#import") || str_contains(buf, "@selector") ||
str_contains(buf, "@encode") || str_contains(buf, "@synthesize") ||
str_contains(buf, "@dynamic");
}
static bool has_magma_end_markers(const char *buf) {
return str_contains(buf, "end function;") || str_contains(buf, "end procedure;") ||
str_contains(buf, "end intrinsic;") || str_contains(buf, "end if;") ||
str_contains(buf, "end for;") || str_contains(buf, "end while;");
}
/* Check for "intrinsic Name(" or "procedure Name(" patterns. */
static bool has_magma_callable_pattern(const char *buf) {
const char *markers[] = {"intrinsic ", "procedure "};
for (int i = 0; i < LANG_SCAN_PASSES; i++) {
const char *p = strstr(buf, markers[i]);
if (!p) {
continue;
}
p += strlen(markers[i]);
while (*p && isalpha((unsigned char)*p)) {
p++;
}
if (*p == '(') {
return true;
}
}
return false;
}
/* Scan lines for MATLAB-specific markers (function/classdef/%%). */
static bool has_matlab_line_markers(const char *buf) {
const char *line = buf;
while (*line) {
const char *p = line;
while (*p == ' ' || *p == '\t') {
p++;
}
if (strncmp(p, "function ", SLEN("function ")) == 0 ||
strncmp(p, "function\t", SLEN("function\t")) == 0 ||
strncmp(p, "classdef ", SLEN("classdef ")) == 0 ||
strncmp(p, "classdef\t", SLEN("classdef\t")) == 0 || strncmp(p, "%%", PAIR_LEN) == 0 ||
(*p == '%' && *(p + SKIP_ONE) != '{')) {
return true;
}
const char *nl = strchr(line, '\n');
if (!nl) {
break;
}
line = nl + SKIP_ONE;
}
return false;
}
CBMLanguage cbm_disambiguate_m(const char *path) {
if (!path) {
return CBM_LANG_MATLAB;
}
FILE *f = fopen(path, "r");
if (!f) {
return CBM_LANG_MATLAB;
}
/* Read first 4KB */
char buf[CBM_SZ_4K + SKIP_ONE];
size_t n = fread(buf, SKIP_ONE, CBM_SZ_4K, f);
buf[n] = '\0';
(void)fclose(f);
if (has_objc_markers(buf)) {
return CBM_LANG_OBJC;
}
if (has_magma_end_markers(buf)) {
return CBM_LANG_MAGMA;
}
if ((str_contains(buf, "intrinsic ") || str_contains(buf, "procedure ")) &&
has_magma_callable_pattern(buf)) {
return CBM_LANG_MAGMA;
}
if (has_matlab_line_markers(buf)) {
return CBM_LANG_MATLAB;
}
return CBM_LANG_MATLAB;
}