Repository navigation
Razor review fixes (#399–#407) and Blazor and Razor docs, film and post - #406
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
… the host proof passes on it
…'s default bracket closing
…5% against main's 6.478%
…L attribute values
MelbourneDeveloper
marked this pull request as ready for review
October 20, 2026 03:29
MelbourneDeveloper
enabled auto-merge (squash)
October 20, 2026 03:29
MelbourneDeveloper
disabled auto-merge
October 20, 2026 03:29
…te values (#407) The C# in an HTML attribute value and every directive attribute is now blank in the projection, while HTML names, quotes and literal text stay. A directive attribute's children are never copied back as HTML, so a binding parameter's value no longer reappears. The compiler gives a minimized directive parameter no span, so its name is read where the attribute before it ends, only when the text there holds it. The C# sidecar coverage threshold ratchets to 95.54%.
MelbourneDeveloper
enabled auto-merge (squash)
October 20, 2026 03:30
MelbourneDeveloper
disabled auto-merge
October 20, 2026 03:59
MelbourneDeveloper
enabled auto-merge (squash)
October 20, 2026 04:02
MelbourneDeveloper
added a commit
to Nimblesite/lspkit
that referenced
this pull request
Oct 20, 2026
#19) ## TLDR A CPU sample in code the compiler hid, such as an F# `match`'s decision tree, now keeps its method's file and takes the visible line before it. A function no longer splits into a located entry and an unlocated twin (#18). ## Details - **`Symbolizer.cs`:** `FrameTable.Located` keeps TraceEvent's `file:line` when the line is visible. It asks the PDB instead when TraceEvent reports line 0, `SequencePoint.HiddenLine`, or no source at all (a prolog, IL −1). The answer is the method's nearest visible sequence point at or before the IL offset, or its first visible point when none comes before. A method with no visible point, such as a compiler-generated closure or a union constructor, stays unlocated as before. `Symbolizer` and `FrameTable` now own one `SourceLocator` for the whole recording, and `CpuCommand` disposes it. - **`SourceLocator.cs`:** the new `Line(module, methodToken, ilOffset)` reuses the per-module PDB cache that memory locations already use. `MethodPoints` returns nothing for a token that names no method in the PDB. - **Fixture:** `HotLeakAppFs/Matching.fs` adds `Matching.classify`, a non-inlined match over a struct active pattern whose tests compile to hidden sequence points. The line protocol gains `classify <count>`. Program.fs keeps its line numbers, so every existing line constant still holds. - **Spec and plan:** [PROF-DOTNET-CPU-SYMBOLS] now states the rule. The plan's "a hidden sequence point is reported without a location" now points to this defect, which is ticked. ## How Do The Automated Tests Prove It Works? - `cpu_samples_in_fsharp_match_code_keep_their_file` (F# e2e) profiles three `classify 50000000` rounds. - **Before the fix it failed:** `HotLeakAppFs.Matching.classify(int32)` came back twice. One entry held 948 samples (63.4%) in Matching.fs; the other was an unlocated twin of 179 samples (12.0%), all self time in the decision tree. - **After the fix it passes, unchanged:** one entry, in Matching.fs, with its line and a hot line inside the match (lines 17–22). - Against SharpLsp's FsPricingEngine (`run parse`, 12 s), `Csv.parseLine` went from 1592 located and 2507 unlocated samples to 4708 located and none unlocated. Under `run mixed`, every named function is located. That split is what failed SharpLsp's Windows profiler suite on Nimblesite/SharpLsp#406 (run 39364686929). - Local CI passes: - `make fmt CHECK=1` - profiler UI build and `test:paths` - `make lint` - `make test`: 413/413 tests; lsp-profiler at 93.29% and lsp-profiler-dotnet at 92.60% against their 90% thresholds - `make build` Closes #18.
This was referenced Oct 20, 2026
MelbourneDeveloper
added a commit
that referenced
this pull request
Oct 20, 2026
…op is sampled where it runs (#409) ## TLDR Fixes two defects that made the F# profiler suites flaky (#408). An F# CPU sample in a `match`'s decision tree now keeps its method's file, and FsOrderLedger's burn loop is now sampled where it actually runs. ## Details - **`lspkit` e6e4172 → 3cd5896 (Nimblesite/lspkit#19, fixes lspkit#18):** - The .NET engine's `Symbolizer` gives a frame on a hidden sequence point its method's nearest visible sequence point, or the method's first, read from the PDB through `SourceLocator`. A method with no visible point, such as a compiler-generated closure, stays unlocated. - Before this, such frames lost their file, so a function split into a located hot function and an unlocated twin. That was 61% of FsPricingEngine's `Csv.parseLine`, and it failed the Windows profiler chunk on #406 (run 39364686929). - lspkit's spec ([PROF-DOTNET-CPU-SYMBOLS]) and plan state the rule. - Its fixture `HotLeakAppFs` gains `Matching.fs` and a `classify` command. The Program.fs lines SharpLsp's `profiler-fsharp-e2e` pins (up to 97) are unchanged. - **`FsOrderLedger` `Gate.fs` and `Program.fs`:** the burn tick no longer takes the process-wide gate. - It only reads (`priceAll` snapshots under the ledger's own lock). - EventPipe's sampler stops a thread at its next safe point, so the uncontended lock call took 98% of the burn thread's samples. dotnet-counters showed 0 lock contentions per second. - That left `priceAll` at the 2% hot-function threshold, and "the burn loop is hot" failed two runs in three on macOS. - Line counts are unchanged, so `PRICE_ALL` (Ledger.fs 63–70) still holds. - **`docs/plans/PROFILER-PLAN.md`:** both defects join the F# real-program list. ## How Do The Automated Tests Prove It Works? - **lspkit's `cpu_samples_in_fsharp_match_code_keep_their_file`:** before the fix, `Matching.classify` came back twice, with 948 samples located and 179 unlocated. After the fix it passes unchanged. lspkit CI is green on the PR (run 39371497743) and on main (run 39371510065). - **FsPricingEngine through this branch's published engine (`run parse`):** `Csv.parseLine` went from 1592 located and 2507 unlocated samples to 3271 located and none unlocated. - **"the burn loop is the hotspot while memory tracks the same program"** (`profiler-fsharp-soak-cpu-e2e`): - Unchanged test. It failed two of three local runs before the fixture fix, both on "the burn loop is hot". - It passes three of three after it. - A direct engine trace puts `priceAll` on 100% of the burn thread's samples, up from 1.6%. - **The full `profiler` VS Code chunk passes locally on macOS against this branch: 323/323.** This PR's pipeline re-checks it on Linux and Windows, where the F# CPU and soak suites assert each named function's file. Closes #408.
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
Razor keeps working when a project's SDK is newer than the server's Roslyn (#399). Completion, signature help and rename now handle unfinished and escaped Razor C# (#400, #401, #402, #404), HTML completion and hover are forwarded only in markup (#405), and the HTML projection blanks every directive attribute and the C# in attribute values (#407). C# and F# semantic highlighting use the standard theme scopes and categories (#403). Blazor gets its docs page, a narrated walkthrough film and an announcement post in English, Japanese and Chinese.
Details
C# sidecar
Razor/RazorGenerator.cs(new) is wired intoRoslynProjectLoading.WithNew.Microsoft.CodeAnalysisthan the sidecar runs. The bundled copy and its two companion files take its place, and a warning names both Roslyn versions.Razor/RazorCompletionRecovery.cs(new) handles completion inside an expression the Razor compiler cannot finish:@scores., an@(missing its), and calls or lambdas inside it.RazorSpanMap.WithoutandRazorProjection.ReadInread the copy's projection back in the original file ([RAZOR-FEATURES-COMPLETION]).SignatureHelpResolveroffers no signatures for a call whose opening parenthesis has no Razor mapping, such as the generatedAddContentandAddAttribute(Razor signature help exposes generated RenderTreeBuilder calls #400).RazorRenameEditscompares a mapped span as a parsed identifier'sValueText, so@classgets its edits (Razor rename drops escaped identifiers such as @class #402).razor-html.ts) forwards completion and hover to its HTML service in markup alone. C# completion lists no longer carry word suggestions taken from the projection.RazorHtmlProjectionblanks every directive attribute and the C# in HTML attribute values, while HTML names, quotes and literal text stay.@onclick,@bind:event, and a minimized@onclick:stopPropagation.F# sidecar and VS Code highlighting
FSharpFeatures.fsmaps values, functions, properties, events and constructors to their standard LSP token categories. Value-type constructors staystruct. Spans cover only the symbol token, found by the real lexer.package.json, plain C# keywords incsharp,razorandaspnetcorerazormap tokeyword.other.cs, sopublic,classandintno longer take the control-flow colour (Use standard theme keyword colors and correct F# semantic categories #403).Docs, film and post
src/website/src/docs/blazor.md(en, ja, zh) covers:src/website/src/blog/blazor-razor-vs-code.md(en, ja, zh) is the announcement post. Its new text-free illustrated cover shows a polished metal @ sculpture with amber fire and cyan-edged glass; all three languages share the artwork with translated alt text. The superseded infographic generator is removed.blazorfilm ships as MP4 and WebM (Git LFS), with a poster, a social card and en/ja/zh captions.films/blazor.mjsshoots it againstdemos/blazor-storefront.typeThroughCompletionis now shared fromkit/acts/language.mjs.?t=instead of throwing.Specs, plans and gates
RAZOR-SPEC,SHARPLSP-SPECandDESIGN-SYSTEMdescribe the new behaviour.RAZOR-PLANrecords each item, andRAZOR-REVIEWis the regression review with its red/green evidence.How Do The Automated Tests Prove It Works?
RazorNewerSdkTestsgives the fixture a newer SDK's Razor compiler. In a fresh sidecar process:intat@currentCount;Program.cshas no CS0246, andAppresolves toApp.razor;Both behaviour tests failed before the fix.
test_latest_sdk_razor_generator_remains_usable(Rust e2e) uses an unpinned fixture copy, which runners with SDK 10.0.401 load with that SDK. It answers hover and resolvesApp.RazorCompletionRecoveryTests:@scores.,@(scores.,@(basket.MinBy(product => product.,…product.Prand…product.)</p>all failed red. They now returnCountorPrice, with edits at the Razor caret. Closed@( )and@{ }controls, an unclosed@codeblock and a brace closer pass too.Razor C# completion in VS Code appends word suggestions from the HTML projection #405:
RazorSidecarTests.Positioned_projection_forwards_markup_but_not_csharp_or_its_whitespaceandIncomplete_csharp_keeps_html_forwarding_off. The Rust e2e checkassert_html_projection_only_in_markupruns over the realBoard.razor, andrazor-lsp.test.tsasserts that C# completion lists hold no projection words.Razor HTML projection retains directive attributes and C# attribute values #407:
RazorHtmlProjectionTests.Projection_query_preserves_html_and_utf16_offsetsgains six rows:<button @onclick=… disabled="@(scores.Count > 9)">, a plaindisabled="@(…)", minimized@onclick:stopPropagationafter a directive, after an HTML attribute and after a quoted attribute, and@bind:event="oninput". The failing rows were red before the fix. The fixture test also asserts thatscores.Count,@bindandoninputnever reach Board's projection.RazorProjectionTests: a copy's map keeps only each fragment's part before its inserted closers, so carets past them never map.RazorCSharpFeatureTests: signature help inside@(anddisabled="@(…)"lists no scaffolding, while real calls still answer. Renaming@classedits every declaration and use.Highlighting:
FSharpSemanticHighlightingTestschecks real-project F# categories with exact spans.semantic-highlighting.test.tschecks that plain and control C# keywords use separate standard scopes, with no hard-coded colours.Website (Playwright, desktop plus two phone projects; 901 tests across the affected specs, all passing):
docs-nav: the page sits in "Write code" after F#.site-layout: the ja/zh page and post keep the English structure and links.site-videos: the walkthrough plays in every language.video-watch-pagesandvideo-discovery: watch pages and metadata match the real file.video-playback: the film decodes, seeks and resumes.screenshotsandmobile-navcover the new page.currentTimepage error before the guard.Local CI legs:
realrepois left to CI because it clones the corpus. The coverage gate over all 14 chunks reads 96.86% (threshold 96.96%, with its 1-point tolerance).Closes #399, closes #400, closes #401, closes #402, closes #403, closes #404, closes #405, closes #407. Refs #397.
The replacement cover passed 18 desktop/phone layout and localization checks, and its rendered desktop and phone headers were visually verified.
Final validation: CI run 39368330371 passed all 52 jobs on head
925b8962; PR merged asab8e713d. Both profiler chunks passed unchanged. The pre-existing F# hidden-sequence-point attribution defect found during investigation is tracked with a real-engine reproduction in lspkit#18.