Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 21 additions & 10 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -439,7 +439,7 @@ Omit `--install` to print to stdout. Full per-shell install paths and notes:

## What CodeGraph Does (and Doesn't)

**Does:** deterministic code-structure extraction across 37 languages (TypeScript,
**Does:** deterministic code-structure extraction across 38 languages (TypeScript,
Python, Go, Rust, Java, C/C++, C#, Vue, Svelte, GDScript, and more — see
[`docs/languages.md`](docs/languages.md)), cross-file resolution (including
Godot project graphs), graph traversal, FTS5 search, whole-graph export with
Expand All @@ -454,9 +454,9 @@ beyond the fixed `LANGUAGES` set.

## Supported Languages

CodeGraph supports **37 languages** grouped by extraction depth. Quick overview:
CodeGraph supports **38 languages** grouped by extraction depth. Quick overview:

- **Tier 1 — Full symbol extraction (28):** TypeScript, TSX, JavaScript, JSX, ArkTS, Python, Go, Rust, Java, C, C++, C#, PHP, Ruby, Swift, Kotlin, Dart, Scala, Lua, Luau, Objective-C, R, Solidity, Nix, Terraform, Erlang, GDScript, Pascal.
- **Tier 1 — Full symbol extraction (29):** TypeScript, TSX, JavaScript, JSX, ArkTS, Python, Go, Rust, Java, C, C++, C#, PHP, Ruby, Swift, Kotlin, Dart, Scala, Lua, Luau, Objective-C, R, Solidity, Nix, Terraform, Erlang, CFML, GDScript, Pascal.
- **Tier 2 — Embedded / template extraction (6):** Vue, Svelte, Astro, Razor/`.cshtml`, Liquid, XML/MyBatis mapper.
- **Tier 3 — File-level only (3):** YAML, Twig, Properties.

Expand Down
5 changes: 5 additions & 0 deletions crates/codegraph-bench/fixtures/cfml/Base.cfc
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
component {
public function ping() {
return true;
}
}
6 changes: 6 additions & 0 deletions crates/codegraph-bench/fixtures/cfml/Gadget.cfs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
component extends="Base" {
property name="x";
public function doThing() {
helper();
}
}
5 changes: 5 additions & 0 deletions crates/codegraph-bench/fixtures/cfml/Widget.cfm
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<cfcomponent name="Widget" extends="Base">
<cffunction name="doThing" access="public" returntype="void">
<cfset x = 1>
</cffunction>
</cfcomponent>
33 changes: 33 additions & 0 deletions crates/codegraph-bench/tests/equivalence.rs
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,31 @@ fn erlang_db_is_self_equivalent_to_erlang_golden() {
assert_equivalent(&erlang_db(), &erlang_golden_dir()).unwrap();
}

#[test]
fn generated_golden_matches_committed_cfml_fixture() {
// Guards CFML extraction (upstream #1153, scope-B extraction slice): the
// `.cfc`/`.cfm`/`.cfs`->Cfml mapping, the dual-grammar dialect switch
// (`is_bare_script_cfml`: cfscript for bare-script, cfml tag grammar for
// tag files), a bare-script `component`->Class named from the FILE (unnamed
// in the grammar) + script-style `extends`->Extends, a tag
// `<cfcomponent>`->Class from `name` attr + `<cffunction>`->Method with
// access/returntype + tag `extends`->Extends. Both extends resolve to
// `Base.cfc`; `helper()` stays unresolved. The `<cfscript>`-in-tag delegation
// + cfquery SQL-body extraction + framework resolvers are DEFERRED.
let tempdir = TestDir::new("generated-golden-cfml");
write_golden(&cfml_db(), tempdir.path()).unwrap();

let expected = load_golden(&cfml_golden_dir()).unwrap();
let actual = load_golden(tempdir.path()).unwrap();

diff_canonical(&expected, &actual, None).unwrap();
}

#[test]
fn cfml_db_is_self_equivalent_to_cfml_golden() {
assert_equivalent(&cfml_db(), &cfml_golden_dir()).unwrap();
}

#[test]
fn tier1_node_drift_is_reported() {
let expected = load_golden(&mini_golden_dir()).unwrap();
Expand Down Expand Up @@ -364,6 +389,14 @@ fn erlang_golden_dir() -> PathBuf {
workspace_root().join("reference/golden/erlang")
}

fn cfml_db() -> PathBuf {
workspace_root().join("reference/golden/cfml/colby.db")
}

fn cfml_golden_dir() -> PathBuf {
workspace_root().join("reference/golden/cfml")
}

struct TestDir {
path: PathBuf,
}
Expand Down
9 changes: 7 additions & 2 deletions crates/codegraph-core/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ pub const EDGE_KIND_STRINGS: [&str; 12] = [
"decorates",
];

pub const LANGUAGE_STRINGS: [&str; 41] = [
pub const LANGUAGE_STRINGS: [&str; 42] = [
"typescript",
"javascript",
"tsx",
Expand Down Expand Up @@ -82,6 +82,7 @@ pub const LANGUAGE_STRINGS: [&str; 41] = [
"nix",
"terraform",
"erlang",
"cfml",
"yaml",
"twig",
"xml",
Expand Down Expand Up @@ -335,6 +336,8 @@ pub enum Language {
Terraform,
#[serde(rename = "erlang")]
Erlang,
#[serde(rename = "cfml")]
Cfml,
#[serde(rename = "yaml")]
Yaml,
#[serde(rename = "twig")]
Expand All @@ -356,7 +359,7 @@ pub enum Language {
}

impl Language {
pub const ALL: [Self; 41] = [
pub const ALL: [Self; 42] = [
Self::TypeScript,
Self::JavaScript,
Self::Tsx,
Expand Down Expand Up @@ -389,6 +392,7 @@ impl Language {
Self::Nix,
Self::Terraform,
Self::Erlang,
Self::Cfml,
Self::Yaml,
Self::Twig,
Self::Xml,
Expand Down Expand Up @@ -434,6 +438,7 @@ impl Language {
Self::Nix => "nix",
Self::Terraform => "terraform",
Self::Erlang => "erlang",
Self::Cfml => "cfml",
Self::Yaml => "yaml",
Self::Twig => "twig",
Self::Xml => "xml",
Expand Down
1 change: 1 addition & 0 deletions crates/codegraph-extract/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ tracing = { workspace = true }
tree-sitter-arkts = "0.2.0"
tree-sitter-c = "0.24.2"
tree-sitter-c-sharp = "0.23.5"
tree-sitter-cfml = "0.26.30"
tree-sitter-cpp = "0.23.4"
tree-sitter-css = "0.25.0"
tree-sitter-dart = "0.2.0"
Expand Down
21 changes: 18 additions & 3 deletions crates/codegraph-extract/src/engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ pub fn builtin_language_for_ext(ext: &str) -> Option<Language> {
"nix" => Language::Nix,
"tf" | "tfvars" | "tofu" => Language::Terraform,
"erl" | "hrl" => Language::Erlang,
"cfc" | "cfm" | "cfs" => Language::Cfml,
"yml" | "yaml" => Language::Yaml,
"twig" => Language::Twig,
"xml" => Language::Xml,
Expand Down Expand Up @@ -203,7 +204,15 @@ pub fn extract_source(
};

let mut parser = Parser::new();
let ts_language = spec.tree_sitter_language();
// Run `pre_parse` first so the grammar selection can see the (possibly
// rewritten) source. Only CFML overrides `tree_sitter_language_for_source`
// to pick between its cfscript / cfml tag grammars per file dialect; every
// other spec inherits the default (`tree_sitter_language`), so this is a
// behavior-neutral reorder (their `pre_parse` is the identity default, and
// the two that override it — C++/embedded — don't override the grammar
// hook, so their parse path is byte-identical).
let parsed_source = spec.pre_parse(source, file_path);
let ts_language = spec.tree_sitter_language_for_source(&parsed_source);
if let Err(error) = parser.set_language(&ts_language) {
return ExtractionResult {
nodes: Vec::new(),
Expand All @@ -213,7 +222,6 @@ pub fn extract_source(
duration_ms: start.elapsed().as_millis() as i64,
};
}
let parsed_source = spec.pre_parse(source, file_path);
let Some(tree) = parser.parse(&parsed_source, None) else {
return ExtractionResult {
nodes: Vec::new(),
Expand Down Expand Up @@ -874,7 +882,7 @@ mod tests {
assert_eq!(detect_language("s.metal"), Language::Cpp);
assert_eq!(detect_language("k.cu"), Language::Cpp);
assert_eq!(detect_language("k.cuh"), Language::Cpp);
assert_eq!(Language::ALL.len(), 41);
assert_eq!(Language::ALL.len(), 42);
}

#[test]
Expand Down Expand Up @@ -905,6 +913,13 @@ mod tests {
assert_eq!(detect_language("defs.hrl"), Language::Erlang);
}

#[test]
fn cfml_extensions_map_to_cfml() {
assert_eq!(detect_language("Widget.cfc"), Language::Cfml);
assert_eq!(detect_language("page.cfm"), Language::Cfml);
assert_eq!(detect_language("Gadget.cfs"), Language::Cfml);
}

#[test]
fn plain_ts_stays_typescript() {
assert_eq!(detect_language("m.ts"), Language::TypeScript);
Expand Down
Loading
Loading