Skip to content

feat(lang): CFML tag-dialect .cfc routing + embedded <cfscript> defs - #1412

Open
allanoepping wants to merge 1 commit into
DeusData:mainfrom
allanoepping:feat/cfml-tag-dialect-and-embedded-cfscript
Open

feat(lang): CFML tag-dialect .cfc routing + embedded <cfscript> defs#1412
allanoepping wants to merge 1 commit into
DeusData:mainfrom
allanoepping:feat/cfml-tag-dialect-and-embedded-cfscript

Conversation

@allanoepping

Copy link
Copy Markdown

Summary

Two extraction fixes for legacy tag-based CFML / ColdFusion. The vendored
cfmleditor/tree-sitter-cfml grammar is Lucee-tuned and script-first, so on
older codebases (predominantly <cfcomponent> tag components with <cfscript>
bodies) most functions were never extracted. Both fixes mirror existing
conventions in the codebase.

1. .cfc dialect disambiguation — cbm_disambiguate_cfc()

The extension table maps .cfc → cfscript unconditionally, but a .cfc may be
script-dialect (component { … }) or tag-dialect (<cfcomponent> … </cfcomponent>).
New content-sniff (same shape as cbm_disambiguate_cls/inc):

  • <cfcomponent> or a bare top-level <cffunction>CBM_LANG_CFML (tag grammar)
  • a leading <cfscript>-wrapped component {}, or a plain script component → CBM_LANG_CFSCRIPT
  • leading <!--- ---> comments are skipped before deciding

2. Embedded <cfscript> definitions — cbm_extract_embedded_defs()

The HTML-derived cfml grammar keeps <cfscript> bodies as an opaque
cf_script_content token, so script functions inside tag components produced no
function_declaration nodes. This re-parses each cf_script_tag → cf_script_content
slice with the cfscript grammar (reusing the embedded_imports machinery) and
walks it for definitions via a new cbm_extract_definitions_body() that skips the
Module node. Block-relative line numbers are shifted by the block's start row —
CBMDefinition carries only line positions, so a constant offset is a complete
remap. Definitions only (calls/usages would need byte remapping), so existing
edges cannot be corrupted. Bonus: functions are recovered even when a <cfsetting>
void-tag ERROR cascade damages the surrounding tree.

Adds an embedded_defs field to CBMLangSpec, wired only to CBM_LANG_CFML.

Tests

  • extract_cfml_embedded_cfscript_defs — script funcs inside <cfscript> in a tag
    component extract with correct (remapped) line numbers, alongside a <cffunction>.
  • 8 × lang_cfc_* — tag / script / <cfscript>-wrapped / bare <cffunction> /
    leading-comment / read-fail routing.

scripts/test.sh --suites "language extraction grammar_regression" → 497 passed, 0 failed.

Real-world impact

Indexing a ~700-file legacy CFML app (156/176 .cfc are tag-based):

  • parse timeouts 4 → 0
  • api.cfc 0 → 62 functions, api-a.cfc 0 → 48
  • +1,532 function nodes overall, with exact line numbers

Notes

Not addressed here: <cfsetting> (and other void CF tags) still emit tree-sitter
ERROR nodes in the cfml grammar — cosmetic now that functions are recovered, but
a genuine grammar issue that belongs in cfmleditor/tree-sitter-cfml (regenerating
parser.c from grammar.js).

@allanoepping
allanoepping requested a review from DeusData as a code owner August 2, 2026 06:31
@github-actions

github-actions Bot commented Aug 2, 2026

Copy link
Copy Markdown

Thanks for opening this — it has been seen, and it is queued.

This note is automated, but it is not a brush-off: it exists so you know where your PR stands instead of having to guess from silence.

Current review status: working through a backlog. 0.9.1-rc.1 is out, so the release freeze that held reviews is over — but it left a large queue of open pull requests behind it, and we are reading through them oldest-first. The background is in discussion #1144.

What that means for this PR, concretely:

  • It will not be closed for inactivity. No stale bot touches pull requests here.
  • It may still sit a while before a human reads it. That is on us, not on you.
  • Older PRs are read first, so a recent one is not being skipped — it is behind a queue.

