feat(extraction): add CFML language support (.cfc/.cfm/.cfs) (#1118) (#1153)

Tag-based and bare-script CFML, extends/implements, <cfscript>/<cfquery> delegation, BOM + unquoted-attribute handling. Wasm grammars verified bit-for-bit reproducible from cfmleditor/tree-sitter-cfml. Validated on FW/1, ColdBox, CFWheels. Follow-up: #1152.

Co-authored-by: ghedwards <125586+ghedwards@users.noreply.github.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Colby Mchenry
2026-07-02 18:25:53 -05:00
committed by GitHub
co-authored by ghedwards Claude Fable 5
parent cc89146454
commit 816bacb7f2
13 changed files with 952 additions and 3 deletions
+20 -1
View File
@@ -39,6 +39,9 @@ const WASM_GRAMMAR_FILES: Record<GrammarLanguage, string> = {
r: 'tree-sitter-r.wasm',
luau: 'tree-sitter-luau.wasm',
objc: 'tree-sitter-objc.wasm',
cfml: 'tree-sitter-cfml.wasm',
cfscript: 'tree-sitter-cfscript.wasm',
cfquery: 'tree-sitter-cfquery.wasm',
};
/**
@@ -108,6 +111,11 @@ export const EXTENSION_MAP: Record<string, Language> = {
'.luau': 'luau',
'.m': 'objc',
'.mm': 'objc',
// CFML: .cfc/.cfm parse with the tag-aware `cfml` grammar (custom CfmlExtractor
// dialect-switches to cfscript for bare-script content); .cfs is pure CFScript.
'.cfc': 'cfml',
'.cfm': 'cfml',
'.cfs': 'cfscript',
// Metal Shading Language ≈ C++14: the C++ grammar extracts its functions,
// structs, and calls. MSL-specific `[[attribute]]` annotations are blanked
// pre-parse for `.metal` files (see blankMetalAttributes in c-cpp.ts). (#1121)
@@ -203,6 +211,14 @@ export async function loadGrammarsForLanguages(languages: Language[]): Promise<v
languages = [...languages, 'typescript', 'javascript'];
}
// CFML (.cfc/.cfm) delegates bare-script content, <cfscript> tag bodies, and
// <cfquery> SQL bodies to the cfscript/cfquery grammars (see injections.scm in
// tree-sitter-cfml) — load both even when no standalone .cfs file is in the
// index set.
if (languages.some((l) => l === 'cfml')) {
languages = [...languages, 'cfscript', 'cfquery'];
}
// Deduplicate and filter to languages that have WASM grammars and aren't already loaded
const toLoad = [...new Set(languages)].filter(
(lang): lang is GrammarLanguage =>
@@ -225,7 +241,7 @@ export async function loadGrammarsForLanguages(languages: Language[]): Promise<v
// `class Foo(...)` as an ERROR that swallows the whole class (#237); we
// vendor the upstream ABI-15 tree-sitter-c-sharp 0.23.5 wasm, which parses
// primary constructors natively.
const wasmPath = (lang === 'pascal' || lang === 'scala' || lang === 'lua' || lang === 'luau' || lang === 'csharp' || lang === 'r')
const wasmPath = (lang === 'pascal' || lang === 'scala' || lang === 'lua' || lang === 'luau' || lang === 'csharp' || lang === 'r' || lang === 'cfml' || lang === 'cfscript' || lang === 'cfquery')
? path.join(__dirname, 'wasm', wasmFile)
: require.resolve(`tree-sitter-wasms/out/${wasmFile}`);
const language = await WasmLanguage.load(wasmPath);
@@ -440,6 +456,9 @@ export function getLanguageDisplayName(language: Language): string {
twig: 'Twig',
xml: 'XML',
properties: 'Java properties',
cfml: 'CFML',
cfscript: 'CFScript',
cfquery: 'CFQuery (SQL)',
unknown: 'Unknown',
};
return names[language] || language;