Analyzer::Crystal::CrystalEngine
Inherits FileScanEngine < Analyzer < FileHelper < Reference < Object
Constants
Precompiled block-structure probes used by extract_crystal_do_block /
extract_crystal_def_block on every body line. Constant shapes — load
once, match many times. Cheap includes? gates reject the common
non-structural body lines before any PCRE2 scan.
Opening token of a Crystal heredoc: <<-DELIM, optionally quoted
(<<-'SQL'). The delimiter must start with an uppercase letter or
underscore — the universal Crystal convention (MD/HTML/SQL/EOF/…) —
which keeps the matcher from firing on arr << -value. The negative
lookbehind rejects a <<- that sits inside a string literal
("<<-MD"), so a stray quoted token can't open a phantom heredoc and
blank the rest of the file.
Crystal "…" strings interpolate #{expr}. The Kemal/Lucky/
Amber/Marten/Grip route extractors capture the literal
characters between quotes, so get "/api/#{VERSION}/items"
came out as /api/#{VERSION}/items with the #{VERSION}
leaking into the URL. Rewrite it as {name} so the path-
parameter extractor picks it up and the URL template reads
cleanly. Mirrors the Python f-string, Ruby #{}, and PHP
$var fixes earlier this pass.
Most route literals have no interpolation; skip the PCRE2
gsub when the #\{ marker is absent.
Track the enclosing module/class by indentation rather than by
counting do/end. Method bodies in real controllers are full of
heredocs, {% … %} macros, and XML.build do … end blocks that
throw off keyword-based depth counting and prematurely "close" the
module, dropping every method defined after them. Indentation is the
one signal those constructs can't corrupt: a namespace stays open
until a line dedents to or past the column where it was declared.
Precompiled: this loop runs once per line of every .cr file when
building the cross-file action index (--include-callee /
--ai-context). The shapes never change, so hoist the two PCRE2
patterns to class constants.
Group 1 = verb (or ws), group 2 = path. ws is folded in so a
single scan covers WebSocket routes too; the caller maps ws →
GET + protocol=ws.