Skip to content

Commit 9275486

Browse files
authored
Merge branch 'main' into copilot/add-intellisense-support
2 parents 3aa8e1b + 64b4e39 commit 9275486

File tree

65 files changed

+3035
-791
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

65 files changed

+3035
-791
lines changed

.github/copilot-instructions.md

Lines changed: 18 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -8,25 +8,17 @@ This repository implements:
88

99
- **Razor Compiler**: The core Razor compilation engine and source generators
1010
- **Language Server**: LSP-based language services for cross-platform editor support
11-
- **Visual Studio Integration**: Rich editing experience and tooling for Visual Studio
11+
- **Visual Studio Integration**: Rich editing experience and tooling for Visual Studio
12+
- **Visual Studio Code Extension**: Rich editing experience and tooling for Visual Studio
1213
- **IDE Tools**: Debugging, IntelliSense, formatting, and other developer productivity features
1314

14-
### Key Components
15-
16-
| Component | Purpose | Key Projects |
17-
|-----------|---------|--------------|
18-
| **Compiler** | Core Razor compilation and code generation | `Microsoft.AspNetCore.Razor.Language`, `Microsoft.CodeAnalysis.Razor.Compiler` |
19-
| **Language Server** | Cross-platform language services via LSP | `Microsoft.AspNetCore.Razor.LanguageServer` |
20-
| **Visual Studio** | VS-specific tooling and integration | `Microsoft.VisualStudio.RazorExtension`, `Microsoft.VisualStudio.LanguageServices.Razor` |
21-
| **Workspaces** | Project system and document management | `Microsoft.CodeAnalysis.Razor.Workspaces` |
22-
2315
## Razor Language Concepts
2416

2517
When working with this codebase, understand these core Razor concepts:
2618

2719
### File Types and Extensions
2820
- `.razor` - Blazor components (client-side and server-side)
29-
- `.cshtml` - Razor views and pages (ASP.NET Core MVC/Pages)
21+
- `.cshtml` - Razor views and pages (ASP.NET Core MVC/Pages) also referred to as "Legacy" in the codebase
3022

3123
### Language Kinds
3224
Razor documents contain multiple languages:
@@ -35,51 +27,24 @@ Razor documents contain multiple languages:
3527
- **HTML markup** - Static HTML and dynamic content
3628
- **JavaScript/CSS** - Client-side code within Razor files
3729

38-
### Key Architecture Patterns
39-
- **RazorCodeDocument** - Central document model containing parsed syntax tree and generated outputs
40-
- **Language Server Protocol (LSP)** - Cross-platform editor integration
41-
- **Virtual Documents** - Separate C# and HTML projections for tooling
42-
- **Document Mapping** - Translation between Razor and generated C# positions
43-
4430
## Development Guidelines
4531

46-
### Code Style and Patterns
47-
48-
Follow these patterns when contributing:
49-
50-
```csharp
51-
// ✅ Prefer readonly fields and properties
52-
private readonly ILogger _logger;
53-
54-
// ✅ Use explicit interface implementations for internal contracts
55-
internal sealed class MyService : IMyService
56-
{
57-
public void DoWork() { }
58-
}
59-
60-
// ✅ Use cancellation tokens in async methods
61-
public async Task<Result> ProcessAsync(CancellationToken cancellationToken)
62-
{
63-
// Implementation
64-
}
65-
66-
// ✅ Follow null-checking patterns
67-
public void Method(string? value)
68-
{
69-
if (value is null)
70-
{
71-
return;
72-
}
73-
74-
// Use value here
75-
}
76-
```
32+
### Coding Patterns
33+
34+
- Always build and test with `build.sh -test` before submitting PRs, without specifying a project or test filter
35+
- Write clear, concise, and maintainable code
36+
- Always place `[WorkItem]` attributes on tests for tracking
37+
- Prefer immutable collection types and pooled collections where possible
38+
- Use `using` statements for disposable resources
39+
- Ensure proper async/await patterns, avoid `Task.Wait()`
7740

