Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
bae92bc
Razor foundation: spec, plan, fixture solution, and C# features insid…
MelbourneDeveloper Oct 19, 2026
3bc8285
Format RazorCompiler.props, and keep the repository's analyzer severi…
MelbourneDeveloper Oct 19, 2026
084d5ce
Video search and playable social previews for every docs film
MelbourneDeveloper Oct 19, 2026
f0ba77a
Razor host routing, and document symbols, folding and linked editing …
MelbourneDeveloper Oct 19, 2026
60a836f
Point lspkit at main, which ignores the local Deslop index
MelbourneDeveloper Oct 19, 2026
5c99b81
Serve the Razor HTML projection through the host, and tick the Razor …
MelbourneDeveloper Oct 19, 2026
73df9bf
Razor spec: describe the engine's three capture passes, the markup fa…
MelbourneDeveloper Oct 19, 2026
5f64634
Razor in VS Code and Zed: sync, activation, HTML forwarding, and an e…
MelbourneDeveloper Oct 19, 2026
4489091
One query skeleton for documents, Razor positions, projections and ma…
MelbourneDeveloper Oct 19, 2026
9131ec0
Razor spec: declare the compiler types the markup facts model uses, s…
MelbourneDeveloper Oct 19, 2026
dfc9d49
Razor HTML projection and markup-context tokenizer
MelbourneDeveloper Oct 19, 2026
2d58669
Razor spec: Zed attaches the server to the community Razor language
MelbourneDeveloper Oct 19, 2026
a17e238
Razor plan: the foundation and the editors are done
MelbourneDeveloper Oct 19, 2026
156b84f
Test the C# symbols behind Razor markup, and tell the generic @bind h…
MelbourneDeveloper Oct 19, 2026
ab742a2
Test the markup lookups by offset: tag, bound attribute and directive…
MelbourneDeveloper Oct 19, 2026
70bf531
End the Razor session with a clean shutdown, so the server writes its…
MelbourneDeveloper Oct 19, 2026
6caf4eb
Test the Razor endpoints over the sidecar's real socket
MelbourneDeveloper Oct 19, 2026
5b20c4b
Test that a Razor edit made before the workspace loads is replayed in…
MelbourneDeveloper Oct 19, 2026
a96f3a3
Test completion's additional edits through real Razor projections
MelbourneDeveloper Oct 19, 2026
8943a6b
Rename skips the Razor reference search in solutions without Razor files
MelbourneDeveloper Oct 19, 2026
f75afd9
Look for Razor projections only in projects that list a Razor file
MelbourneDeveloper Oct 19, 2026
c351c5d
Split the linked editing answer out of its handler
MelbourneDeveloper Oct 19, 2026
b6a3466
Razor plan: record the foundation's Release validation
MelbourneDeveloper Oct 19, 2026
1f29cc0
Date each watch page by its video's upload, not by the checkout that …
MelbourneDeveloper Oct 19, 2026
66f8940
Razor HTML forwarding lives and dies with the client, and forgets clo…
MelbourneDeveloper Oct 19, 2026
8044569
Razor HTML: test every reachable tokenizer and comment path, drop the…
MelbourneDeveloper Oct 19, 2026
85a34c0
Test Razor markup that has no projection: a stray file, a refused URI…
MelbourneDeveloper Oct 19, 2026
11e9721
Razor sidecar: test the projection, span map, rename, settings and fi…
MelbourneDeveloper Oct 19, 2026
014ee9f
Host: test Razor structure requests without a sidecar, and postfix te…
MelbourneDeveloper Oct 19, 2026
64372af
Razor HTML tokenizer: reconsume after a stray slash, a doubled `<` an…
MelbourneDeveloper Oct 19, 2026
2079443
Type definition and declaration share one Razor-aware location query
MelbourneDeveloper Oct 19, 2026
e0720e1
Ratchet the C# sidecar coverage threshold to 95.39%
MelbourneDeveloper Oct 19, 2026
6aba324
Razor plan: record the final validation counts and full new-code cove…
MelbourneDeveloper Oct 19, 2026
0ca0e2a
Razor import files never mark their usings unnecessary
MelbourneDeveloper Oct 19, 2026
4cbc805
No Razor file reports a using as unnecessary
MelbourneDeveloper Oct 19, 2026
4aa7d6a
The VS Code Razor fixture keeps the repository's SDK pin in its scrat…
MelbourneDeveloper Oct 20, 2026
5c9a882
The Rust Razor fixture keeps the repository's SDK pin in its scratch …
MelbourneDeveloper Oct 20, 2026
b19d0bd
The Razor target-path formatter lives in NativePaths
MelbourneDeveloper Oct 20, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions THIRD-PARTY-NOTICES.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ Copied into the VSIX at `bin/all/` alongside the sidecar executable.
| Serilog | 4.4.0 | Apache-2.0 | Serilog Contributors |
| Serilog.Sinks.File | 7.0.0 | Apache-2.0 | Serilog Contributors |
| Outcome | 1.0.0 | MIT | Christian Findlay |
| Microsoft.CodeAnalysis.Razor.Compiler and Microsoft.AspNetCore.Razor.Utilities.Shared (the Razor compiler, copied from the .NET SDK) | 10.0.0-preview.26377.103 (SDK 10.0.303) | MIT | .NET Foundation and Contributors |
| Microsoft.Extensions.ObjectPool (copied from the .NET SDK with the Razor compiler) | 10.0.1 | MIT | .NET Foundation and Contributors |