Things that will genuinely speed it up whenever review does happen:

  • Keep it rebased on main — the tree is moving quickly right now, and a conflicting branch cannot be reviewed as the diff you intended.
  • Get CI green, or say which failures you believe are pre-existing.
  • Keep the change to one claim. Bundled features and refactors get split before they get merged, which costs you a round trip.
  • Every commit needs a sign-off (git commit -s) — CI enforces DCO.

If this fixes a bug, a reproduction we can run is worth more than a description of the symptom.

Thanks for contributing, and sorry in advance for the wait.

Two extraction fixes for legacy tag-based CFML/ColdFusion, where the
vendored cfmleditor grammar (Lucee-tuned, script-first) leaves most
real-world components unextracted.

1. .cfc dialect disambiguation (cbm_disambiguate_cfc)
   The extension table maps .cfc -> cfscript unconditionally, but legacy
   codebases are predominantly tag-based (<cfcomponent>). Content-sniff the
   file head, mirroring cbm_disambiguate_cls/inc: <cfcomponent>/<cffunction>
   route to the cfml tag grammar; a leading <cfscript>-wrapped "component {}"
   or a plain script component route to cfscript; leading <!--- ---> comments
   are skipped.

2. Embedded <cfscript> definition extraction (cbm_extract_embedded_defs)
   The HTML-derived cfml grammar keeps <cfscript> bodies as opaque
   cf_script_content, so script functions inside tag components were never
   extracted. Re-parse cf_script_tag -> cf_script_content slices with the
   cfscript grammar (reusing the embedded-imports machinery) and walk them
   for definitions via a new cbm_extract_definitions_body() that skips the
   Module node. Block-relative line numbers are shifted by the block's start
   row; CBMDefinition carries only line positions, so a constant offset is a
   complete remap. Definitions only (calls/usages would need byte remapping),
   so existing edges cannot be corrupted. As a bonus, functions are recovered
   even when a <cfsetting> void-tag ERROR cascade damages the surrounding tree.

The opt-in is an extract_definitions flag on CBMEmbeddedLangSpec (carried by
CFML's embedded_imports entry), not a new CBMLangSpec field -- the latter
would trip -Wmissing-field-initializers across every language row under
clang -Werror.

Tests: extract_cfml_embedded_cfscript_defs (extraction + line remap) and
eight lang_cfc_* disambiguation cases. Verified on a ~700-file legacy CFML
codebase: parse timeouts 4->0, api.cfc 0->62 functions, +1,532 function
nodes overall, with exact line numbers.

Signed-off-by: Allan Oepping <allan@intellipay.com>
@allanoepping
allanoepping force-pushed the feat/cfml-tag-dialect-and-embedded-cfscript branch from 0ff692d to aeac05c Compare August 2, 2026 16:48
@allanoepping

allanoepping commented Aug 2, 2026

Copy link
Copy Markdown
Author

The Windows red check (test-windows … 2/2, plus the downstream ci-ok / shard-completeness aggregates) is unrelated to this change — it fails in the Step-0 static infrastructure contracts, not in any test suite or in code this PR touches (CFML .cfc disambiguation + embedded <cfscript> definition extraction, all in the C extraction path).

Two Windows-runner environmental failures surface there:

  • Step 0a — path-safety.sh: ERROR: repository build/ ancestor must be absent or a real directory. build/ is gitignored, so a clean checkout has it absent; this fires only when the runner leaves build/ as a symlink/non-dir (a cache/filesystem artifact on the Windows VM).
  • Step 0i — parallel suite scheduler contract: a file-lock race during cleanup —
    rm: cannot remove '…/cbm-parallel-harness.…/logs/timeout_exit_race.log': Device or resource busy
    ValueError: invalid literal for int() with base 10: ''
    
    i.e. the scheduler's own timeout/hang fixtures (hang_after_summary, stubborn_tree, timeout_exit_race) left a handle open so the rm failed and empty output tripped an int('') parse.

Everything relevant is green, including the full suite under clang-TSAN on Linux + macOS (which exercises the new lang_cfc_* and extract_cfml_embedded_cfscript_defs tests): DCO, lint, all test-unix gcc shards, all test-tsan (ubuntu/arm/macOS clang), test-unix (macOS), and test-windows-guards. A re-run of the Windows shard should clear it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants