What problem does this solve?
Two gaps stack up, and the second is invisible until the first is worked
around.
1. .razor has no entry in EXT_TABLE (src/discover/language.c), so
discovery skips Blazor component files entirely. Indexing a Blazor
application produces no graph nodes for any component. The only way in is an
undocumented extra_extensions entry in a per-project
.codebase-memory.json, which I found by reading src/discover/userconfig.c
rather than from any documentation.
2. Even with the files forced in, @page yields nothing. Blazor's route
directive lives in markup above the @code block:
@page "/counter"
@inject NavigationManager Nav
<h1>Counter</h1>
@code {
private int currentCount = 0;
}
Tree-sitter's C# grammar recovers @code but never parses the directive, so
no Route node is created. On a real Blazor application here — 36 .razor
files with 11 @page directives — get_architecture(routes) returns the
aspect absent, not empty: the entry surface of the whole application is
missing from the graph, while Route is a first-class label with a BM25
boost of +8 and a dedicated routes aspect.
This is not #1041. That issue is ASP.NET Core attribute routing —
[HttpGet]/[Route] on controller methods, parsed C# attributes on a
Method node. Blazor's is a markup directive on a file whose class is
implicit, so there is no Method and no Class to hang a route on, and no
AST node containing the directive at all. Fixing #1041 would not produce a
single Blazor route. They are neighbours, not duplicates. (I checked all 778
issues; #1162 is closed as a duplicate of #1041, and nothing mentions Blazor
or .razor.)
Proposed solution
Three small changes, no new grammar and no new dependency:
-
src/discover/language.c — map .razor to CBM_LANG_CSHARP. This is
best-effort by design and worth saying plainly: the C# grammar recovers the
@code block, while the surrounding markup lands in ERROR regions and the
file is reported through parse_partial. That is still strictly more than
the Module-and-imports currently extracted for the other markup-hosted
languages, and it makes the files reachable without a per-project config
key.
-
internal/cbm/extract_defs.c — scan the raw source for @page and put
the result on the file's module def as route_path, with
route_method = "GET". The module QN already is the component's identity,
and insert_def_into_gbuf is label-agnostic, so Route + HANDLES follow
from the existing machinery. The match is deliberately strict — directive
first on its line, then whitespace, then a double-quoted path starting
/ — so @pageSize and prose mentions cannot match.
-
src/pipeline/pass_route_nodes.c — add "Module" to
ensure_decorator_routes. Extraction alone covers a full index; this
backstop is what runs on an incremental re-index, so without it a
component's Route appears on a full index and disappears the next time
that one file changes.
Test beds. Two public repos, both verified as I write this:
dotnet/eShop — 40 .razor files;
src/WebApp/Components/Pages/Catalog/Catalog.razor opens with
@page "/". A realistic application rather than a sample.
dotnet/blazor-samples — 3,058
.razor files, the official sample set, good for breadth.
MudBlazor/MudBlazor (1,996) and
radzenhq/radzen-blazor
(1,476) are component libraries — heavy markup, useful as stress cases for
the parse-recovery behaviour rather than for routes.
What I would not claim for it. @code methods are not reliably
extracted from .razor: real markup (class=, role= attributes) defeats
recovery and the file comes back parse_partial as a whole. Bare @code
fields are a separate gap. Two further gaps reproduce on the unmodified
binary and are out of scope here: routes report handler:"" although the
HANDLES edge exists, and the language census counts .razor as C# rather
than as its own surface. [Parameter], @inject and component composition
are all still invisible; @inject looks like the best next one, and
composition the worst economics, since most tags point at library components
with no definition in the repo.
Alternatives considered
Vendor a tree-sitter Razor grammar and register CBM_LANG_RAZOR. The
honest option, and it would fix @code recovery too. It is a new vendored
dependency — explicitly on CONTRIBUTING's "requires maintainer approval"
list — for a language whose C# half already extracts well. I did not think it
was mine to propose first.
CBMEmbeddedLangSpec (internal/cbm/lang_specs.c:863), the mechanism
Vue, Svelte, HTML and Astro use: outer grammar, named node slice re-parsed
with an inner grammar. Blazor is the same shape — markup outside, C# in
@code. It needs an outer grammar that can parse Razor markup, so it reduces
to the option above.
Leave it to extra_extensions. The status quo. It gets .razor files
indexed, but it is per-project, undocumented, and does nothing at all for
@page — which is the part that is categorically missing rather than merely
thin.
Do nothing. Defensible on volume: on the application I measured, .razor
is ~12% of files and the C# 88% already indexes fully. I think routes are
worth it anyway because the loss is categorical, not proportional — zero
routes for an entire application, in a tool that treats routes as a
first-class concept.
Confirmations
Disclosure, since CONTRIBUTING asks for the issue first: I have already
built and measured this — it is three files plus tests, ~190 added lines, on
a branch, full suite green with lint clean. I am not attaching it as a PR
because CONTRIBUTING says to open an issue and wait for feedback, and because
"new indexing algorithms" is on the approval-required list. Say the word and
I will open it against this issue; say no and that is a fine answer too.
Investigated with Claude Code; the counts and the parse behaviour above are
from real runs on a real application, not generated.
What problem does this solve?
Two gaps stack up, and the second is invisible until the first is worked
around.
1.
.razorhas no entry inEXT_TABLE(src/discover/language.c), sodiscovery skips Blazor component files entirely. Indexing a Blazor
application produces no graph nodes for any component. The only way in is an
undocumented
extra_extensionsentry in a per-project.codebase-memory.json, which I found by readingsrc/discover/userconfig.crather than from any documentation.
2. Even with the files forced in,
@pageyields nothing. Blazor's routedirective lives in markup above the
@codeblock:Tree-sitter's C# grammar recovers
@codebut never parses the directive, sono
Routenode is created. On a real Blazor application here — 36.razorfiles with 11
@pagedirectives —get_architecture(routes)returns theaspect absent, not empty: the entry surface of the whole application is
missing from the graph, while
Routeis a first-class label with a BM25boost of +8 and a dedicated
routesaspect.This is not #1041. That issue is ASP.NET Core attribute routing —
[HttpGet]/[Route]on controller methods, parsed C# attributes on aMethodnode. Blazor's is a markup directive on a file whose class isimplicit, so there is no
Methodand noClassto hang a route on, and noAST node containing the directive at all. Fixing #1041 would not produce a
single Blazor route. They are neighbours, not duplicates. (I checked all 778
issues; #1162 is closed as a duplicate of #1041, and nothing mentions Blazor
or
.razor.)Proposed solution
Three small changes, no new grammar and no new dependency:
src/discover/language.c— map.razortoCBM_LANG_CSHARP. This isbest-effort by design and worth saying plainly: the C# grammar recovers the
@codeblock, while the surrounding markup lands in ERROR regions and thefile is reported through
parse_partial. That is still strictly more thanthe Module-and-imports currently extracted for the other markup-hosted
languages, and it makes the files reachable without a per-project config
key.
internal/cbm/extract_defs.c— scan the raw source for@pageand putthe result on the file's module def as
route_path, withroute_method = "GET". The module QN already is the component's identity,and
insert_def_into_gbufis label-agnostic, soRoute+HANDLESfollowfrom the existing machinery. The match is deliberately strict — directive
first on its line, then whitespace, then a double-quoted path starting
/— so@pageSizeand prose mentions cannot match.src/pipeline/pass_route_nodes.c— add"Module"toensure_decorator_routes. Extraction alone covers a full index; thisbackstop is what runs on an incremental re-index, so without it a
component's
Routeappears on a full index and disappears the next timethat one file changes.
Test beds. Two public repos, both verified as I write this:
dotnet/eShop— 40.razorfiles;src/WebApp/Components/Pages/Catalog/Catalog.razoropens with@page "/". A realistic application rather than a sample.dotnet/blazor-samples— 3,058.razorfiles, the official sample set, good for breadth.MudBlazor/MudBlazor(1,996) andradzenhq/radzen-blazor(1,476) are component libraries — heavy markup, useful as stress cases for
the parse-recovery behaviour rather than for routes.
What I would not claim for it.
@codemethods are not reliablyextracted from
.razor: real markup (class=,role=attributes) defeatsrecovery and the file comes back
parse_partialas a whole. Bare@codefields are a separate gap. Two further gaps reproduce on the unmodified
binary and are out of scope here: routes report
handler:""although theHANDLESedge exists, and the language census counts.razoras C# ratherthan as its own surface.
[Parameter],@injectand component compositionare all still invisible;
@injectlooks like the best next one, andcomposition the worst economics, since most tags point at library components
with no definition in the repo.
Alternatives considered
Vendor a tree-sitter Razor grammar and register
CBM_LANG_RAZOR. Thehonest option, and it would fix
@coderecovery too. It is a new vendoreddependency — explicitly on CONTRIBUTING's "requires maintainer approval"
list — for a language whose C# half already extracts well. I did not think it
was mine to propose first.
CBMEmbeddedLangSpec(internal/cbm/lang_specs.c:863), the mechanismVue, Svelte, HTML and Astro use: outer grammar, named node slice re-parsed
with an inner grammar. Blazor is the same shape — markup outside, C# in
@code. It needs an outer grammar that can parse Razor markup, so it reducesto the option above.
Leave it to
extra_extensions. The status quo. It gets.razorfilesindexed, but it is per-project, undocumented, and does nothing at all for
@page— which is the part that is categorically missing rather than merelythin.
Do nothing. Defensible on volume: on the application I measured,
.razoris ~12% of files and the C# 88% already indexes fully. I think routes are
worth it anyway because the loss is categorical, not proportional — zero
routes for an entire application, in a tool that treats routes as a
first-class concept.
Confirmations
Disclosure, since CONTRIBUTING asks for the issue first: I have already
built and measured this — it is three files plus tests, ~190 added lines, on
a branch, full suite green with lint clean. I am not attaching it as a PR
because CONTRIBUTING says to open an issue and wait for feedback, and because
"new indexing algorithms" is on the approval-required list. Say the word and
I will open it against this issue; say no and that is a fine answer too.
Investigated with Claude Code; the counts and the parse behaviour above are
from real runs on a real application, not generated.