7841
### Testing Patterns
7942

43+
- Add appropriate test coverage for new features
8044
- Prefer `TestCode` over plain strings for before/after test scenarios
8145
- Prefer raw string literals over verbatim strings
8246
- Ideally we test the end user scenario, not implementation details
47+
- Consider cross-platform compatibility by testing path handling and case sensitivity where applicable
8348

8449
### Architecture Considerations
8550

@@ -92,55 +57,14 @@ public void Method(string? value)
9257

9358
### Prerequisites
9459
- .NET 8.0+ SDK (latest version specified in `global.json`)
95-
- Visual Studio 2022 17.8+ or VS Code with C# extension
60+
- Visual Studio 2026 (Windows) or VS Code with C# extension (Windows, macOS or Linux)
9661
- PowerShell (for Windows build scripts)
9762

9863
### Building
9964
- `./restore.sh` - Restore dependencies
10065
- `./build.sh` - Full build
101-
- `./build.sh -test` - Build and run tests
102-
103-
### Visual Studio Development
104-
- `./startvs.ps1` - Open Visual Studio with correct environment
105-
- `./build.cmd -deploy` - Build and deploy VS extension for testing
106-
107-
## Project Structure Navigation
108-
109-
```
110-
src/
111-
├── Compiler/ # Core Razor compilation engine
112-
│ ├── Microsoft.AspNetCore.Razor.Language/ # Core language APIs
113-
│ └── Microsoft.CodeAnalysis.Razor.Compiler/ # Roslyn integration
114-
├── Razor/ # Language server and tooling
115-
│ ├── src/Microsoft.AspNetCore.Razor.LanguageServer/ # LSP implementation
116-
│ ├── src/Microsoft.VisualStudio.RazorExtension/ # VS package
117-
│ └── test/ # Language server tests
118-
└── Shared/ # Common utilities and test helpers
119-
```
66+
- DO NOT USE `dotnet build` directly
12067

