Repository navigation
Razor foundation: Blazor components, Razor Pages and MVC views in the host, the sidecar, VS Code and Zed - #398
Merged
Merged
Conversation
…e Razor through the SDK generator's projection Bundles the SDK's Razor compiler for markup facts, maps hover, definition, references, highlights, completion, signature help, rename and semantic tokens between .razor/.cshtml files and their generated C#. Refs #397
…ties out of the Razor fixtures The root .editorconfig raises every analyzer diagnostic to an error, and .editorconfig files cascade by folder, so the fixtures' empty Directory.Build.props did not stop CA1716 failing BlazorApp's build. A root .editorconfig in the fixture folder does. Refs #397
Every catalogued video gets a watch page in each language, a bare embed player, a 1200x630 social card, a video sitemap entry and complete VideoObject data, so Google can index the films and X, LinkedIn and Slack can play them in link previews. Every docs page gets its own description. [WEB-DESIGN-MEDIA-DISCOVERY]
…from the sidecar - LangId::Razor for .razor and .cshtml, with no tree-sitter grammar; the C# sidecar owns it - documentSymbol, foldingRange and linkedEditingRange for Razor go to the sidecar; selectionRange answers empty - < and @ completion triggers, answered null outside Razor documents - Razor diagnostics publish under the source sharplsp-razor - Sidecar outline, folds and linked tag names from the markup facts and the C# projection Refs #397
…host foundation
sharplsp/razor/htmlProjection forwards { textDocument } to the C# sidecar and answers
{ text }, or null for a file no project lists. The full-stack Razor session now checks
the projection keeps markup at its positions and blanks Razor.
Refs #397
…cts model as built, and structure as implemented - The capture passes run at three points, not once at the end of optimization - Markup facts get a typeDiagram model; they hold no directive tokens or C# fragment spans - Tag helper discovery covers every compilation reference, project references included - Document symbols, folds and linked editing are specified as they behave - The HTML projection restores component HTML comments from a second parse Refs #397
…nd-to-end chunk VS Code syncs razor and aspnetcorerazor documents, activates on onLanguage:razor, and forwards completion and hover in Razor markup to the built-in HTML language service over the server's HTML projection, following VS Code's request-forwarding pattern. A razor chunk runs the Razor suite over the real fixture solution on both platforms. Zed's dev extension attaches to the Razor language the community Razor extensions define. [RAZOR-EDITORS] [RAZOR-FEATURES-HTML]
…rkup facts Find, answer empty when there is nothing, else query, with any exception a failure: four copies of that skeleton become RunQueryAsync, clearing a Deslop cluster. Refs #397
…o the typeDiagram generates Refs #397
- The HTML projection blanks every non-HTML character of a Razor file, keeping offsets and newlines, from the compiler's IR; component HTML comments are restored from a second parse with component parsing off - A WHATWG-state tokenizer over the projection finds the markup context at a caret - The sidecar serves sharplsp/razor/htmlProjection, and the structure handlers are registered - End tag names tolerate whitespace before > and match plain elements case-insensitively Refs #397
Phase 1 closes with the editor verification of the HTML projection, and Phase 5 with VS Code sync, HTML forwarding, the razor end-to-end chunk and Zed's Razor registration, each checked against the test that proves it.
… coverage The session dropped its client, which kills the server before LLVM writes its profile: the Razor routing it exercised measured as uncovered. razor.rs goes from 71% to 99%. Refs #397
The HTML projection round-trips unsaved text and answers null for an unlisted file; the structure requests keep their wire shapes and Razor ranges; a malformed payload answers an error and leaves the socket healthy. Refs #397
…to the file The plan's foundation item promised the pending-edit replay for Razor files; nothing exercised it. The edit now waits for the solution, and both the markup facts and the C# projection read the edited text once it loads. Refs #397
Every C# rename, and every rename that starts in F#, searched the whole solution for references a second time to find Razor uses, even where no project lists a Razor file. Only a solution with Razor files pays for it now. [RAZOR-FEATURES-RENAME]
Mapping a location outside the workspace's documents, such as decompiled source, asked every project for its generated documents, so going to a framework definition compiled every project and ran its generators, Razor or not. Only Razor projects are searched now. Refs #397
…built it A watch page has no file of its own whose date means anything, so its datePublished and sitemap lastmod took the template's file date, which every fresh checkout moves. They now carry the video's upload date; every other page keeps its own. [WEB-DESIGN-MEDIA-DISCOVERY]
…sed documents - The projection provider joins the extension's subscriptions once the client has started, and is disposed when the start fails. - A closed Razor document's projection is dropped, so projections no longer pile up for the whole session. - The projection's address is built in paths.ts (embeddedUri), the extension's one path module [SHARPLSP-ARCHITECTURE-PATHS]. - A failed projection logs a structured event with the URI and no error text. Refs #397
… unreachable ones - The markup-context tokenizer dispatches on its state and the next character. Quoted values close in one place, and the raw-text and fallback arms that Advance never reaches are gone. - An unterminated comment never yields a standalone `<!--` token in the IR, so the end-of-file tail case in RazorHtmlComments is removed. - RazorIr.DirectiveName is tested directly: a directive name with no source has no span. - Seven malformed-HTML caret cases; the plan records 33 tokenizer cases, full line coverage of the HTML files, and the editor validation. Refs #397
… and a stopped client keep the server's answers An unlisted Razor file gets no HTML items or hover, its projection address reads as empty HTML, and a projection the server refuses is logged and answers none. The HTML completion command always resolves to a list, so its undefined fallback goes.
…le system without the fixture - AdhocRazorProject builds an in-memory project the way MSBuild hands one over: Razor files as additional documents, MSBuild properties and file metadata in a global analyzer config, and fixed generator output in place of the SDK's Razor generator. RazorLocationsTests now uses its generator types. - RazorProjectionTests (6 cases) runs hand-written generator output through every kind of line mapping: the checksum lookup; hidden, fileless, foreign and out-of-range mappings, which never map; carets at a fragment's end; and rename, which edits only listed Razor files whose span holds the old name. - RazorEngineSettingsTests (12 cases) covers the settings fallbacks, target-path metadata, the engine's file system and missing files, projects and documents without a path, cancellation, and an unreadable file that answers null without breaking later requests. - Code that no input can reach is gone. TypeOf reads a type name's arity from its rightmost part and has no dead arms. A projection's Razor path is always the one its checksum names. The span map's bound already keeps it inside the Razor text. Hover treats a missing semantic model as a failure. Refs #397
…mplates in Razor - linkedEditingRange and foldingRange fail and name themselves when the C# sidecar is missing, instead of answering that nothing folds or links. - Postfix templates never expand in a Razor file, where the same text can be markup. [RAZOR-FEATURES-COMPLETION] now says so. Refs #397
…d a bad end-tag opening The markup context at a caret now follows the WHATWG tokenizer in three malformed cases: - After `/` in a start tag, any character but `>` is reconsumed as the start of an attribute, so `<div /x=|` is an attribute value. - `<<` keeps the second `<` as a tag opening, so `<<div |` is an attribute name. - `</` followed by anything but a letter or `>` opens a bogus comment that ends at the next `>`. Three regression tests failed before the fix and pass unchanged after it. The tokenizer now has 36 cases and full line coverage. Refs #397
Both ran the same wrapper around a different DefinitionResolver call: resolve at the mapped position, then map the location back into the Razor file. RunLocationQueryAsync is that wrapper, next to the Razor location mapping, and WorkspaceManager.cs is 26 lines shorter. Refs #397
The full .NET run measures 96.39% with 671 C# tests, and every line the Razor branch adds is covered. Refs #397
MelbourneDeveloper
marked this pull request as ready for review
October 19, 2026 23:17
MelbourneDeveloper
enabled auto-merge (squash)
October 19, 2026 23:17
MelbourneDeveloper
disabled auto-merge
October 19, 2026 23:24
`_Imports.razor` and `_ViewImports.cshtml` hold usings for the files beside and below them, which their own projections never see. Roslyn reported every one of them as unnecessary (CS8019), and editors faded the whole file. MapDiagnostics now drops CS8019 and IDE0005 in import files. An unused using in a component is still reported there. NativePaths.IsRazorImport replaces the file system's private check, and [RAZOR-FEATURES-DIAGNOSTICS] now says this. Running the host over a fresh `dotnet new blazor` app found this. The plan records that validation and the debugger chunk's 80 passing cases. Refs #397
MelbourneDeveloper
enabled auto-merge (squash)
October 19, 2026 23:29
MelbourneDeveloper
disabled auto-merge
October 19, 2026 23:33
Roslyn judges a Razor file's usings from its projection alone, where they look unused for two reasons. The generated C# names every component in full, although the markup resolves `<Virtualize>` and the like through a using. And an import file's usings serve the files beside and below it. So a using marked unnecessary there can be one the markup needs. MapDiagnostics now drops CS8019 and IDE0005 in every Razor file. C# files, code-behind included, still report them. Unused-using analysis that understands Razor is future work. Refs #397
MelbourneDeveloper
enabled auto-merge (squash)
October 19, 2026 23:36
MelbourneDeveloper
disabled auto-merge
October 20, 2026 00:00
…ch copy A copy under the temp root has no global.json above it, so CI's newest installed SDK (10.0.401, past the 10.0.303 pin) built and loaded it, and that SDK's Razor generator needs a newer Roslyn than the sidecar bundles: no projection, and every Razor hover null.
…copy A copy under the temp root has no global.json above it, so CI's newest installed SDK (10.0.401, past the 10.0.303 pin) restored and loaded it. That SDK's Razor generator needs a newer Roslyn than the sidecar bundles, so there was no projection and every Razor hover was null. [RAZOR-ERRORS] now describes the limitation, and #399 tracks loading the generator with any SDK. Refs #397
RazorEngineSettings built a Razor project path from MSBuild's TargetPath by hand. NativePaths.ToRazorProjectPath now has a one-argument overload that both paths use, so every path string is formed in the path module [SHARPLSP-ARCHITECTURE-PATHS]. Behaviour is unchanged. Refs #397
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
TLDR
.razorand.cshtmlfiles become first-class: the C# inside them answers through the SDK generator's projection, markup structure comes from the bundled Razor compiler, editors get an HTML projection, and VS Code and Zed attach to Razor. The branch also carries the website's video search pages and playable social previews.Details
Spec and plan
docs/specs/RAZOR-SPEC.md(new): how the Razor toolchain works; host routing; Razor files as additional documents; the C# projection and its span map; the bundled engine, its three IR capture passes and the markup facts model (typeDiagram); the HTML projection; every feature at Razor positions; Blazor and MVC coverage; F#; editors; degradation; tests.docs/plans/RAZOR-PLAN.md(new): Phase 1 (foundation) and Phase 5 (editors) are complete. Phase 2 is partly done; Phases 3, 4 and 6 are next.Rust host
LangId::Razorfor.razorand.cshtml. It has no tree-sitter grammar, and the C# sidecar owns it.documentSymbol,foldingRangeandlinkedEditingRangego to the sidecar (razor.rs,document_symbols::handle_via_sidecar), andselectionRangeanswers[].<and@are completion triggers. In C# and F# documents they answernullwithout reaching a sidecar..if,.foreach, …) never expand in Razor files, where the same text can be markup.sharplsp-razor.sharplsp/razor/htmlProjectiontakes{ textDocument }and answers{ text }, ornullfor a file no project lists.C# sidecar
#pragma checksumnames the file. A verified span map translates positions both ways (RazorProjection,RazorSpanMap).RazorLocations,RazorResults,RazorRenameEdits,RazorCompletionEdits, which turnsusinginto@using). This covers hover, definition, type definition, declaration, references, highlights, completion, signature help, rename, semantic tokens and diagnostics. Hidden scaffolding is dropped.RazorCompiler.props, MIT, listed inTHIRD-PARTY-NOTICES.md). Each project gets its own engine (RazorEngineSettings,WorkspaceRazorFileSystem,RazorTagHelpers).RazorMarkupCapture,RazorIr) produce the markup facts (RazorMarkup).RazorMarkupSymbolsfinds the C# symbols behind them.RazorStructure).RazorHtmlProjection). Component HTML comments are restored from a second parse (RazorHtmlComments). A WHATWG-state tokenizer finds the markup context at a caret (HtmlMarkupContext).RunQueryAsyncis one query skeleton shared by documents, projections and markup facts.CS8019,IDE0005). Markup resolves components through usings that the generated C# never needs, and an import file's usings serve the files below it.Editors
razorandaspnetcorerazorfiles and activates ononLanguage:razor. Markup completion and hover go to the built-in HTML service over the projection (razor-html.ts): the server's items come first, and an HTML hover is used only when the server has none. A newrazorend-to-end chunk runs on Ubuntu and Windows.Razorwith language idrazor.Fixture:
src/sharplsp/tests/fixtures/razor/is a real solution with four projects:BlazorApp,Widgets(a Razor class library),PagesApp(Razor Pages and MVC) andDomain(F#). Its ownDirectory.Build.propsand.editorconfigkeep the repository's build settings and analyzers out. The end-to-end suites copy it to a temp folder together with the repository'sglobal.json. Without that pin, a newer installed SDK's Razor generator needs a newer Roslyn than the sidecar bundles; #399 tracks that limitation, and[RAZOR-ERRORS]describes it.Website
robots.txtaccess, per[WEB-DESIGN-MEDIA-DISCOVERY]inDESIGN-SYSTEM.md.lspkit: the pointer moves to
main, which ignores the local Deslop index.Refs #397
How Do The Automated Tests Prove It Works?
Rust end-to-end:
razor::test_full_stack_razor_routing_sessionruns the real host and sidecars over the fixture. It checks that:.,<and@as triggers;captioninside@bindarrives through the projection;Board.razorreads@page … @code, withPanelas Class nestingBadge, andAddas Method;(6,9) (7,8) (14,24);<Panelreturns both tag names, and a self-closing tag returns none;selectionRangeanswers[];@Id,@pageand@code {, and keeps<Paneland</Panel>;nullforProgram.cs, and<and@inProgram.csanswernull;Iddpublishes CS0103 undersharplsp-razoron line 22, and reverting the edit clears it;Rust unit:
razor::tests::*: markup triggers short-circuit only outside Razor; projections answer text or null; without the sidecar, the projection, linked editing and folding requests fail and name themselves.postfix_completion::tests::razor_documents_get_no_postfix_templates: postfix templates never expand in Razor, while the same text in C# offers them.diagnostics::tests::razor_files_publish_under_the_razor_source,tree_sitter_parse::tests::razor_and_cshtml_files_are_razor_without_a_grammar, and theMethodandKeykinds indocument_symbols.C# sidecar: these run against the built fixture solution in a real MSBuildWorkspace.
RazorCSharpFeatureTests:Hover_answers_csharp_fragments_with_ranges_in_the_razor_fileDefinition_lands_in_razor_files_from_razor_and_from_csharpReferences_and_highlights_cover_every_razor_use_and_drop_scaffoldingSignature_help_answers_inside_a_razor_callDiagnostics_map_csharp_errors_into_the_razor_file_and_the_solution_scanCompletion_in_an_expression_offers_members_with_edits_in_the_razor_fileRename_of_a_code_block_field_edits_every_razor_use_and_prepares_its_rangeSemantic_tokens_classify_csharp_fragments_at_their_razor_positionsImport_files_never_report_their_usings_as_unnecessaryandComponents_never_report_a_using_their_markup_may_need_as_unnecessary:_Imports.razor,_ViewImports.cshtmland a component using<Virtualize>report no unnecessary using, in the file and in the solution scan. The tag binds through its using, and a C# code-behind file still reports an unused using.RazorStructureTests: folds keep their last line, the outline nests components and members, and start and end tag names edit together.RazorMarkupEngineTests: components bind from the app and its library; attributes and directive parameters carry their descriptors; RZ10012 appears after an edit; names are found at an offset; an edit made before the workspace loads is replayed into both the facts and the projection.RazorCompletionEditsTests: an edit inside a fragment maps back to it; an addedusingbecomes one@usingline after the file's own, keeping static and alias spellings and the file's line break; an edit that cannot be placed is dropped.RazorMarkupSymbolsTests: component types, parameter and@bind-Xproperties, and child content resolve, and@onclick, HTML attributes and the generic@bindset nothing.RazorLocationsTests: a solution without Razor files compiles nothing to look for a projection.RazorHtmlProjectionTests(20 cases) andHtmlMarkupContextTests(36 cases). The tokenizer cases include malformed HTML: after a stray/, a doubled<or a bad</opening, the next character is reconsumed as the HTML tokenizer does.These run against in-memory projects (
AdhocRazorProject): Razor files as additional documents, MSBuild properties and file metadata in a global analyzer config, and hand-written generator output.RazorProjectionTests:RazorEngineSettingsTests:TargetPathmetadata;RazorSidecarTests, over the sidecar's real socket: the projection round-trips unsaved text and isnullfor an unlisted file; the structure requests keep their wire shapes; a malformed payload answers an error and leaves the socket healthy.VS Code:
razor-lsp.test.tsruns on both platforms with these tests:The
lspchunk (210 tests), thefsharpchunk (122 tests) and theworkspacechunk (446 tests) pass with the new middleware.Zed:
the_server_attaches_to_the_csharp_fsharp_and_razor_languages.Website (desktop and phone):
video-discovery.spec.jschecks that every docs page shares its film as a playable preview in every language, with unique descriptions.video-watch-pages.spec.jscovers the watch pages, embed pages, sitemap androbots.txt.Coverage of new code: every line the branch adds is hit by a test.
razor-html.ts: 160 of 160 lines.Real-app check: the host and sidecars were run over a fresh
dotnet new blazorapp (.NET 10). Every file reports no errors and keeps its shape in the HTML projection. Hover, definition, references, member completion and signature help answer on the template's own code.