> `Microsoft.Build`, `Microsoft.Build.Framework`, and `Microsoft.NET.StringTools`
> are referenced **compile-time only** (`ExcludeAssets="runtime"`); MSBuildLocator
Expand Down
2 changes: 1 addition & 1 deletion coverage-thresholds.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"language": "dotnet"
},
"sharplsp-sidecar-csharp": {
"threshold": 95,
"threshold": 95.39,
"language": "dotnet"
},
"sharplsp-sidecar-fsharp": {
Expand Down
89 changes: 89 additions & 0 deletions docs/plans/RAZOR-PLAN.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
# Razor and Blazor Implementation Plan

**Spec:** [RAZOR-SPEC.md](../specs/RAZOR-SPEC.md)

## Context

SharpLsp has not supported `.razor` or `.cshtml` files until now. Research confirmed three things ([RAZOR-RESEARCH]):

- The SDK's own Razor source generator already runs inside the C# sidecar's design-time workspace.
- Its enhanced `#line` directives map every C# fragment back to Razor.
- The public compiler IR supplies everything the mapping misses.

So the C# sidecar owns Razor end to end, the host routes the documents to it, and the editors sync and forward HTML.

The phases below are in dependency order. Each item is checked only when a test that exercises it passes.

## TODO

### Phase 1: Foundation

- [x] Fixture solution `src/sharplsp/tests/fixtures/razor/` with `BlazorApp`, `Widgets` (RCL), `PagesApp` (Pages + MVC) and `Domain` (F#), isolated by its own `Directory.Build.props` ([RAZOR-TESTING])
- [x] Host: add `LangId::Razor` for `.razor` and `.cshtml`, owned by the C# sidecar ([RAZOR-ARCHITECTURE-ROUTING])
- [x] Host: route `documentSymbol`, `foldingRange` and `linkedEditingRange` for Razor to the sidecar, and answer `selectionRange` empty
- [x] Host: add `<` and `@` completion triggers, and short-circuit them for C# and F# documents
- [x] Host: publish Razor diagnostics under the source `sharplsp-razor`
- [x] Sidecar: bundle the Razor compiler from the build SDK, and list it in `THIRD-PARTY-NOTICES.md` ([RAZOR-ARCHITECTURE-ENGINE])
- [x] Sidecar: find Razor files as additional documents, and sync their text in every framework context, including the pending-edit replay ([RAZOR-ARCHITECTURE-DOCUMENTS])
- [x] Sidecar: build the C# projection — find the generated document by its checksum pragma, and build the verified span map in both directions ([RAZOR-ARCHITECTURE-PROJECTION])
- [x] Verify the checksum lookup, the mappings that never map (hidden, fileless, foreign, outside either text), carets at a fragment's end, and rename's check that a span holds the old name — `RazorProjectionTests`: 6 passing cases over hand-written generator output
- [x] Sidecar: build the Razor engine per project — configuration from the analyzer and parse options, an in-memory file system, tag helpers per compilation, the capture pass and markup facts
- [x] Verify configuration fallbacks, target-path metadata, current file/import reads, missing files, missing paths, cancellation and failure recovery — `RazorEngineSettingsTests`: 12 passing cases; settings, filesystem and engine each have 100% line coverage
- [x] Sidecar: build the HTML projection and the markup-context tokenizer ([RAZOR-ARCHITECTURE-HTML])
- [x] HTML projection from compiler IR, preserving offsets/newlines and current unsaved text — `RazorHtmlProjectionTests`: 19 passing fixture cases (Blazor and MVC, comments, UTF-16, incomplete tags, unsaved edits)
- [x] HTML markup-context tokenizer — `HtmlMarkupContextTests`: 36 passing cases with 100% line coverage, including malformed start/end tag recovery; the three character-reconsumption regressions were verified failing before the fix and pass unchanged afterward
- [x] Host: `sharplsp/razor/htmlProjection` answers `{ text }`, or `null` for a file no project lists — `test_full_stack_razor_routing_session`
- [x] Verify `sharplsp/razor/htmlProjection` through editor integration — `razor-lsp.test.ts`: the projection keeps the editor's offsets, line breaks and unsaved text, and feeds VS Code's HTML completion and hover

**Validation (2026-10-20):** the Release sidecar solution builds with zero warnings and errors; 671 C#, 440 F# and 125 Common tests pass, and all three coverage gates pass. C# coverage is 96.39%, with all 1,530 new C# executable lines covered. `RazorSidecarTests` exercises the real IPC contract and malformed-request recovery; `RazorCompletionEditsTests` covers mapped edits, import deduplication, aliases, static imports and LF/CRLF insertion.

**Editor validation (2026-10-20):** the final Razor chunk passes all 6 cases, the workspace chunk passes all 446 cases (including server-start lifecycle), the debugger chunk passes all 80 cases, and editor lint and formatting pass. The debugger group-outcome test that previously timed out passes unchanged in the full chunk in 6.5 seconds.

**Template validation (2026-10-20):** the real host and sidecars run over a fresh `dotnet new blazor` app (.NET 10). Its files report no errors and keep their shape in the HTML projection. Hover, definition, references, member completion and signature help answer on its real code: layouts, `@inherits`, `@Body`, `NavLinkMatch`, `@Assets`, `typeof(Program)`. The one false report found was usings marked unnecessary: every using in `_Imports.razor`, and, as review showed, a component's usings that only its markup needs. Razor files no longer report unnecessary usings (`Import_files_never_report_their_usings_as_unnecessary`, `Components_never_report_a_using_their_markup_may_need_as_unnecessary`).

### Phase 2: C# Inside Razor

- [ ] Diagnostics: C# diagnostics from projections, `RZ` diagnostics from facts, and Razor files in solution-wide diagnostics ([RAZOR-FEATURES-DIAGNOSTICS])
- [x] Hover at mapped positions ([RAZOR-FEATURES-HOVER])
- [ ] Definition, type definition, declaration and implementation from mapped positions, with generated component classes landing in their `.razor` file ([RAZOR-FEATURES-DEFINITION])
- [x] References and document highlights across projections, with hidden scaffolding dropped ([RAZOR-FEATURES-REFERENCES])
- [x] C# completion, with mapped edits and `using` → `@using` ([RAZOR-FEATURES-COMPLETION])
- [x] Signature help ([RAZOR-FEATURES-SIGNATURE-HELP])
- [x] Semantic tokens for C# fragments ([RAZOR-FEATURES-SEMANTIC-TOKENS])
- [ ] Rename and prepareRename — Razor usages and `@code` declarations, from `.cs` and `.razor` origins ([RAZOR-FEATURES-RENAME])

### Phase 3: Blazor Markup

- [ ] Component tags: hover, definition, references, highlights and linked editing
- [ ] Component attributes and `@bind-X`: hover, definition, references and rename
- [ ] Directive attributes and directives: hover
- [ ] Completion: components, parameters (with `[EditorRequired]` first), `@bind-X`, directive attributes and their parameters, and directives
- [ ] Semantic tokens for markup: component tags, attributes, directive attributes and directives
- [ ] Folding ranges and document symbols ([RAZOR-FEATURES-STRUCTURE])

### Phase 4: Razor Pages and MVC

- [ ] `@model`, `@inject`, `@section` and `@functions` through the projection ([RAZOR-MVC])
- [ ] Tag helper elements and attributes: hover and completion
- [ ] `_ViewImports.cshtml` imports resolved by the engine

### Phase 5: Editors

- [x] VS Code: sync `razor` and `aspnetcorerazor` documents, and activate on `onLanguage:razor` ([RAZOR-EDITORS]) — `razor-lsp.test.ts`: a Razor file opens as Razor, and its unsaved edits reach the server
- [x] VS Code: forward HTML completion and hover over `sharplsp/razor/htmlProjection` ([RAZOR-FEATURES-HTML]) — `razor-lsp.test.ts`: element names and HTML hovers in markup, C# completion and hover in code
- [x] VS Code: add a `razor` end-to-end chunk on both platforms — `test-chunks.json`, in both matrices
- [x] Zed: Razor language registration (Zed has no built-in Razor grammar) — `the_server_attaches_to_the_csharp_fsharp_and_razor_languages`

### Phase 6: F#

- [ ] Hover, completion and definition from Razor into F# types of a referenced F# project ([RAZOR-FSHARP])
- [ ] F#-origin rename that includes Razor usages, mapped back from the projections

### Deferred ([RAZOR-FEATURES-DEFERRED])

- [ ] Razor formatting
- [ ] Selection ranges
- [ ] Code actions: extract a block to code-behind, extract a component, generate an event handler, add `@using`
- [ ] Inlay hints, code lens, and call and type hierarchy from Razor positions
- [ ] `.razor.css` and `.razor.js` collocation awareness
- [ ] Blazor WebAssembly debugging and Razor Hot Reload
19 changes: 18 additions & 1 deletion docs/specs/DESIGN-SYSTEM.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,9 +137,26 @@ Screenshots and video of a SharpLsp feature show the real extension, never a moc
- Every autoplaying video pauses and shows its controls under `prefers-reduced-motion: reduce`.
- Every video ships a WebP poster, the films and the recordings alike.
- **Narration.** A walkthrough may be narrated (`kit/sound/`). Its reviewed script (`films/<film>.narration.json`) is spoken by Kokoro-82M through `kokoro-js` on the capturing machine, never by a speech service or the operating system's voices, whose licences forbid publishing. Each line starts just after its scene's cut, and a scene holds its last frame until its line ends. The music is an original score synthesised from code (`kit/sound/score.mjs`), so no film carries licensed or sampled music, and it ducks under the voice. A narrated film ships AAC in its MP4 and Opus in its WebM, plays unmuted with controls, and carries WebVTT captions in every site language (`<id>.<lang>.vtt`, the page's own language shown by default), listed under `captions` in the catalogue.
- **One catalogue.** Every video on the site is declared once in `src/website/src/_data/videos.json`: encodings, size, length, upload date, and its label and caption in each language. Pages place a video with the `{% video "<id>" %}` shortcode (`src/website/video-figure.js`) and never repeat `<video>` markup. The shortcode renders in the page's language and emits a schema.org `VideoObject` whose name is the player's label, so search results describe exactly what a reader hears.
- **One catalogue.** Every video on the site is declared once in `src/website/src/_data/videos.json`: encodings, size, length, upload date, the `page` that explains it, and its label and caption in each language. Every video also ships a 1200x630 JPEG social card, `<id>-card.jpg`, cut from its poster frame by `kit/encode.mjs`. Pages place a video with the `{% video "<id>" %}` shortcode (`src/website/video-figure.js`) and never repeat `<video>` markup. The shortcode renders in the page's language and emits a schema.org `VideoObject` whose name is the player's label, so search results describe exactly what a reader hears.
- **Placement.** A video goes wherever a page talks about what it shows: the home page (the profiler film autoplaying in the hero, the CPU recording in the profiler section), every docs page with a feature to show (its own narrated walkthrough, after the page's introduction; the profiler docs add both recordings, the F# language page the F# profiling walkthrough too), and every blog post that discusses the profiler or the editor experience, in every language the page is published in. The Zed page has no film: Zed is a native application, and filming it would mean recording the real desktop.

## [WEB-DESIGN-MEDIA-DISCOVERY] Video Search and Sharing

Every catalogued video can be found in Google's video results and plays in social previews. The rules follow Google's [video SEO best practices](https://developers.google.com/search/docs/appearance/video), [VideoObject structured data](https://developers.google.com/search/docs/appearance/structured-data/video) and [video sitemaps](https://developers.google.com/search/docs/crawling-indexing/sitemaps/video-sitemaps).

- **Watch pages.** Google only shows a video in video results from a page where it is the main content, and a docs page is not one. Each video therefore has a watch page per site language at `/videos/<id>/` (`/<lang>/videos/<id>/`), generated from the catalogue. It holds the video's label as the title and heading, the player, a link to the `page` that explains it, and the narration's transcript, read from its captions. It is dated by its video's upload, in its `datePublished` and its sitemap `lastmod`, never by a build. Docs and article figures link to their video's watch page.
- **Embed pages.** `/videos/<id>/embed/` is the bare player and nothing else (`noindex`). It is the `embedUrl` in structured data, the `player_loc` in the sitemap, and the `twitter:player` an X card frames.
- **Structured data.** Every figure's `VideoObject` carries `name`, `description`, `thumbnailUrl` (the WebP poster, the one thumbnail per video everywhere), `contentUrl` (the MP4), `embedUrl`, `uploadDate` with its time zone, `duration`, `width` and `height`, which must match the real files. A watch page adds `transcript` and a `SeekToAction` whose `?t=<seconds>` deep link starts the film at that second (`custom.js`), so Google can offer key moments.
- **Sitemap.** `sitemap.xml` lists every page, including every docs page, and every watch page carries a `<video:video>` entry: thumbnail, title, description, content and player locations, duration in seconds and publication date. It is the site's own template (`overrides/sitemap.njk`).
- **Crawling.** `robots.txt` never disallows `/assets/`: Google must fetch video files and thumbnails, and X, LinkedIn and Slack read the generic `User-agent: *` group before fetching card images and videos.
- **Social previews.** A docs page previews the first catalogued video whose `page` it is, and a watch page previews its own video. The preview uses:
- `og:image` and `twitter:image`: the 1200x630 card, with its alt text;
- `og:video`: the MP4, with its type and size, for networks that play it inline;
- an X `player` card: the embed page, with the MP4 as its stream.

A watch page is `og:type` `video.other`, with `video:duration` and `video:release_date`. A page without a film shares the site's `og-image.png`, with its alt text.
- **Docs metadata.** Every docs page has its own `description` in every language, and its `<title>` ends in `| SharpLsp`.

## [WEB-DESIGN-RESPONSIVE] Responsive Behavior

Design mobile-first: content order, meaning, and actions must work in a single column without hover. `768px` is the primary responsive boundary; at and below it:
Expand Down
9 changes: 6 additions & 3 deletions docs/specs/DISTRIBUTION-SPEC.md
Original file line number Diff line number Diff line change
Expand Up @@ -685,9 +685,12 @@ Every rule below binds the upstream server option and the dev extension alike.
1. **A language server and nothing else.** The server id is `sharplsp` in both
extensions, so `lsp.sharplsp.*` settings are the same in either. It declares
no grammar: the C# and F# extensions define the `CSharp` and `FSharp`
languages it attaches to, with LSP language ids `csharp` and `fsharp`. Users
choose it with Zed's per-language `language_servers` setting. Zed has
removed extension slash commands, so it declares none.
languages it attaches to, with LSP language ids `csharp` and `fsharp`. It
also attaches to `Razor`, with LSP language id `razor`: Zed has no built-in
Razor language, and the community Razor extensions all name theirs `Razor`
([RAZOR-EDITORS]). Users choose it with Zed's per-language
`language_servers` setting. Zed has removed extension slash commands, so it
declares none.
2. **Never bundled.** The server resolves from `lsp.sharplsp.binary.path`, then
`sharplsp` on the worktree's PATH, then a download: the latest stable release's
[DIST-ARCHIVE] asset for this platform, unpacked into the extension's work
Expand Down
Loading
Loading