121-
## Debugging and Diagnostics
122-
123-
### Language Server Debugging
124-
- Use F5 debugging in VS with `Microsoft.VisualStudio.RazorExtension` as startup project
125-
- Check language server logs in VS Output window (Razor Logger Output)
126-
127-
### Common Issues
128-
- **Performance**: Check document mapping efficiency and async/await usage
129-
- **Cross-platform**: Test path handling and case sensitivity
130-
- **Memory**: Use `using` statements for disposable resources
131-
- **Threading**: Ensure proper async/await patterns, avoid `Task.Wait()`
132-
133-
## Contributing
134-
135-
- Follow the [Contributing Guidelines](CONTRIBUTING.md)
136-
- Build and test locally before submitting PRs
137-
- Add appropriate test coverage for new features
138-
- Update documentation for public API changes
139-
- Consider cross-platform compatibility in all changes
140-
141-
## Useful Resources
142-
143-
- [ASP.NET Core Razor documentation](https://docs.microsoft.com/aspnet/core/razor-pages)
144-
- [Blazor documentation](https://learn.microsoft.com/en-gb/aspnet/core/blazor/?)
145-
- [Language Server Protocol specification](https://microsoft.github.io/language-server-protocol/)
146-
- [Build from source instructions](docs/contributing/BuildFromSource.md)
68+
### Testing
69+
- `./build.sh -test` - Build and run tests
70+
- DO NOT USE `dotnet test` directly

azure-pipelines-official.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -348,7 +348,7 @@ extends:
348348
- job: Linux
349349
pool:
350350
name: $(DncEngInternalBuildPool)
351-
demands: ImageOverride -equals 1es-ubuntu-2004
351+
demands: ImageOverride -equals 1es-ubuntu-2204
352352
os: linux
353353

354354
strategy:

eng/scripts/UpdateTextMate.ps1

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,6 @@ function DownloadTmLanguageJson {
2929

3030
# Copy to razor extension
3131
$content.content | Out-File -FilePath "../../src/Razor/src/Microsoft.VisualStudio.RazorExtension/EmbeddedGrammars/$fileName" -Encoding ascii
32-
33-
# Copy to grammar tests
34-
$content.content | Out-File -FilePath "../../src/Razor/test/Microsoft.AspNetCore.Razor.VSCode.Grammar.Test/embeddedGrammars/$fileName" -Encoding ascii
3532
}
3633

3734
# Find the current main branch SHA to download from

src/Compiler/Microsoft.AspNetCore.Razor.Language/test/IntegrationTests/CodeGenerationIntegrationTest.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,9 @@ public void ConditionalAttributes2()
203203
[IntegrationTestFact]
204204
public void TagHelpersWithBoundAttributes() => RunTagHelpersTest(TestTagHelperDescriptors.SimpleTagHelperDescriptors);
205205

206+
[IntegrationTestFact, WorkItem("https://github.com/dotnet/razor/issues/12261")]
207+
public void TagHelpersWithBoundAttributesAndRazorComment() => RunTagHelpersTest(TestTagHelperDescriptors.SimpleTagHelperDescriptors);
208+
206209
[IntegrationTestFact]
207210
public void TagHelpersWithPrefix() => RunTagHelpersTest(TestTagHelperDescriptors.SimpleTagHelperDescriptors);
208211

src/Compiler/Microsoft.AspNetCore.Razor.Language/test/Legacy/HtmlAttributeTest.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,12 @@ public void ConditionalAttribute_CommentBefore()
344344
ParseDocumentTest("""<p @* comment *@ class="@c" />""");
345345
}
346346

347+
[Fact, WorkItem("https://github.com/dotnet/razor/issues/12261")]
348+
public void AttributeAfterComment()
349+
{
350+
ParseDocumentTest("""<p class="first" @* comment *@ data-value="second" />""");
351+
}
352+
347353
[Fact]
348354
public void EscapedAttributeName_WithValue()
349355
{

src/Compiler/Microsoft.AspNetCore.Razor.Language/test/Legacy/TagHelperBlockRewriterTest.cs

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2472,4 +2472,104 @@ public void Rewrites_MinimizedComponentDirectiveAttributes()
24722472
builder.AllowCSharpInMarkupAttributeArea = false;
24732473
});
24742474
}
2475+
2476+
[Fact, WorkItem("https://github.com/dotnet/razor/issues/12261")]
2477+
public void TagHelper_AttributeAfterRazorComment()
2478+
{
2479+
// Arrange
2480+
var descriptors = ImmutableArray.Create(
2481+
TagHelperDescriptorBuilder.CreateTagHelper("PTagHelper", "TestAssembly")
2482+
.TagMatchingRuleDescriptor(rule => rule.RequireTagName("p"))
2483+
.BoundAttributeDescriptor(attribute => attribute
2484+
.Name("attribute-1")
2485+
.PropertyName("Attribute1")
2486+
.TypeName(typeof(string).FullName))
2487+
.BoundAttributeDescriptor(attribute => attribute
2488+
.Name("not-visible")
2489+
.PropertyName("NotVisible")
2490+
.TypeName(typeof(bool).FullName))
2491+
.Build());
2492+
2493+
// Act & Assert
2494+
EvaluateData(descriptors, """
2495+
<p
2496+
attribute-1="true"
2497+
@* visible *@
2498+
not-visible>
2499+
</p>
2500+
""");
2501+
}
2502+
2503+
[Fact, WorkItem("https://github.com/dotnet/razor/issues/12261")]
2504+
public void TagHelper_MultipleAttributesAfterRazorComment()
2505+
{
2506+
// Arrange
2507+
var descriptors = ImmutableArray.Create(
2508+
TagHelperDescriptorBuilder.CreateTagHelper("PTagHelper", "TestAssembly")
2509+
.TagMatchingRuleDescriptor(rule => rule.RequireTagName("p"))
2510+
.BoundAttributeDescriptor(attribute => attribute
2511+
.Name("attr-1")
2512+
.PropertyName("Attr1")
2513+
.TypeName(typeof(string).FullName))
2514+
.BoundAttributeDescriptor(attribute => attribute
2515+
.Name("attr-2")
2516+
.PropertyName("Attr2")
2517+
.TypeName(typeof(string).FullName))
2518+
.BoundAttributeDescriptor(attribute => attribute
2519+
.Name("attr-3")
2520+
.PropertyName("Attr3")
2521+
.TypeName(typeof(string).FullName))
2522+
.Build());
2523+
2524+
// Act & Assert
2525+
EvaluateData(descriptors, """
2526+
<p attr-1="first" @* comment *@ attr-2="second" attr-3="third"></p>
2527+
""");
2528+
}
2529+
2530+
[Fact, WorkItem("https://github.com/dotnet/razor/issues/12261")]
2531+
public void TagHelper_MultipleInterleavedRazorComments()
2532+
{
2533+
// Arrange
2534+
var descriptors = ImmutableArray.Create(
2535+
TagHelperDescriptorBuilder.CreateTagHelper("InputTagHelper", "TestAssembly")
2536+
.TagMatchingRuleDescriptor(rule => rule.RequireTagName("input"))
2537+
.BoundAttributeDescriptor(attribute => attribute
2538+
.Name("type")
2539+
.PropertyName("Type")
2540+
.TypeName(typeof(string).FullName))
2541+
.BoundAttributeDescriptor(attribute => attribute
2542+
.Name("value")
2543+
.PropertyName("Value")
2544+
.TypeName(typeof(string).FullName))
2545+
.Build());
2546+
2547+
// Act & Assert
2548+
EvaluateData(descriptors, """
2549+
<input @* comment1 *@ type="text" @* comment2 *@ value="test" @* comment3 *@ />
2550+
""");
2551+
}
2552+
2553+
[Fact, WorkItem("https://github.com/dotnet/razor/issues/12261")]
2554+
public void TagHelper_MinimizedAttributeAfterRazorComment()
2555+
{
2556+
// Arrange
2557+
var descriptors = ImmutableArray.Create(
2558+
TagHelperDescriptorBuilder.CreateTagHelper("InputTagHelper", "TestAssembly")
2559+
.TagMatchingRuleDescriptor(rule => rule.RequireTagName("input"))
2560+
.BoundAttributeDescriptor(attribute => attribute
2561+
.Name("type")
2562+
.PropertyName("Type")
2563+
.TypeName(typeof(string).FullName))
2564+
.BoundAttributeDescriptor(attribute => attribute
2565+
.Name("checked")
2566+
.PropertyName("Checked")
2567+
.TypeName(typeof(bool).FullName))
2568+
.Build());
2569+
2570+
// Act & Assert
2571+
EvaluateData(descriptors, """
2572+
<input type="checkbox" @* comment *@ checked />
2573+
""");
2574+
}
24752575
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
@addTagHelper *, TestAssembly
2+
<input value="Hello" @* comment between attributes *@ bound="World" />
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
// <auto-generated/>
2+
#pragma warning disable 1591
3+
namespace AspNetCoreGeneratedDocument
4+
{
5+
#line default
6+
using TModel = global::System.Object;
7+
using global::System;
8+
using global::System.Collections.Generic;
9+
using global::System.Linq;
10+
using global::System.Threading.Tasks;
11+
using global::Microsoft.AspNetCore.Mvc;
12+
using global::Microsoft.AspNetCore.Mvc.Rendering;
13+
using global::Microsoft.AspNetCore.Mvc.ViewFeatures;
14+
#line default
15+
#line hidden
16+
[global::Microsoft.AspNetCore.Razor.Hosting.RazorCompiledItemMetadataAttribute("Identifier", "/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/TagHelpersWithBoundAttributesAndRazorComment.cshtml")]
17+
[global::System.Runtime.CompilerServices.CreateNewOnMetadataUpdateAttribute]
18+
#nullable restore
19+
internal sealed class TestFiles_IntegrationTests_CodeGenerationIntegrationTest_TagHelpersWithBoundAttributesAndRazorComment : global::Microsoft.AspNetCore.Mvc.Razor.RazorPage<dynamic>
20+
#nullable disable
21+
{
22+
#line hidden
23+
#pragma warning disable 0649
24+
private global::Microsoft.AspNetCore.Razor.Runtime.TagHelpers.TagHelperExecutionContext __tagHelperExecutionContext;
25+
#pragma warning restore 0649
26+
private global::Microsoft.AspNetCore.Razor.Runtime.TagHelpers.TagHelperRunner __tagHelperRunner = new global::Microsoft.AspNetCore.Razor.Runtime.TagHelpers.TagHelperRunner();
27+
private global::InputTagHelper __InputTagHelper;
28+
#pragma warning disable 219
29+
private void __RazorDirectiveTokenHelpers__() {
30+
((global::System.Action)(() => {
31+
#nullable restore
32+
#line 1 "TestFiles/IntegrationTests/CodeGenerationIntegrationTest/TagHelpersWithBoundAttributesAndRazorComment.cshtml"
33+
global::System.Object __typeHelper = "*, TestAssembly";
34+
35+
#line default
36+
#line hidden
37+
#nullable disable
38+
}
39+
))();
40+
}
41+
#pragma warning restore 219
42+
#pragma warning disable 0414
43+
private static object __o = null;
44+
#pragma warning restore 0414
45+
#pragma warning disable 1998
46+
public async override global::System.Threading.Tasks.Task ExecuteAsync()
47+
{
48+
__InputTagHelper = CreateTagHelper<global::InputTagHelper>();
49+
__InputTagHelper.FooProp = "Hello";
50+
__InputTagHelper.BoundProp = "World";
51+
await __tagHelperRunner.RunAsync(__tagHelperExecutionContext);
52+
}
53+
#pragma warning restore 1998
54+
#nullable restore
55+
[global::Microsoft.AspNetCore.Mvc.Razor.Internal.RazorInjectAttribute]
56+
public global::Microsoft.AspNetCore.Mvc.ViewFeatures.IModelExpressionProvider ModelExpressionProvider { get; private set; } = default!;
57+
#nullable disable
58+
#nullable restore
59+
[global::Microsoft.AspNetCore.Mvc.Razor.Internal.RazorInjectAttribute]
60+
public global::Microsoft.AspNetCore.Mvc.IUrlHelper Url { get; private set; } = default!;
61+
#nullable disable
62+
#nullable restore
63+
[global::Microsoft.AspNetCore.Mvc.Razor.Internal.RazorInjectAttribute]
64+
public global::Microsoft.AspNetCore.Mvc.IViewComponentHelper Component { get; private set; } = default!;
65+
#nullable disable
66+
#nullable restore
67+
[global::Microsoft.AspNetCore.Mvc.Razor.Internal.RazorInjectAttribute]
68+
public global::Microsoft.AspNetCore.Mvc.Rendering.IJsonHelper Json { get; private set; } = default!;
69+
#nullable disable
70+
#nullable restore
71+
[global::Microsoft.AspNetCore.Mvc.Razor.Internal.RazorInjectAttribute]
72+
public global::Microsoft.AspNetCore.Mvc.Rendering.IHtmlHelper<dynamic> Html { get; private set; } = default!;
73+
#nullable disable
74+
}
75+
}
76+
#pragma warning restore 1591
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
/*~~~~~~~~~*/ ~~ /*~~~~~~~~*/
2+
<input value="Hello" ~~ /*~~~*/ /*~~~*/ /*~~~~~~*/ ~~ bound="World" />

0 commit comments

Comments
 (0)