From 3f9eced62a5df820ac73af3abc0b5ba3fe023142 Mon Sep 17 00:00:00 2001 From: Todd Grunke Date: Tue, 31 Mar 2026 11:31:33 -0700 Subject: [PATCH 01/13] Initial support for razor isolation files (cs, css, js) This adds contextmenu support to solution explorer in VS for creating and viewing razor isolation files in support of this feedback ticket: https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1903761 Menu items are hooked up through the RazorPackage, thus requiring an appropriate UIContextRule to handle when appropriate files are right clicked on. This also required registering the razor package as allowing to be loaded on a background thread, something we should have been allowing anyway. The actual implementation to create the files was kept very simple. I originally had coded up the c# implementation to walk up the hierarchy and parse editor configs, but it added more complexity than it's worth without feedback that it's wanted (verified this decision with Sayed) Most of the project interaction is done through DTE as this is implemented at the VS extension layer. Would love to hear feedback about whether another choice would be more appropriate. --- .../CSharpIsolationFileCommandHandler.cs | 85 +++++++ .../CssIsolationFileCommandHandler.cs | 28 +++ .../IsolationFileCommandHandler.cs | 222 ++++++++++++++++++ .../JavascriptIsolationFileCommandHandler.cs | 29 +++ .../RazorContextMenu.vsct | 70 +++++- .../RazorPackage.cs | 56 ++++- .../Resources.resx | 20 +- .../xlf/RazorContextMenu.vsct.cs.xlf | 45 ++++ .../xlf/RazorContextMenu.vsct.de.xlf | 45 ++++ .../xlf/RazorContextMenu.vsct.es.xlf | 45 ++++ .../xlf/RazorContextMenu.vsct.fr.xlf | 45 ++++ .../xlf/RazorContextMenu.vsct.it.xlf | 45 ++++ .../xlf/RazorContextMenu.vsct.ja.xlf | 45 ++++ .../xlf/RazorContextMenu.vsct.ko.xlf | 45 ++++ .../xlf/RazorContextMenu.vsct.pl.xlf | 45 ++++ .../xlf/RazorContextMenu.vsct.pt-BR.xlf | 45 ++++ .../xlf/RazorContextMenu.vsct.ru.xlf | 45 ++++ .../xlf/RazorContextMenu.vsct.tr.xlf | 45 ++++ .../xlf/RazorContextMenu.vsct.zh-Hans.xlf | 45 ++++ .../xlf/RazorContextMenu.vsct.zh-Hant.xlf | 45 ++++ .../xlf/Resources.cs.xlf | 30 +++ .../xlf/Resources.de.xlf | 30 +++ .../xlf/Resources.es.xlf | 30 +++ .../xlf/Resources.fr.xlf | 30 +++ .../xlf/Resources.it.xlf | 30 +++ .../xlf/Resources.ja.xlf | 30 +++ .../xlf/Resources.ko.xlf | 30 +++ .../xlf/Resources.pl.xlf | 30 +++ .../xlf/Resources.pt-BR.xlf | 30 +++ .../xlf/Resources.ru.xlf | 30 +++ .../xlf/Resources.tr.xlf | 30 +++ .../xlf/Resources.zh-Hans.xlf | 30 +++ .../xlf/Resources.zh-Hant.xlf | 30 +++ 33 files changed, 1480 insertions(+), 5 deletions(-) create mode 100644 src/Razor/src/Microsoft.VisualStudio.RazorExtension/IsolationFiles/CSharpIsolationFileCommandHandler.cs create mode 100644 src/Razor/src/Microsoft.VisualStudio.RazorExtension/IsolationFiles/CssIsolationFileCommandHandler.cs create mode 100644 src/Razor/src/Microsoft.VisualStudio.RazorExtension/IsolationFiles/IsolationFileCommandHandler.cs create mode 100644 src/Razor/src/Microsoft.VisualStudio.RazorExtension/IsolationFiles/JavascriptIsolationFileCommandHandler.cs diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/IsolationFiles/CSharpIsolationFileCommandHandler.cs b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/IsolationFiles/CSharpIsolationFileCommandHandler.cs new file mode 100644 index 00000000000..649851a6c13 --- /dev/null +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/IsolationFiles/CSharpIsolationFileCommandHandler.cs @@ -0,0 +1,85 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.IO; +using Microsoft.VisualStudio.Shell; + +namespace Microsoft.VisualStudio.RazorExtension.IsolationFiles; + +/// +/// Command handler for C# code-behind isolation files (.razor.cs). +/// +internal sealed class CSharpIsolationFileCommandHandler(IServiceProvider serviceProvider) + : IsolationFileCommandHandler(serviceProvider, ".cs") +{ + protected override string AddText => Resources.Add_Code_Behind_File; + + protected override string ViewText => Resources.View_Code_Behind_File; + + protected override string GenerateFileContent(string razorFilePath, string componentOrViewName) + { + // Get namespace from the project's RootNamespace + relative path, + // matching how the Razor compiler resolves namespaces. + var namespaceName = GetNamespaceForRazorFile(razorFilePath) ?? "MyApp"; + + return $$""" + namespace {{namespaceName}} + { + public partial class {{componentOrViewName}} + { + } + } + """; + } + + /// + /// Gets the namespace for a Razor file by reading the project's RootNamespace property + /// and computing the relative path, matching the Razor compiler's namespace resolution. + /// + private string? GetNamespaceForRazorFile(string razorFilePath) + { + ThreadHelper.ThrowIfNotOnUIThread(); + + try + { + var projectItem = FindProjectItem(razorFilePath); + if (projectItem?.ContainingProject is EnvDTE.Project project) + { + var rootNamespace = project.Properties.Item("RootNamespace")?.Value as string; + if (!rootNamespace.IsNullOrEmpty()) + { + if (Path.GetDirectoryName(project.FullName) is not string projectDir + || Path.GetDirectoryName(razorFilePath) is not string fileDir + || !fileDir.StartsWith(projectDir, StringComparison.OrdinalIgnoreCase) + || (fileDir.Length > projectDir.Length + && fileDir[projectDir.Length] != Path.DirectorySeparatorChar + && fileDir[projectDir.Length] != Path.AltDirectorySeparatorChar)) + { + return rootNamespace; + } + + var relativePath = fileDir[projectDir.Length..] + .TrimStart(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); + + if (relativePath.IsNullOrEmpty()) + { + return rootNamespace; + } + + // Convert path separators to dots, matching how the Razor compiler computes namespaces + var relativeNamespace = relativePath + .Replace(Path.DirectorySeparatorChar, '.') + .Replace(Path.AltDirectorySeparatorChar, '.'); + + return rootNamespace + "." + relativeNamespace; + } + } + } + catch + { + } + + return null; + } +} diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/IsolationFiles/CssIsolationFileCommandHandler.cs b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/IsolationFiles/CssIsolationFileCommandHandler.cs new file mode 100644 index 00000000000..6e62d3fb858 --- /dev/null +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/IsolationFiles/CssIsolationFileCommandHandler.cs @@ -0,0 +1,28 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +namespace Microsoft.VisualStudio.RazorExtension.IsolationFiles; + +/// +/// Command handler for CSS isolation files (.razor.css). +/// +internal sealed class CssIsolationFileCommandHandler(IServiceProvider serviceProvider) + : IsolationFileCommandHandler(serviceProvider, ".css") +{ + protected override string AddText => Resources.Add_CSS_Isolation_File; + + protected override string ViewText => Resources.View_CSS_Isolation_File; + + protected override string GenerateFileContent(string razorFilePath, string componentOrViewName) + { + var fileType = IsRazorComponentFile(razorFilePath) ? "component" : "view"; + + // For CSS isolation files, we create an empty CSS file with a comment + return $$""" + /* Scoped CSS styles for {{componentOrViewName}} {{fileType}} */ + + """; + } +} diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/IsolationFiles/IsolationFileCommandHandler.cs b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/IsolationFiles/IsolationFileCommandHandler.cs new file mode 100644 index 00000000000..79dd17e4207 --- /dev/null +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/IsolationFiles/IsolationFileCommandHandler.cs @@ -0,0 +1,222 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.IO; +using System.Runtime.InteropServices; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; + +namespace Microsoft.VisualStudio.RazorExtension.IsolationFiles; + +/// +/// Base class for handling Add/View isolation file commands for Razor documents. +/// +internal abstract class IsolationFileCommandHandler(IServiceProvider serviceProvider, string fileExtension) +{ + private readonly IServiceProvider _serviceProvider = serviceProvider; + private readonly string _fileExtension = fileExtension; + + /// + /// Gets the display text when the file doesn't exist (e.g., "Add CSS Isolation File"). + /// + protected abstract string AddText { get; } + + /// + /// Gets the display text when the file exists (e.g., "View CSS Isolation File"). + /// + protected abstract string ViewText { get; } + + /// + /// Generates the default content for a new isolation file. + /// + protected abstract string GenerateFileContent(string razorFilePath, string componentOrViewName); + + /// + /// Returns whether this command is applicable for the given Razor file. + /// Override to hide the command for certain file types. + /// + protected virtual bool IsApplicable(string razorFilePath) => true; + + /// + /// Configures the command status and text based on whether the isolation file exists. + /// + public void OnBeforeQueryStatus(object sender, EventArgs e) + { + ThreadHelper.ThrowIfNotOnUIThread(); + + if (sender is not OleMenuCommand command) + { + return; + } + + var contextGuid = RazorPackage.GuidRazorFileContext; + + // Check if the Razor file context is active before doing expensive hierarchy queries + if (_serviceProvider.GetService(typeof(SVsShellMonitorSelection)) is not IVsMonitorSelection monitorSelection + || monitorSelection.GetCmdUIContextCookie(ref contextGuid, out var cookie) != VSConstants.S_OK + || monitorSelection.IsCmdUIContextActive(cookie, out var isActive) != VSConstants.S_OK + || isActive == 0 + || GetSelectedRazorFilePath() is not string razorFilePath + || !IsApplicable(razorFilePath)) + { + command.Visible = false; + return; + } + + var isolationFilePath = GetIsolationFilePath(razorFilePath); + var isolationFileExists = File.Exists(isolationFilePath); + + command.Visible = true; + command.Enabled = true; + command.Text = isolationFileExists ? ViewText : AddText; + } + + /// + /// Executes the command - either creates a new file or opens an existing one. + /// + public void Execute(object sender, EventArgs e) + { + ThreadHelper.ThrowIfNotOnUIThread(); + + if (GetSelectedRazorFilePath() is not string razorFilePath) + { + return; + } + + var isolationFilePath = GetIsolationFilePath(razorFilePath); + + if (!File.Exists(isolationFilePath)) + { + // Create the file + CreateIsolationFile(razorFilePath, isolationFilePath); + } + + // Open the file + VsShellUtilities.OpenDocument(_serviceProvider, isolationFilePath); + } + + /// + /// Gets the path to the isolation file based on the Razor file path. + /// + protected virtual string GetIsolationFilePath(string razorFilePath) + { + return razorFilePath + _fileExtension; + } + + /// + /// Creates the isolation file with appropriate content. + /// + private void CreateIsolationFile(string razorFilePath, string isolationFilePath) + { + var componentName = Path.GetFileNameWithoutExtension(razorFilePath); + var content = GenerateFileContent(razorFilePath, componentName); + + // Write the file + File.WriteAllText(isolationFilePath, content); + + // Add to project + AddFileToProject(isolationFilePath, razorFilePath); + } + + /// + /// Adds the newly created file to the project. + /// + private void AddFileToProject(string isolationFilePath, string razorFilePath) + { + ThreadHelper.ThrowIfNotOnUIThread(); + + try + { + if (FindProjectItem(razorFilePath) is EnvDTE.ProjectItem razorProjectItem) + { + // Add the isolation file as a nested item under the Razor file + razorProjectItem.ProjectItems.AddFromFile(isolationFilePath); + } + } + catch + { + // If adding to project fails, the file still exists on disk + // which is better than nothing + } + } + + protected EnvDTE.ProjectItem? FindProjectItem(string filePath) + { + ThreadHelper.ThrowIfNotOnUIThread(); + + var dte = (EnvDTE.DTE?)_serviceProvider.GetService(typeof(EnvDTE.DTE)); + + return dte?.Solution.FindProjectItem(filePath); + } + + /// + /// Gets the file path of the currently selected Razor file in Solution Explorer. + /// + private string? GetSelectedRazorFilePath() + { + ThreadHelper.ThrowIfNotOnUIThread(); + + if (_serviceProvider.GetService(typeof(SVsShellMonitorSelection)) is not IVsMonitorSelection monitorSelection) + { + return null; + } + + monitorSelection.GetCurrentSelection(out var hierarchyPtr, out var itemId, out _, out var selectionContainerPtr); + + try + { + if (itemId is VSConstants.VSITEMID_NIL or VSConstants.VSITEMID_ROOT or VSConstants.VSITEMID_SELECTION) + { + return null; + } + + if (hierarchyPtr != IntPtr.Zero) + { + var hierarchy = Marshal.GetObjectForIUnknown(hierarchyPtr) as IVsHierarchy; + if (hierarchy is IVsProject project) + { + project.GetMkDocument(itemId, out var filePath); + + if (!string.IsNullOrEmpty(filePath) && IsRazorFile(filePath)) + { + return filePath; + } + } + } + + return null; + } + finally + { + if (hierarchyPtr != IntPtr.Zero) + { + Marshal.Release(hierarchyPtr); + } + + if (selectionContainerPtr != IntPtr.Zero) + { + Marshal.Release(selectionContainerPtr); + } + } + } + + /// + /// Checks if a file is a Razor file (.razor or .cshtml). + /// + private static bool IsRazorFile(string filePath) + { + var extension = Path.GetExtension(filePath); + return string.Equals(extension, ".razor", StringComparison.OrdinalIgnoreCase) || + string.Equals(extension, ".cshtml", StringComparison.OrdinalIgnoreCase); + } + + /// + /// Checks if a file is a Razor file (.razor or .cshtml). + /// + protected static bool IsRazorComponentFile(string filePath) + { + var extension = Path.GetExtension(filePath); + return string.Equals(extension, ".razor", StringComparison.OrdinalIgnoreCase); + } +} diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/IsolationFiles/JavascriptIsolationFileCommandHandler.cs b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/IsolationFiles/JavascriptIsolationFileCommandHandler.cs new file mode 100644 index 00000000000..fa6bd98d4ec --- /dev/null +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/IsolationFiles/JavascriptIsolationFileCommandHandler.cs @@ -0,0 +1,29 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +namespace Microsoft.VisualStudio.RazorExtension.IsolationFiles; + +/// +/// Command handler for JavaScript isolation files (.razor.js). +/// +internal sealed class JavascriptIsolationFileCommandHandler(IServiceProvider serviceProvider) + : IsolationFileCommandHandler(serviceProvider, ".js") +{ + protected override string AddText => Resources.Add_JavaScript_Isolation_File; + + protected override string ViewText => Resources.View_JavaScript_Isolation_File; + + // JS isolation files are only applicable to Razor components (.razor), not MVC views (.cshtml). + protected override bool IsApplicable(string razorFilePath) + => IsRazorComponentFile(razorFilePath); + + protected override string GenerateFileContent(string razorFilePath, string componentOrViewName) + { + // For JavaScript isolation files, we create an empty js file with a comment + return $$""" + // JavaScript isolation for {{componentOrViewName}} component + """; + } +} diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/RazorContextMenu.vsct b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/RazorContextMenu.vsct index 5c84f40e8d5..8df37420876 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/RazorContextMenu.vsct +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/RazorContextMenu.vsct @@ -1,5 +1,5 @@ - - + + @@ -7,7 +7,7 @@ - + @@ -22,13 +22,30 @@ + + + + + + + + + + + + + + + + + @@ -42,6 +59,7 @@ + + + + + + + + + + + + + + diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/RazorPackage.cs b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/RazorPackage.cs index 67567b6478d..380f18e5e2f 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/RazorPackage.cs +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/RazorPackage.cs @@ -15,6 +15,7 @@ using Microsoft.VisualStudio.Razor.LanguageClient.Options; using Microsoft.VisualStudio.Razor.Logging; using Microsoft.VisualStudio.Razor.Snippets; +using Microsoft.VisualStudio.RazorExtension.IsolationFiles; using Microsoft.VisualStudio.RazorExtension.Snippets; using Microsoft.VisualStudio.RazorExtension.SyntaxVisualizer; using Microsoft.VisualStudio.Shell; @@ -25,7 +26,7 @@ namespace Microsoft.VisualStudio.RazorExtension; -[PackageRegistration(UseManagedResourcesOnly = true)] +[PackageRegistration(UseManagedResourcesOnly = true, AllowsBackgroundLoading = true)] [AboutDialogInfo(PackageGuidString, "Razor (ASP.NET Core)", "#110", "#112", IconResourceID = "#400")] [ProvideService(typeof(RazorLanguageService))] [ProvideLanguageService(typeof(RazorLanguageService), RazorConstants.RazorLSPContentTypeName, 110)] @@ -41,6 +42,14 @@ namespace Microsoft.VisualStudio.RazorExtension; expression: "RazorContentType", termNames: ["RazorContentType"], termValues: [$"ActiveEditorContentType:{RazorConstants.RazorLSPContentTypeName}"])] +// Activate context menu commands when a .razor or .cshtml file is selected in Solution Explorer +[ProvideAutoLoad(GuidRazorFileContextString, PackageAutoLoadFlags.BackgroundLoad)] +[ProvideUIContextRule( + contextGuid: GuidRazorFileContextString, + name: "Razor File Selected", + expression: "RazorFile | CshtmlFile", + termNames: ["RazorFile", "CshtmlFile"], + termValues: ["HierSingleSelectionName:.razor$", "HierSingleSelectionName:.cshtml$"])] internal sealed class RazorPackage : AsyncPackage { public const string PackageGuidString = "13b72f58-279e-49e0-a56d-296be02f0805"; @@ -49,6 +58,17 @@ internal sealed class RazorPackage : AsyncPackage internal static readonly Guid GuidSyntaxVisualizerMenuCmdSet = new Guid(GuidSyntaxVisualizerMenuCmdSetString); internal const uint CmdIDRazorSyntaxVisualizer = 0x101; + // Razor isolation files command set + internal const string GuidRazorIsolationFilesCmdSetString = "8B2B3C5D-6E4A-4F9B-9C8D-1A2B3C4D5E6F"; + internal static readonly Guid GuidRazorIsolationFilesCmdSet = new Guid(GuidRazorIsolationFilesCmdSetString); + internal const uint CmdIdAddOrViewCssIsolationFile = 0x0100; + internal const uint CmdIdAddOrViewCsIsolationFile = 0x0101; + internal const uint CmdIdAddOrViewJsIsolationFile = 0x0102; + + // UI Context for when a .razor or .cshtml file is selected + internal const string GuidRazorFileContextString = "7C3F2F9E-8D4A-4B6C-9E1F-5A8D7C6B3E2D"; + internal static readonly Guid GuidRazorFileContext = new Guid(GuidRazorFileContextString); + private OptionsStorage? _optionsStorage = null; protected override async Task InitializeAsync(CancellationToken cancellationToken, IProgress progress) @@ -78,6 +98,9 @@ protected override async Task InitializeAsync(CancellationToken cancellationToke var toolwndCommandID = new CommandID(GuidSyntaxVisualizerMenuCmdSet, (int)CmdIDRazorSyntaxVisualizer); var menuToolWin = new MenuCommand(ShowToolWindow, toolwndCommandID); mcs.AddCommand(menuToolWin); + + // Register isolation file commands + RegisterIsolationFileCommands(mcs); } var componentModel = (IComponentModel)GetGlobalService(typeof(SComponentModel)); @@ -135,4 +158,35 @@ private void ShowToolWindow(object sender, EventArgs e) ErrorHandler.ThrowOnFailure(windowFrame.Show()); } + + /// + /// Registers the isolation file commands (CSS, C#, Javascript) in the menu command service. + /// + private void RegisterIsolationFileCommands(OleMenuCommandService mcs) + { + ThreadHelper.ThrowIfNotOnUIThread(); + + // Create command handlers + var cssHandler = new CssIsolationFileCommandHandler(this); + var csharpHandler = new CSharpIsolationFileCommandHandler(this); + var javascriptHandler = new JavascriptIsolationFileCommandHandler(this); + + // CSS Isolation File Command + var cssCommandId = new CommandID(GuidRazorIsolationFilesCmdSet, (int)CmdIdAddOrViewCssIsolationFile); + var cssCommand = new OleMenuCommand(cssHandler.Execute, cssCommandId); + cssCommand.BeforeQueryStatus += cssHandler.OnBeforeQueryStatus; + mcs.AddCommand(cssCommand); + + // C# Code-Behind Isolation File Command + var csharpCommandId = new CommandID(GuidRazorIsolationFilesCmdSet, (int)CmdIdAddOrViewCsIsolationFile); + var csharpCommand = new OleMenuCommand(csharpHandler.Execute, csharpCommandId); + csharpCommand.BeforeQueryStatus += csharpHandler.OnBeforeQueryStatus; + mcs.AddCommand(csharpCommand); + + // JS Isolation File Command + var javascriptCommandId = new CommandID(GuidRazorIsolationFilesCmdSet, (int)CmdIdAddOrViewJsIsolationFile); + var javascriptCommand = new OleMenuCommand(javascriptHandler.Execute, javascriptCommandId); + javascriptCommand.BeforeQueryStatus += javascriptHandler.OnBeforeQueryStatus; + mcs.AddCommand(javascriptCommand); + } } diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/Resources.resx b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/Resources.resx index ad98e81b0b7..8be71c0373d 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/Resources.resx +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/Resources.resx @@ -120,4 +120,22 @@ LSP Razor Editor - \ No newline at end of file + + Add CSS Isolation File + + + View CSS Isolation File + + + Add C# Code-Behind File + + + View C# Code-Behind File + + + Add JavaScript Isolation File + + + View JavaScript Isolation File + + diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.cs.xlf b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.cs.xlf index 4a4cf09c0cd..52e9ec1de1d 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.cs.xlf +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.cs.xlf @@ -17,6 +17,51 @@ Show Referenced Tag Helpers + + Add or View C# Code-Behind File + Add or View C# Code-Behind File + + + + AddOrViewCsIsolationFile + AddOrViewCsIsolationFile + + + + AddOrViewCsIsolationFile + AddOrViewCsIsolationFile + + + + Add or View CSS Isolation File + Add or View CSS Isolation File + + + + AddOrViewCssIsolationFile + AddOrViewCssIsolationFile + + + + AddOrViewCssIsolationFile + AddOrViewCssIsolationFile + + + + Add or View JavaScript Isolation File + Add or View JavaScript Isolation File + + + + AddOrViewJsIsolationFile + AddOrViewJsIsolationFile + + + + AddOrViewJsIsolationFile + AddOrViewJsIsolationFile + + Razor Syntax Visualizer Razor Syntax Visualizer diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.de.xlf b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.de.xlf index 4fd4350b5f2..0ea157700bd 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.de.xlf +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.de.xlf @@ -17,6 +17,51 @@ Show Referenced Tag Helpers + + Add or View C# Code-Behind File + Add or View C# Code-Behind File + + + + AddOrViewCsIsolationFile + AddOrViewCsIsolationFile + + + + AddOrViewCsIsolationFile + AddOrViewCsIsolationFile + + + + Add or View CSS Isolation File + Add or View CSS Isolation File + + + + AddOrViewCssIsolationFile + AddOrViewCssIsolationFile + + + + AddOrViewCssIsolationFile + AddOrViewCssIsolationFile + + + + Add or View JavaScript Isolation File + Add or View JavaScript Isolation File + + + + AddOrViewJsIsolationFile + AddOrViewJsIsolationFile + + + + AddOrViewJsIsolationFile + AddOrViewJsIsolationFile + + Razor Syntax Visualizer Razor Syntax Visualizer diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.es.xlf b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.es.xlf index ff65a866545..a32fd0baa25 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.es.xlf +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.es.xlf @@ -17,6 +17,51 @@ Show Referenced Tag Helpers + + Add or View C# Code-Behind File + Add or View C# Code-Behind File + + + + AddOrViewCsIsolationFile + AddOrViewCsIsolationFile + + + + AddOrViewCsIsolationFile + AddOrViewCsIsolationFile + + + + Add or View CSS Isolation File + Add or View CSS Isolation File + + + + AddOrViewCssIsolationFile + AddOrViewCssIsolationFile + + + + AddOrViewCssIsolationFile + AddOrViewCssIsolationFile + + + + Add or View JavaScript Isolation File + Add or View JavaScript Isolation File + + + + AddOrViewJsIsolationFile + AddOrViewJsIsolationFile + + + + AddOrViewJsIsolationFile + AddOrViewJsIsolationFile + + Razor Syntax Visualizer Razor Syntax Visualizer diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.fr.xlf b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.fr.xlf index 26855340d6e..c4057c494bb 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.fr.xlf +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.fr.xlf @@ -17,6 +17,51 @@ Show Referenced Tag Helpers + + Add or View C# Code-Behind File + Add or View C# Code-Behind File + + + + AddOrViewCsIsolationFile + AddOrViewCsIsolationFile + + + + AddOrViewCsIsolationFile + AddOrViewCsIsolationFile + + + + Add or View CSS Isolation File + Add or View CSS Isolation File + + + + AddOrViewCssIsolationFile + AddOrViewCssIsolationFile + + + + AddOrViewCssIsolationFile + AddOrViewCssIsolationFile + + + + Add or View JavaScript Isolation File + Add or View JavaScript Isolation File + + + + AddOrViewJsIsolationFile + AddOrViewJsIsolationFile + + + + AddOrViewJsIsolationFile + AddOrViewJsIsolationFile + + Razor Syntax Visualizer Razor Syntax Visualizer diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.it.xlf b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.it.xlf index 11ebf63457c..28e7d3305a0 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.it.xlf +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.it.xlf @@ -17,6 +17,51 @@ Show Referenced Tag Helpers + + Add or View C# Code-Behind File + Add or View C# Code-Behind File + + + + AddOrViewCsIsolationFile + AddOrViewCsIsolationFile + + + + AddOrViewCsIsolationFile + AddOrViewCsIsolationFile + + + + Add or View CSS Isolation File + Add or View CSS Isolation File + + + + AddOrViewCssIsolationFile + AddOrViewCssIsolationFile + + + + AddOrViewCssIsolationFile + AddOrViewCssIsolationFile + + + + Add or View JavaScript Isolation File + Add or View JavaScript Isolation File + + + + AddOrViewJsIsolationFile + AddOrViewJsIsolationFile + + + + AddOrViewJsIsolationFile + AddOrViewJsIsolationFile + + Razor Syntax Visualizer Razor Syntax Visualizer diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.ja.xlf b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.ja.xlf index ab6ae5b95ac..fd82032c029 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.ja.xlf +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.ja.xlf @@ -17,6 +17,51 @@ Show Referenced Tag Helpers + + Add or View C# Code-Behind File + Add or View C# Code-Behind File + + + + AddOrViewCsIsolationFile + AddOrViewCsIsolationFile + + + + AddOrViewCsIsolationFile + AddOrViewCsIsolationFile + + + + Add or View CSS Isolation File + Add or View CSS Isolation File + + + + AddOrViewCssIsolationFile + AddOrViewCssIsolationFile + + + + AddOrViewCssIsolationFile + AddOrViewCssIsolationFile + + + + Add or View JavaScript Isolation File + Add or View JavaScript Isolation File + + + + AddOrViewJsIsolationFile + AddOrViewJsIsolationFile + + + + AddOrViewJsIsolationFile + AddOrViewJsIsolationFile + + Razor Syntax Visualizer Razor Syntax Visualizer diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.ko.xlf b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.ko.xlf index 1a9dbc08b46..8ea6d8ea12e 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.ko.xlf +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.ko.xlf @@ -17,6 +17,51 @@ Show Referenced Tag Helpers + + Add or View C# Code-Behind File + Add or View C# Code-Behind File + + + + AddOrViewCsIsolationFile + AddOrViewCsIsolationFile + + + + AddOrViewCsIsolationFile + AddOrViewCsIsolationFile + + + + Add or View CSS Isolation File + Add or View CSS Isolation File + + + + AddOrViewCssIsolationFile + AddOrViewCssIsolationFile + + + + AddOrViewCssIsolationFile + AddOrViewCssIsolationFile + + + + Add or View JavaScript Isolation File + Add or View JavaScript Isolation File + + + + AddOrViewJsIsolationFile + AddOrViewJsIsolationFile + + + + AddOrViewJsIsolationFile + AddOrViewJsIsolationFile + + Razor Syntax Visualizer Razor Syntax Visualizer diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.pl.xlf b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.pl.xlf index 42b4eee2f24..2e3251705e4 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.pl.xlf +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.pl.xlf @@ -17,6 +17,51 @@ Show Referenced Tag Helpers + + Add or View C# Code-Behind File + Add or View C# Code-Behind File + + + + AddOrViewCsIsolationFile + AddOrViewCsIsolationFile + + + + AddOrViewCsIsolationFile + AddOrViewCsIsolationFile + + + + Add or View CSS Isolation File + Add or View CSS Isolation File + + + + AddOrViewCssIsolationFile + AddOrViewCssIsolationFile + + + + AddOrViewCssIsolationFile + AddOrViewCssIsolationFile + + + + Add or View JavaScript Isolation File + Add or View JavaScript Isolation File + + + + AddOrViewJsIsolationFile + AddOrViewJsIsolationFile + + + + AddOrViewJsIsolationFile + AddOrViewJsIsolationFile + + Razor Syntax Visualizer Razor Syntax Visualizer diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.pt-BR.xlf b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.pt-BR.xlf index 0d245e6f5e8..1c78267cebb 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.pt-BR.xlf +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.pt-BR.xlf @@ -17,6 +17,51 @@ Show Referenced Tag Helpers + + Add or View C# Code-Behind File + Add or View C# Code-Behind File + + + + AddOrViewCsIsolationFile + AddOrViewCsIsolationFile + + + + AddOrViewCsIsolationFile + AddOrViewCsIsolationFile + + + + Add or View CSS Isolation File + Add or View CSS Isolation File + + + + AddOrViewCssIsolationFile + AddOrViewCssIsolationFile + + + + AddOrViewCssIsolationFile + AddOrViewCssIsolationFile + + + + Add or View JavaScript Isolation File + Add or View JavaScript Isolation File + + + + AddOrViewJsIsolationFile + AddOrViewJsIsolationFile + + + + AddOrViewJsIsolationFile + AddOrViewJsIsolationFile + + Razor Syntax Visualizer Razor Syntax Visualizer diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.ru.xlf b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.ru.xlf index 78a450180fc..59450993693 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.ru.xlf +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.ru.xlf @@ -17,6 +17,51 @@ Show Referenced Tag Helpers + + Add or View C# Code-Behind File + Add or View C# Code-Behind File + + + + AddOrViewCsIsolationFile + AddOrViewCsIsolationFile + + + + AddOrViewCsIsolationFile + AddOrViewCsIsolationFile + + + + Add or View CSS Isolation File + Add or View CSS Isolation File + + + + AddOrViewCssIsolationFile + AddOrViewCssIsolationFile + + + + AddOrViewCssIsolationFile + AddOrViewCssIsolationFile + + + + Add or View JavaScript Isolation File + Add or View JavaScript Isolation File + + + + AddOrViewJsIsolationFile + AddOrViewJsIsolationFile + + + + AddOrViewJsIsolationFile + AddOrViewJsIsolationFile + + Razor Syntax Visualizer Razor Syntax Visualizer diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.tr.xlf b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.tr.xlf index 87fbdf7382d..39938843386 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.tr.xlf +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.tr.xlf @@ -17,6 +17,51 @@ Show Referenced Tag Helpers + + Add or View C# Code-Behind File + Add or View C# Code-Behind File + + + + AddOrViewCsIsolationFile + AddOrViewCsIsolationFile + + + + AddOrViewCsIsolationFile + AddOrViewCsIsolationFile + + + + Add or View CSS Isolation File + Add or View CSS Isolation File + + + + AddOrViewCssIsolationFile + AddOrViewCssIsolationFile + + + + AddOrViewCssIsolationFile + AddOrViewCssIsolationFile + + + + Add or View JavaScript Isolation File + Add or View JavaScript Isolation File + + + + AddOrViewJsIsolationFile + AddOrViewJsIsolationFile + + + + AddOrViewJsIsolationFile + AddOrViewJsIsolationFile + + Razor Syntax Visualizer Razor Syntax Visualizer diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.zh-Hans.xlf b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.zh-Hans.xlf index 21f68f07fa8..b5c4f7943cf 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.zh-Hans.xlf +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.zh-Hans.xlf @@ -17,6 +17,51 @@ Show Referenced Tag Helpers + + Add or View C# Code-Behind File + Add or View C# Code-Behind File + + + + AddOrViewCsIsolationFile + AddOrViewCsIsolationFile + + + + AddOrViewCsIsolationFile + AddOrViewCsIsolationFile + + + + Add or View CSS Isolation File + Add or View CSS Isolation File + + + + AddOrViewCssIsolationFile + AddOrViewCssIsolationFile + + + + AddOrViewCssIsolationFile + AddOrViewCssIsolationFile + + + + Add or View JavaScript Isolation File + Add or View JavaScript Isolation File + + + + AddOrViewJsIsolationFile + AddOrViewJsIsolationFile + + + + AddOrViewJsIsolationFile + AddOrViewJsIsolationFile + + Razor Syntax Visualizer Razor Syntax Visualizer diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.zh-Hant.xlf b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.zh-Hant.xlf index 39d52c85fe0..64424e0b5ff 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.zh-Hant.xlf +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.zh-Hant.xlf @@ -17,6 +17,51 @@ Show Referenced Tag Helpers + + Add or View C# Code-Behind File + Add or View C# Code-Behind File + + + + AddOrViewCsIsolationFile + AddOrViewCsIsolationFile + + + + AddOrViewCsIsolationFile + AddOrViewCsIsolationFile + + + + Add or View CSS Isolation File + Add or View CSS Isolation File + + + + AddOrViewCssIsolationFile + AddOrViewCssIsolationFile + + + + AddOrViewCssIsolationFile + AddOrViewCssIsolationFile + + + + Add or View JavaScript Isolation File + Add or View JavaScript Isolation File + + + + AddOrViewJsIsolationFile + AddOrViewJsIsolationFile + + + + AddOrViewJsIsolationFile + AddOrViewJsIsolationFile + + Razor Syntax Visualizer Razor Syntax Visualizer diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.cs.xlf b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.cs.xlf index 3ad5cba1c89..c371f5dc24a 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.cs.xlf +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.cs.xlf @@ -2,11 +2,41 @@ + + Add CSS Isolation File + Add CSS Isolation File + + + + Add C# Code-Behind File + Add C# Code-Behind File + + + + Add JavaScript Isolation File + Add JavaScript Isolation File + + LSP Razor Editor LSP Razor Editor + + View CSS Isolation File + View CSS Isolation File + + + + View C# Code-Behind File + View C# Code-Behind File + + + + View JavaScript Isolation File + View JavaScript Isolation File + + \ No newline at end of file diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.de.xlf b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.de.xlf index 6fa422528c6..0c65bffd549 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.de.xlf +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.de.xlf @@ -2,11 +2,41 @@ + + Add CSS Isolation File + Add CSS Isolation File + + + + Add C# Code-Behind File + Add C# Code-Behind File + + + + Add JavaScript Isolation File + Add JavaScript Isolation File + + LSP Razor Editor LSP-Razor-Editor + + View CSS Isolation File + View CSS Isolation File + + + + View C# Code-Behind File + View C# Code-Behind File + + + + View JavaScript Isolation File + View JavaScript Isolation File + + \ No newline at end of file diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.es.xlf b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.es.xlf index 70657327a7d..da1866ffa45 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.es.xlf +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.es.xlf @@ -2,11 +2,41 @@ + + Add CSS Isolation File + Add CSS Isolation File + + + + Add C# Code-Behind File + Add C# Code-Behind File + + + + Add JavaScript Isolation File + Add JavaScript Isolation File + + LSP Razor Editor LSP Razor Editor + + View CSS Isolation File + View CSS Isolation File + + + + View C# Code-Behind File + View C# Code-Behind File + + + + View JavaScript Isolation File + View JavaScript Isolation File + + \ No newline at end of file diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.fr.xlf b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.fr.xlf index ee1e31e99f7..1ae3ec5b639 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.fr.xlf +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.fr.xlf @@ -2,11 +2,41 @@ + + Add CSS Isolation File + Add CSS Isolation File + + + + Add C# Code-Behind File + Add C# Code-Behind File + + + + Add JavaScript Isolation File + Add JavaScript Isolation File + + LSP Razor Editor Éditeur Razor LSP + + View CSS Isolation File + View CSS Isolation File + + + + View C# Code-Behind File + View C# Code-Behind File + + + + View JavaScript Isolation File + View JavaScript Isolation File + + \ No newline at end of file diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.it.xlf b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.it.xlf index 3114eaeea5b..e7c1976fc26 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.it.xlf +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.it.xlf @@ -2,11 +2,41 @@ + + Add CSS Isolation File + Add CSS Isolation File + + + + Add C# Code-Behind File + Add C# Code-Behind File + + + + Add JavaScript Isolation File + Add JavaScript Isolation File + + LSP Razor Editor Editor LSP Razor + + View CSS Isolation File + View CSS Isolation File + + + + View C# Code-Behind File + View C# Code-Behind File + + + + View JavaScript Isolation File + View JavaScript Isolation File + + \ No newline at end of file diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.ja.xlf b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.ja.xlf index 178a63eae4c..a09bc084054 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.ja.xlf +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.ja.xlf @@ -2,11 +2,41 @@ + + Add CSS Isolation File + Add CSS Isolation File + + + + Add C# Code-Behind File + Add C# Code-Behind File + + + + Add JavaScript Isolation File + Add JavaScript Isolation File + + LSP Razor Editor LSP Razor エディター + + View CSS Isolation File + View CSS Isolation File + + + + View C# Code-Behind File + View C# Code-Behind File + + + + View JavaScript Isolation File + View JavaScript Isolation File + + \ No newline at end of file diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.ko.xlf b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.ko.xlf index 0fa91903d5a..f8b2748d753 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.ko.xlf +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.ko.xlf @@ -2,11 +2,41 @@ + + Add CSS Isolation File + Add CSS Isolation File + + + + Add C# Code-Behind File + Add C# Code-Behind File + + + + Add JavaScript Isolation File + Add JavaScript Isolation File + + LSP Razor Editor LSP Razor 편집기 + + View CSS Isolation File + View CSS Isolation File + + + + View C# Code-Behind File + View C# Code-Behind File + + + + View JavaScript Isolation File + View JavaScript Isolation File + + \ No newline at end of file diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.pl.xlf b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.pl.xlf index 5c13f9bb7bc..959fa81dbfe 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.pl.xlf +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.pl.xlf @@ -2,11 +2,41 @@ + + Add CSS Isolation File + Add CSS Isolation File + + + + Add C# Code-Behind File + Add C# Code-Behind File + + + + Add JavaScript Isolation File + Add JavaScript Isolation File + + LSP Razor Editor Edytor składni Razor LSP + + View CSS Isolation File + View CSS Isolation File + + + + View C# Code-Behind File + View C# Code-Behind File + + + + View JavaScript Isolation File + View JavaScript Isolation File + + \ No newline at end of file diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.pt-BR.xlf b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.pt-BR.xlf index 0643d627bef..2e140ecbd73 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.pt-BR.xlf +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.pt-BR.xlf @@ -2,11 +2,41 @@ + + Add CSS Isolation File + Add CSS Isolation File + + + + Add C# Code-Behind File + Add C# Code-Behind File + + + + Add JavaScript Isolation File + Add JavaScript Isolation File + + LSP Razor Editor Editor LSP Razor + + View CSS Isolation File + View CSS Isolation File + + + + View C# Code-Behind File + View C# Code-Behind File + + + + View JavaScript Isolation File + View JavaScript Isolation File + + \ No newline at end of file diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.ru.xlf b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.ru.xlf index 0396a970708..ea65d453e40 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.ru.xlf +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.ru.xlf @@ -2,11 +2,41 @@ + + Add CSS Isolation File + Add CSS Isolation File + + + + Add C# Code-Behind File + Add C# Code-Behind File + + + + Add JavaScript Isolation File + Add JavaScript Isolation File + + LSP Razor Editor Редактор Razor с поддержкой LSP + + View CSS Isolation File + View CSS Isolation File + + + + View C# Code-Behind File + View C# Code-Behind File + + + + View JavaScript Isolation File + View JavaScript Isolation File + + \ No newline at end of file diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.tr.xlf b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.tr.xlf index 9632813155a..8dbadf6088f 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.tr.xlf +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.tr.xlf @@ -2,11 +2,41 @@ + + Add CSS Isolation File + Add CSS Isolation File + + + + Add C# Code-Behind File + Add C# Code-Behind File + + + + Add JavaScript Isolation File + Add JavaScript Isolation File + + LSP Razor Editor LSP Razor Editor + + View CSS Isolation File + View CSS Isolation File + + + + View C# Code-Behind File + View C# Code-Behind File + + + + View JavaScript Isolation File + View JavaScript Isolation File + + \ No newline at end of file diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.zh-Hans.xlf b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.zh-Hans.xlf index d18c2d08b3c..c4423f7c412 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.zh-Hans.xlf +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.zh-Hans.xlf @@ -2,11 +2,41 @@ + + Add CSS Isolation File + Add CSS Isolation File + + + + Add C# Code-Behind File + Add C# Code-Behind File + + + + Add JavaScript Isolation File + Add JavaScript Isolation File + + LSP Razor Editor LSP Razor 编辑器 + + View CSS Isolation File + View CSS Isolation File + + + + View C# Code-Behind File + View C# Code-Behind File + + + + View JavaScript Isolation File + View JavaScript Isolation File + + \ No newline at end of file diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.zh-Hant.xlf b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.zh-Hant.xlf index 59fbf2123f9..65794cb9dcf 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.zh-Hant.xlf +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.zh-Hant.xlf @@ -2,11 +2,41 @@ + + Add CSS Isolation File + Add CSS Isolation File + + + + Add C# Code-Behind File + Add C# Code-Behind File + + + + Add JavaScript Isolation File + Add JavaScript Isolation File + + LSP Razor Editor LSP Razor 編輯器 + + View CSS Isolation File + View CSS Isolation File + + + + View C# Code-Behind File + View C# Code-Behind File + + + + View JavaScript Isolation File + View JavaScript Isolation File + + \ No newline at end of file From 1baa75bdc27915b714a0d2b1df3b64c52e4714e6 Mon Sep 17 00:00:00 2001 From: Todd Grunke Date: Tue, 31 Mar 2026 17:26:06 -0700 Subject: [PATCH 02/13] Address some of the easy feedback --- .../CssIsolationFileCommandHandler.cs | 3 +- .../IsolationFileCommandHandler.cs | 43 +++++++------------ .../JavascriptIsolationFileCommandHandler.cs | 3 +- .../RazorPackage.cs | 6 +-- 4 files changed, 22 insertions(+), 33 deletions(-) diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/IsolationFiles/CssIsolationFileCommandHandler.cs b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/IsolationFiles/CssIsolationFileCommandHandler.cs index 6e62d3fb858..e8f4676d614 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/IsolationFiles/CssIsolationFileCommandHandler.cs +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/IsolationFiles/CssIsolationFileCommandHandler.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using Microsoft.AspNetCore.Razor.Language; namespace Microsoft.VisualStudio.RazorExtension.IsolationFiles; @@ -17,7 +18,7 @@ internal sealed class CssIsolationFileCommandHandler(IServiceProvider servicePro protected override string GenerateFileContent(string razorFilePath, string componentOrViewName) { - var fileType = IsRazorComponentFile(razorFilePath) ? "component" : "view"; + var fileType = FileKinds.GetFileKindFromPath(razorFilePath).IsComponent() ? "component" : "view"; // For CSS isolation files, we create an empty CSS file with a comment return $$""" diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/IsolationFiles/IsolationFileCommandHandler.cs b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/IsolationFiles/IsolationFileCommandHandler.cs index 79dd17e4207..53562ff9e7f 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/IsolationFiles/IsolationFileCommandHandler.cs +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/IsolationFiles/IsolationFileCommandHandler.cs @@ -4,6 +4,7 @@ using System; using System.IO; using System.Runtime.InteropServices; +using Microsoft.AspNetCore.Razor.Language; using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Shell.Interop; @@ -43,20 +44,13 @@ internal abstract class IsolationFileCommandHandler(IServiceProvider serviceProv /// public void OnBeforeQueryStatus(object sender, EventArgs e) { - ThreadHelper.ThrowIfNotOnUIThread(); - if (sender is not OleMenuCommand command) { return; } - var contextGuid = RazorPackage.GuidRazorFileContext; - // Check if the Razor file context is active before doing expensive hierarchy queries - if (_serviceProvider.GetService(typeof(SVsShellMonitorSelection)) is not IVsMonitorSelection monitorSelection - || monitorSelection.GetCmdUIContextCookie(ref contextGuid, out var cookie) != VSConstants.S_OK - || monitorSelection.IsCmdUIContextActive(cookie, out var isActive) != VSConstants.S_OK - || isActive == 0 + if (!IsRazorFileUIContextActive() || GetSelectedRazorFilePath() is not string razorFilePath || !IsApplicable(razorFilePath)) { @@ -72,6 +66,18 @@ public void OnBeforeQueryStatus(object sender, EventArgs e) command.Text = isolationFileExists ? ViewText : AddText; } + private bool IsRazorFileUIContextActive() + { + ThreadHelper.ThrowIfNotOnUIThread(); + + var contextGuid = RazorPackage.GuidRazorFileContext; + + return _serviceProvider.GetService(typeof(SVsShellMonitorSelection)) is IVsMonitorSelection monitorSelection + && monitorSelection.GetCmdUIContextCookie(ref contextGuid, out var cookie) == VSConstants.S_OK + && monitorSelection.IsCmdUIContextActive(cookie, out var isActive) == VSConstants.S_OK + && isActive != 0; + } + /// /// Executes the command - either creates a new file or opens an existing one. /// @@ -178,7 +184,7 @@ private void AddFileToProject(string isolationFilePath, string razorFilePath) { project.GetMkDocument(itemId, out var filePath); - if (!string.IsNullOrEmpty(filePath) && IsRazorFile(filePath)) + if (!string.IsNullOrEmpty(filePath) && FileKinds.TryGetFileKindFromPath(filePath, out _)) { return filePath; } @@ -200,23 +206,4 @@ private void AddFileToProject(string isolationFilePath, string razorFilePath) } } } - - /// - /// Checks if a file is a Razor file (.razor or .cshtml). - /// - private static bool IsRazorFile(string filePath) - { - var extension = Path.GetExtension(filePath); - return string.Equals(extension, ".razor", StringComparison.OrdinalIgnoreCase) || - string.Equals(extension, ".cshtml", StringComparison.OrdinalIgnoreCase); - } - - /// - /// Checks if a file is a Razor file (.razor or .cshtml). - /// - protected static bool IsRazorComponentFile(string filePath) - { - var extension = Path.GetExtension(filePath); - return string.Equals(extension, ".razor", StringComparison.OrdinalIgnoreCase); - } } diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/IsolationFiles/JavascriptIsolationFileCommandHandler.cs b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/IsolationFiles/JavascriptIsolationFileCommandHandler.cs index fa6bd98d4ec..88f457c6991 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/IsolationFiles/JavascriptIsolationFileCommandHandler.cs +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/IsolationFiles/JavascriptIsolationFileCommandHandler.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using Microsoft.AspNetCore.Razor.Language; namespace Microsoft.VisualStudio.RazorExtension.IsolationFiles; @@ -17,7 +18,7 @@ internal sealed class JavascriptIsolationFileCommandHandler(IServiceProvider ser // JS isolation files are only applicable to Razor components (.razor), not MVC views (.cshtml). protected override bool IsApplicable(string razorFilePath) - => IsRazorComponentFile(razorFilePath); + => FileKinds.TryGetFileKindFromPath(razorFilePath, out _); protected override string GenerateFileContent(string razorFilePath, string componentOrViewName) { diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/RazorPackage.cs b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/RazorPackage.cs index 380f18e5e2f..236dbe49c39 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/RazorPackage.cs +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/RazorPackage.cs @@ -47,9 +47,9 @@ namespace Microsoft.VisualStudio.RazorExtension; [ProvideUIContextRule( contextGuid: GuidRazorFileContextString, name: "Razor File Selected", - expression: "RazorFile | CshtmlFile", - termNames: ["RazorFile", "CshtmlFile"], - termValues: ["HierSingleSelectionName:.razor$", "HierSingleSelectionName:.cshtml$"])] + expression: "DotNetCoreRazorProject & (RazorFile | CshtmlFile)", + termNames: ["DotNetCoreRazorProject", "RazorFile", "CshtmlFile"], + termValues: ["ActiveProjectCapability:DotNetCoreRazor", "HierSingleSelectionName:.razor$", "HierSingleSelectionName:.cshtml$"])] internal sealed class RazorPackage : AsyncPackage { public const string PackageGuidString = "13b72f58-279e-49e0-a56d-296be02f0805"; From 86a52b929715d8284cb31e9970782ae8c8d3103a Mon Sep 17 00:00:00 2001 From: Todd Grunke Date: Tue, 31 Mar 2026 18:07:10 -0700 Subject: [PATCH 03/13] Add LSP server endpoint for isolation file creation Add a Razor LSP endpoint and OOP remote service that generates and creates isolation files (CSS, C#, JS) for Razor components via workspace/applyEdit. --- eng/targets/Services.props | 1 + .../RazorLSPConstants.cs | 2 + .../IsolationFiles/IsolationFileKind.cs | 14 ++ .../Remote/IRemoteAddIsolationFileService.cs | 23 ++ .../Remote/RazorServices.cs | 1 + .../RemoteAddIsolationFileService.cs | 207 ++++++++++++++++++ .../Cohost/AddIsolationFileParams.cs | 26 +++ .../Cohost/CohostAddIsolationFileEndpoint.cs | 74 +++++++ 8 files changed, 348 insertions(+) create mode 100644 src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/IsolationFiles/IsolationFileKind.cs create mode 100644 src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Remote/IRemoteAddIsolationFileService.cs create mode 100644 src/Razor/src/Microsoft.CodeAnalysis.Remote.Razor/IsolationFiles/RemoteAddIsolationFileService.cs create mode 100644 src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/LanguageClient/Cohost/AddIsolationFileParams.cs create mode 100644 src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/LanguageClient/Cohost/CohostAddIsolationFileEndpoint.cs diff --git a/eng/targets/Services.props b/eng/targets/Services.props index 9cd98594a27..c21769a8a9a 100644 --- a/eng/targets/Services.props +++ b/eng/targets/Services.props @@ -37,6 +37,7 @@ + diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.CohostingShared/RazorLSPConstants.cs b/src/Razor/src/Microsoft.CodeAnalysis.Razor.CohostingShared/RazorLSPConstants.cs index ce46b54215b..634192d8a84 100644 --- a/src/Razor/src/Microsoft.CodeAnalysis.Razor.CohostingShared/RazorLSPConstants.cs +++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.CohostingShared/RazorLSPConstants.cs @@ -34,4 +34,6 @@ internal static class RazorLSPConstants public const string RoslynSemanticTokenRangesEndpointName = "roslyn/semanticTokenRanges"; public const string ApplyRenameEditName = "razor/applyRenameEdit"; + + public const string AddIsolationFileName = "razor/addIsolationFile"; } diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/IsolationFiles/IsolationFileKind.cs b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/IsolationFiles/IsolationFileKind.cs new file mode 100644 index 00000000000..64c51cf7d61 --- /dev/null +++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/IsolationFiles/IsolationFileKind.cs @@ -0,0 +1,14 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.CodeAnalysis.Razor.IsolationFiles; + +/// +/// Specifies which type of isolation file to create for a Razor component. +/// +internal static class IsolationFileKind +{ + public const string Css = "css"; + public const string CSharp = "csharp"; + public const string JavaScript = "javascript"; +} diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Remote/IRemoteAddIsolationFileService.cs b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Remote/IRemoteAddIsolationFileService.cs new file mode 100644 index 00000000000..23b1de42a3c --- /dev/null +++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Remote/IRemoteAddIsolationFileService.cs @@ -0,0 +1,23 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.ExternalAccess.Razor; + +namespace Microsoft.CodeAnalysis.Razor.Remote; + +internal interface IRemoteAddIsolationFileService : IRemoteJsonService +{ + /// + /// Creates an isolation file (CSS, C# code-behind, or JavaScript) for a Razor file. + /// Returns a containing CreateFile + TextDocumentEdit operations, + /// or null if the operation could not be completed. + /// + ValueTask AddIsolationFileAsync( + JsonSerializableRazorPinnedSolutionInfoWrapper solutionInfo, + Uri razorFileUri, + string fileKind, + CancellationToken cancellationToken); +} diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Remote/RazorServices.cs b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Remote/RazorServices.cs index 402806a3f49..9a2363a1333 100644 --- a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Remote/RazorServices.cs +++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Remote/RazorServices.cs @@ -47,6 +47,7 @@ internal static class RazorServices (typeof(IRemoteDiagnosticsService), null), (typeof(IRemoteCompletionService), null), (typeof(IRemoteCodeActionsService), null), + (typeof(IRemoteAddIsolationFileService), null), (typeof(IRemoteFindAllReferencesService), null), (typeof(IRemoteMEFInitializationService), null), (typeof(IRemoteCodeLensService), null), diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Remote.Razor/IsolationFiles/RemoteAddIsolationFileService.cs b/src/Razor/src/Microsoft.CodeAnalysis.Remote.Razor/IsolationFiles/RemoteAddIsolationFileService.cs new file mode 100644 index 00000000000..305d27ce169 --- /dev/null +++ b/src/Razor/src/Microsoft.CodeAnalysis.Remote.Razor/IsolationFiles/RemoteAddIsolationFileService.cs @@ -0,0 +1,207 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Razor.Language; +using Microsoft.AspNetCore.Razor.Language.Intermediate; +using Microsoft.AspNetCore.Razor.PooledObjects; +using Microsoft.CodeAnalysis.ExternalAccess.Razor; +using Microsoft.CodeAnalysis.Razor; +using Microsoft.CodeAnalysis.Razor.CodeActions; +using Microsoft.CodeAnalysis.Razor.IsolationFiles; +using Microsoft.CodeAnalysis.Razor.Logging; +using Microsoft.CodeAnalysis.Razor.Remote; +using Microsoft.CodeAnalysis.Razor.Utilities; +using Microsoft.CodeAnalysis.Razor.Workspaces; +using Microsoft.CodeAnalysis.Remote.Razor.ProjectSystem; + +namespace Microsoft.CodeAnalysis.Remote.Razor; + +internal sealed class RemoteAddIsolationFileService(in ServiceArgs args) + : RazorDocumentServiceBase(in args), IRemoteAddIsolationFileService +{ + internal sealed class Factory : FactoryBase + { + protected override IRemoteAddIsolationFileService CreateService(in ServiceArgs args) + => new RemoteAddIsolationFileService(in args); + } + + private readonly IRoslynCodeActionHelpers _roslynCodeActionHelpers = + args.ExportProvider.GetExportedValue(); + + private readonly LanguageServerFeatureOptions _languageServerFeatureOptions = + args.ExportProvider.GetExportedValue(); + + public ValueTask AddIsolationFileAsync( + JsonSerializableRazorPinnedSolutionInfoWrapper solutionInfo, + Uri razorFileUri, + string fileKind, + CancellationToken cancellationToken) + => RunServiceAsync( + solutionInfo, + solution => AddIsolationFileAsync(solution, razorFileUri, fileKind, cancellationToken), + cancellationToken); + + private async ValueTask AddIsolationFileAsync( + Solution solution, + Uri razorFileUri, + string fileKind, + CancellationToken cancellationToken) + { + if (!solution.TryGetRazorDocument(razorFileUri, out var razorDocument)) + { + Logger.LogWarning($"Could not find Razor document for URI: {razorFileUri}"); + return null; + } + + var documentContext = CreateRazorDocumentContext(solution, razorDocument.Id); + if (documentContext is null) + { + Logger.LogWarning($"Could not create document context for: {razorFileUri}"); + return null; + } + + var razorFilePath = FilePathNormalizer.Normalize(razorFileUri.GetAbsoluteOrUNCPath()); + var isolationFilePath = GetIsolationFilePath(razorFilePath, fileKind); + if (isolationFilePath is null) + { + return null; + } + + var isolationFileUri = LspFactory.CreateFilePathUri(isolationFilePath, _languageServerFeatureOptions); + + var content = await GenerateContentAsync( + fileKind, documentContext, razorFilePath, isolationFileUri, cancellationToken).ConfigureAwait(false); + + var isolationFileDocumentIdentifier = new OptionalVersionedTextDocumentIdentifier + { + DocumentUri = new DocumentUri(isolationFileUri) + }; + + var documentChanges = new SumType[] + { + new CreateFile { DocumentUri = isolationFileDocumentIdentifier.DocumentUri }, + new TextDocumentEdit + { + TextDocument = isolationFileDocumentIdentifier, + Edits = [LspFactory.CreateTextEdit(position: (0, 0), content)] + } + }; + + return new WorkspaceEdit + { + DocumentChanges = documentChanges, + }; + } + + private static string? GetIsolationFilePath(string razorFilePath, string fileKind) + { + return fileKind switch + { + IsolationFileKind.Css => razorFilePath + ".css", + IsolationFileKind.CSharp => razorFilePath + ".cs", + IsolationFileKind.JavaScript => razorFilePath + ".js", + _ => null + }; + } + + private async Task GenerateContentAsync( + string fileKind, + RemoteDocumentContext documentContext, + string razorFilePath, + Uri isolationFileUri, + CancellationToken cancellationToken) + { + return fileKind switch + { + IsolationFileKind.CSharp => await GenerateCSharpContentAsync( + documentContext, razorFilePath, isolationFileUri, cancellationToken).ConfigureAwait(false), + IsolationFileKind.Css => GenerateCssContent(razorFilePath), + IsolationFileKind.JavaScript => GenerateJavaScriptContent(razorFilePath), + _ => string.Empty + }; + } + + private static string GenerateCssContent(string razorFilePath) + { + var componentName = Path.GetFileNameWithoutExtension(razorFilePath); + var fileType = FileKinds.GetFileKindFromPath(razorFilePath).IsComponent() ? "component" : "view"; + return $"/* Scoped CSS styles for {componentName} {fileType} */\r\n"; + } + + private static string GenerateJavaScriptContent(string razorFilePath) + { + var componentName = Path.GetFileNameWithoutExtension(razorFilePath); + return $"// JavaScript isolation for {componentName} component\r\n"; + } + + private async Task GenerateCSharpContentAsync( + RemoteDocumentContext documentContext, + string razorFilePath, + Uri isolationFileUri, + CancellationToken cancellationToken) + { + var codeDocument = await documentContext.GetCodeDocumentAsync(cancellationToken).ConfigureAwait(false); + var className = Path.GetFileNameWithoutExtension(razorFilePath); + + // Use the Razor compiler's namespace resolution which handles @namespace directives, + // _Imports.razor, and SDK-provided root namespace + if (!codeDocument.TryGetNamespace(fallbackToRootNamespace: true, out var @namespace)) + { + Logger.LogWarning($"Could not determine namespace for: {razorFilePath}"); + @namespace = "Unknown"; + } + + var content = GenerateCodeBehindClass(className, @namespace, codeDocument); + + // Format via Roslyn (handles file-scoped namespaces, indentation, etc.) + content = await _roslynCodeActionHelpers.GetFormattedNewFileContentsAsync( + documentContext.Snapshot.Project, + isolationFileUri, + content, + cancellationToken).ConfigureAwait(false); + + return content; + } + + private static string GenerateCodeBehindClass(string className, string namespaceName, RazorCodeDocument razorCodeDocument) + { + using var _ = StringBuilderPool.GetPooledObject(out var builder); + + var usingDirectives = razorCodeDocument + .GetRequiredDocumentNode() + .FindDescendantNodes(); + + foreach (var usingDirective in usingDirectives) + { + builder.Append("using "); + + var content = usingDirective.Content; + var startIndex = content.StartsWith("global::", StringComparison.Ordinal) + ? 8 + : 0; + + builder.Append(content, startIndex, content.Length - startIndex); + builder.Append(';'); + builder.AppendLine(); + } + + builder.AppendLine(); + builder.Append("namespace "); + builder.AppendLine(namespaceName); + builder.Append('{'); + builder.AppendLine(); + builder.Append("public partial class "); + builder.AppendLine(className); + builder.Append('{'); + builder.AppendLine(); + builder.Append('}'); + builder.AppendLine(); + builder.Append('}'); + + return builder.ToString(); + } +} diff --git a/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/LanguageClient/Cohost/AddIsolationFileParams.cs b/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/LanguageClient/Cohost/AddIsolationFileParams.cs new file mode 100644 index 00000000000..22451de6038 --- /dev/null +++ b/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/LanguageClient/Cohost/AddIsolationFileParams.cs @@ -0,0 +1,26 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Text.Json.Serialization; +using Microsoft.CodeAnalysis.Razor.IsolationFiles; + +namespace Microsoft.VisualStudio.Razor.LanguageClient.Cohost; + +/// +/// Parameters for the razor/addIsolationFile endpoint. +/// +internal sealed class AddIsolationFileParams +{ + /// + /// The URI of the Razor file (.razor or .cshtml) to create an isolation file for. + /// + [JsonPropertyName("razorFileUri")] + public required Uri RazorFileUri { get; set; } + + /// + /// The kind of isolation file to create (one of constants). + /// + [JsonPropertyName("fileKind")] + public required string FileKind { get; set; } +} diff --git a/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/LanguageClient/Cohost/CohostAddIsolationFileEndpoint.cs b/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/LanguageClient/Cohost/CohostAddIsolationFileEndpoint.cs new file mode 100644 index 00000000000..742750ccc00 --- /dev/null +++ b/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/LanguageClient/Cohost/CohostAddIsolationFileEndpoint.cs @@ -0,0 +1,74 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Composition; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Razor; +using Microsoft.CodeAnalysis.ExternalAccess.Razor; +using Microsoft.CodeAnalysis.ExternalAccess.Razor.Cohost; +using Microsoft.CodeAnalysis.ExternalAccess.Razor.Features; +using Microsoft.CodeAnalysis.Razor.Logging; +using Microsoft.CodeAnalysis.Razor.Remote; + +namespace Microsoft.VisualStudio.Razor.LanguageClient.Cohost; + +#pragma warning disable RS0030 // Do not use banned APIs +[Shared] +[RazorMethod(RazorLSPConstants.AddIsolationFileName)] +[ExportRazorStatelessLspService(typeof(CohostAddIsolationFileEndpoint))] +[method: ImportingConstructor] +#pragma warning restore RS0030 // Do not use banned APIs +internal sealed class CohostAddIsolationFileEndpoint( + IRemoteServiceInvoker remoteServiceInvoker, + ILoggerFactory loggerFactory) + : AbstractRazorCohostRequestHandler +{ + private readonly IRemoteServiceInvoker _remoteServiceInvoker = remoteServiceInvoker; + private readonly ILogger _logger = loggerFactory.GetOrCreateLogger(); + + protected override bool MutatesSolutionState => true; + + protected override bool RequiresLSPSolution => true; + + protected override async Task HandleRequestAsync( + AddIsolationFileParams request, + RazorCohostRequestContext context, + CancellationToken cancellationToken) + { + var solution = context.Solution; + if (solution is null) + { + _logger.LogWarning($"No solution available for addIsolationFile request."); + return new(); + } + + var workspaceEdit = await _remoteServiceInvoker.TryInvokeAsync( + solution, + (service, solutionInfo, ct) => service.AddIsolationFileAsync( + solutionInfo, + request.RazorFileUri, + request.FileKind, + ct), + cancellationToken).ConfigureAwait(false); + + if (workspaceEdit is null) + { + _logger.LogWarning($"Remote service returned no edit for addIsolationFile."); + return new(); + } + + var razorCohostClientLanguageServerManager = context.GetRequiredService(); + var response = await razorCohostClientLanguageServerManager.SendRequestAsync( + Methods.WorkspaceApplyEditName, + new ApplyWorkspaceEditParams { Edit = workspaceEdit }, + cancellationToken).ConfigureAwait(false); + + if (!response.Applied) + { + _logger.LogWarning($"Failed to apply workspace edit for addIsolationFile: {response.FailureReason}"); + } + + return new(); + } +} From 028fb6052c166595531cba530cdd26c71aad5a9f Mon Sep 17 00:00:00 2001 From: Todd Grunke Date: Tue, 31 Mar 2026 18:35:54 -0700 Subject: [PATCH 04/13] Simplify VS-side command handlers to delegate to LSP endpoint Replace DTE-based file generation in the VS command handlers with thin LSP client calls to the new isolation file endpoint. Save the file after creation to avoid a dirty buffer in the editor. --- .../CSharpIsolationFileCommandHandler.cs | 76 +---------- .../CssIsolationFileCommandHandler.cs | 20 +-- .../IsolationFileCommandHandler.cs | 124 ++++++++++-------- .../JavascriptIsolationFileCommandHandler.cs | 16 +-- .../RazorPackage.cs | 10 +- 5 files changed, 92 insertions(+), 154 deletions(-) diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/IsolationFiles/CSharpIsolationFileCommandHandler.cs b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/IsolationFiles/CSharpIsolationFileCommandHandler.cs index 649851a6c13..1edd37e7132 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/IsolationFiles/CSharpIsolationFileCommandHandler.cs +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/IsolationFiles/CSharpIsolationFileCommandHandler.cs @@ -2,84 +2,20 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.IO; -using Microsoft.VisualStudio.Shell; +using Microsoft.CodeAnalysis.Razor.IsolationFiles; +using Microsoft.VisualStudio.Razor.ProjectSystem; namespace Microsoft.VisualStudio.RazorExtension.IsolationFiles; /// /// Command handler for C# code-behind isolation files (.razor.cs). /// -internal sealed class CSharpIsolationFileCommandHandler(IServiceProvider serviceProvider) - : IsolationFileCommandHandler(serviceProvider, ".cs") +internal sealed class CSharpIsolationFileCommandHandler( + IServiceProvider serviceProvider, + Lazy requestInvoker) + : IsolationFileCommandHandler(serviceProvider, ".cs", IsolationFileKind.CSharp, requestInvoker) { protected override string AddText => Resources.Add_Code_Behind_File; protected override string ViewText => Resources.View_Code_Behind_File; - - protected override string GenerateFileContent(string razorFilePath, string componentOrViewName) - { - // Get namespace from the project's RootNamespace + relative path, - // matching how the Razor compiler resolves namespaces. - var namespaceName = GetNamespaceForRazorFile(razorFilePath) ?? "MyApp"; - - return $$""" - namespace {{namespaceName}} - { - public partial class {{componentOrViewName}} - { - } - } - """; - } - - /// - /// Gets the namespace for a Razor file by reading the project's RootNamespace property - /// and computing the relative path, matching the Razor compiler's namespace resolution. - /// - private string? GetNamespaceForRazorFile(string razorFilePath) - { - ThreadHelper.ThrowIfNotOnUIThread(); - - try - { - var projectItem = FindProjectItem(razorFilePath); - if (projectItem?.ContainingProject is EnvDTE.Project project) - { - var rootNamespace = project.Properties.Item("RootNamespace")?.Value as string; - if (!rootNamespace.IsNullOrEmpty()) - { - if (Path.GetDirectoryName(project.FullName) is not string projectDir - || Path.GetDirectoryName(razorFilePath) is not string fileDir - || !fileDir.StartsWith(projectDir, StringComparison.OrdinalIgnoreCase) - || (fileDir.Length > projectDir.Length - && fileDir[projectDir.Length] != Path.DirectorySeparatorChar - && fileDir[projectDir.Length] != Path.AltDirectorySeparatorChar)) - { - return rootNamespace; - } - - var relativePath = fileDir[projectDir.Length..] - .TrimStart(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); - - if (relativePath.IsNullOrEmpty()) - { - return rootNamespace; - } - - // Convert path separators to dots, matching how the Razor compiler computes namespaces - var relativeNamespace = relativePath - .Replace(Path.DirectorySeparatorChar, '.') - .Replace(Path.AltDirectorySeparatorChar, '.'); - - return rootNamespace + "." + relativeNamespace; - } - } - } - catch - { - } - - return null; - } } diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/IsolationFiles/CssIsolationFileCommandHandler.cs b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/IsolationFiles/CssIsolationFileCommandHandler.cs index e8f4676d614..be803759a5a 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/IsolationFiles/CssIsolationFileCommandHandler.cs +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/IsolationFiles/CssIsolationFileCommandHandler.cs @@ -2,28 +2,20 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using Microsoft.AspNetCore.Razor.Language; +using Microsoft.CodeAnalysis.Razor.IsolationFiles; +using Microsoft.VisualStudio.Razor.ProjectSystem; namespace Microsoft.VisualStudio.RazorExtension.IsolationFiles; /// /// Command handler for CSS isolation files (.razor.css). /// -internal sealed class CssIsolationFileCommandHandler(IServiceProvider serviceProvider) - : IsolationFileCommandHandler(serviceProvider, ".css") +internal sealed class CssIsolationFileCommandHandler( + IServiceProvider serviceProvider, + Lazy requestInvoker) + : IsolationFileCommandHandler(serviceProvider, ".css", IsolationFileKind.Css, requestInvoker) { protected override string AddText => Resources.Add_CSS_Isolation_File; protected override string ViewText => Resources.View_CSS_Isolation_File; - - protected override string GenerateFileContent(string razorFilePath, string componentOrViewName) - { - var fileType = FileKinds.GetFileKindFromPath(razorFilePath).IsComponent() ? "component" : "view"; - - // For CSS isolation files, we create an empty CSS file with a comment - return $$""" - /* Scoped CSS styles for {{componentOrViewName}} {{fileType}} */ - - """; - } } diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/IsolationFiles/IsolationFileCommandHandler.cs b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/IsolationFiles/IsolationFileCommandHandler.cs index 53562ff9e7f..eeb2d139e94 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/IsolationFiles/IsolationFileCommandHandler.cs +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/IsolationFiles/IsolationFileCommandHandler.cs @@ -4,7 +4,12 @@ using System; using System.IO; using System.Runtime.InteropServices; +using System.Text.Json.Serialization; +using System.Threading; +using System.Threading.Tasks; using Microsoft.AspNetCore.Razor.Language; +using Microsoft.VisualStudio.Razor.LanguageClient; +using Microsoft.VisualStudio.Razor.ProjectSystem; using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Shell.Interop; @@ -12,11 +17,19 @@ namespace Microsoft.VisualStudio.RazorExtension.IsolationFiles; /// /// Base class for handling Add/View isolation file commands for Razor documents. +/// When the file doesn't exist, sends an LSP request to the Razor language server +/// to create it via workspace/applyEdit. When the file exists, just opens it. /// -internal abstract class IsolationFileCommandHandler(IServiceProvider serviceProvider, string fileExtension) +internal abstract class IsolationFileCommandHandler( + IServiceProvider serviceProvider, + string fileExtension, + string fileKind, + Lazy requestInvoker) { private readonly IServiceProvider _serviceProvider = serviceProvider; private readonly string _fileExtension = fileExtension; + private readonly string _fileKind = fileKind; + private readonly Lazy _requestInvoker = requestInvoker; /// /// Gets the display text when the file doesn't exist (e.g., "Add CSS Isolation File"). @@ -28,11 +41,6 @@ internal abstract class IsolationFileCommandHandler(IServiceProvider serviceProv /// protected abstract string ViewText { get; } - /// - /// Generates the default content for a new isolation file. - /// - protected abstract string GenerateFileContent(string razorFilePath, string componentOrViewName); - /// /// Returns whether this command is applicable for the given Razor file. /// Override to hide the command for certain file types. @@ -79,7 +87,8 @@ private bool IsRazorFileUIContextActive() } /// - /// Executes the command - either creates a new file or opens an existing one. + /// Executes the command - either opens an existing isolation file or creates a new one + /// via the LSP server and then opens it. /// public void Execute(object sender, EventArgs e) { @@ -92,68 +101,56 @@ public void Execute(object sender, EventArgs e) var isolationFilePath = GetIsolationFilePath(razorFilePath); - if (!File.Exists(isolationFilePath)) + if (File.Exists(isolationFilePath)) { - // Create the file - CreateIsolationFile(razorFilePath, isolationFilePath); + // View: just open the existing file + VsShellUtilities.OpenDocument(_serviceProvider, isolationFilePath); + } + else + { + // Add: send LSP request to create the file, then open it. + // FileAndForget ensures exceptions are reported to telemetry rather than silently swallowed. +#pragma warning disable VSSDK007 // Fire-and-forget from synchronous EventHandler is intentional + ThreadHelper.JoinableTaskFactory.RunAsync( + () => CreateAndOpenIsolationFileAsync(razorFilePath, isolationFilePath, CancellationToken.None)).FileAndForget("IsolationFileCommandHandler.Execute"); +#pragma warning restore VSSDK007 } - - // Open the file - VsShellUtilities.OpenDocument(_serviceProvider, isolationFilePath); - } - - /// - /// Gets the path to the isolation file based on the Razor file path. - /// - protected virtual string GetIsolationFilePath(string razorFilePath) - { - return razorFilePath + _fileExtension; } - /// - /// Creates the isolation file with appropriate content. - /// - private void CreateIsolationFile(string razorFilePath, string isolationFilePath) + private async Task CreateAndOpenIsolationFileAsync( + string razorFilePath, + string isolationFilePath, + CancellationToken cancellationToken) { - var componentName = Path.GetFileNameWithoutExtension(razorFilePath); - var content = GenerateFileContent(razorFilePath, componentName); + // The cohost endpoint will create the file via workspace/applyEdit. + // By the time this returns, the file should exist on disk. + await _requestInvoker.Value.ReinvokeRequestOnServerAsync( + RazorLSPConstants.AddIsolationFileName, + RazorLSPConstants.RoslynLanguageServerName, + new AddIsolationFileRequest + { + RazorFileUri = new Uri(razorFilePath), + FileKind = _fileKind, + }, + cancellationToken); - // Write the file - File.WriteAllText(isolationFilePath, content); + if (File.Exists(isolationFilePath)) + { + await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); - // Add to project - AddFileToProject(isolationFilePath, razorFilePath); + // The workspace/applyEdit creates the file and inserts content via TextDocumentEdit, + // which leaves the buffer dirty. Save it so the user sees a clean document. + VsShellUtilities.SaveFileIfDirty(_serviceProvider, isolationFilePath); + VsShellUtilities.OpenDocument(_serviceProvider, isolationFilePath); + } } /// - /// Adds the newly created file to the project. + /// Gets the path to the isolation file based on the Razor file path. /// - private void AddFileToProject(string isolationFilePath, string razorFilePath) - { - ThreadHelper.ThrowIfNotOnUIThread(); - - try - { - if (FindProjectItem(razorFilePath) is EnvDTE.ProjectItem razorProjectItem) - { - // Add the isolation file as a nested item under the Razor file - razorProjectItem.ProjectItems.AddFromFile(isolationFilePath); - } - } - catch - { - // If adding to project fails, the file still exists on disk - // which is better than nothing - } - } - - protected EnvDTE.ProjectItem? FindProjectItem(string filePath) + protected virtual string GetIsolationFilePath(string razorFilePath) { - ThreadHelper.ThrowIfNotOnUIThread(); - - var dte = (EnvDTE.DTE?)_serviceProvider.GetService(typeof(EnvDTE.DTE)); - - return dte?.Solution.FindProjectItem(filePath); + return razorFilePath + _fileExtension; } /// @@ -206,4 +203,17 @@ private void AddFileToProject(string isolationFilePath, string razorFilePath) } } } + + /// + /// Local request type matching AddIsolationFileParams on the server side. + /// JSON property names must match the server's expected format. + /// + private sealed class AddIsolationFileRequest + { + [JsonPropertyName("razorFileUri")] + public required Uri RazorFileUri { get; set; } + + [JsonPropertyName("fileKind")] + public required string FileKind { get; set; } + } } diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/IsolationFiles/JavascriptIsolationFileCommandHandler.cs b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/IsolationFiles/JavascriptIsolationFileCommandHandler.cs index 88f457c6991..c206cd69f2e 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/IsolationFiles/JavascriptIsolationFileCommandHandler.cs +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/IsolationFiles/JavascriptIsolationFileCommandHandler.cs @@ -3,14 +3,18 @@ using System; using Microsoft.AspNetCore.Razor.Language; +using Microsoft.CodeAnalysis.Razor.IsolationFiles; +using Microsoft.VisualStudio.Razor.ProjectSystem; namespace Microsoft.VisualStudio.RazorExtension.IsolationFiles; /// /// Command handler for JavaScript isolation files (.razor.js). /// -internal sealed class JavascriptIsolationFileCommandHandler(IServiceProvider serviceProvider) - : IsolationFileCommandHandler(serviceProvider, ".js") +internal sealed class JavascriptIsolationFileCommandHandler( + IServiceProvider serviceProvider, + Lazy requestInvoker) + : IsolationFileCommandHandler(serviceProvider, ".js", IsolationFileKind.JavaScript, requestInvoker) { protected override string AddText => Resources.Add_JavaScript_Isolation_File; @@ -19,12 +23,4 @@ internal sealed class JavascriptIsolationFileCommandHandler(IServiceProvider ser // JS isolation files are only applicable to Razor components (.razor), not MVC views (.cshtml). protected override bool IsApplicable(string razorFilePath) => FileKinds.TryGetFileKindFromPath(razorFilePath, out _); - - protected override string GenerateFileContent(string razorFilePath, string componentOrViewName) - { - // For JavaScript isolation files, we create an empty js file with a comment - return $$""" - // JavaScript isolation for {{componentOrViewName}} component - """; - } } diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/RazorPackage.cs b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/RazorPackage.cs index 236dbe49c39..d0cc103c88c 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/RazorPackage.cs +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/RazorPackage.cs @@ -14,6 +14,7 @@ using Microsoft.VisualStudio.Razor.Debugging; using Microsoft.VisualStudio.Razor.LanguageClient.Options; using Microsoft.VisualStudio.Razor.Logging; +using Microsoft.VisualStudio.Razor.ProjectSystem; using Microsoft.VisualStudio.Razor.Snippets; using Microsoft.VisualStudio.RazorExtension.IsolationFiles; using Microsoft.VisualStudio.RazorExtension.Snippets; @@ -166,10 +167,13 @@ private void RegisterIsolationFileCommands(OleMenuCommandService mcs) { ThreadHelper.ThrowIfNotOnUIThread(); + var componentModel = (IComponentModel)GetGlobalService(typeof(SComponentModel)); + var requestInvoker = new Lazy(() => componentModel.GetService()); + // Create command handlers - var cssHandler = new CssIsolationFileCommandHandler(this); - var csharpHandler = new CSharpIsolationFileCommandHandler(this); - var javascriptHandler = new JavascriptIsolationFileCommandHandler(this); + var cssHandler = new CssIsolationFileCommandHandler(this, requestInvoker); + var csharpHandler = new CSharpIsolationFileCommandHandler(this, requestInvoker); + var javascriptHandler = new JavascriptIsolationFileCommandHandler(this, requestInvoker); // CSS Isolation File Command var cssCommandId = new CommandID(GuidRazorIsolationFilesCmdSet, (int)CmdIdAddOrViewCssIsolationFile); From 8d8323fc7eba937d69b24bdc7df34112b7bca9d0 Mon Sep 17 00:00:00 2001 From: Todd Grunke Date: Wed, 1 Apr 2026 06:01:16 -0700 Subject: [PATCH 05/13] Add unit tests for RemoteAddIsolationFileService Add tests covering CSS, C#, and JS isolation file generation including content validation and namespace resolution. --- .../RemoteAddIsolationFileServiceTest.cs | 193 ++++++++++++++++++ ...lysis.Razor.CohostingShared.Test.projitems | 1 + 2 files changed, 194 insertions(+) create mode 100644 src/Razor/test/Microsoft.CodeAnalysis.Razor.CohostingShared.Test/IsolationFiles/RemoteAddIsolationFileServiceTest.cs diff --git a/src/Razor/test/Microsoft.CodeAnalysis.Razor.CohostingShared.Test/IsolationFiles/RemoteAddIsolationFileServiceTest.cs b/src/Razor/test/Microsoft.CodeAnalysis.Razor.CohostingShared.Test/IsolationFiles/RemoteAddIsolationFileServiceTest.cs new file mode 100644 index 00000000000..1c1baf943a8 --- /dev/null +++ b/src/Razor/test/Microsoft.CodeAnalysis.Razor.CohostingShared.Test/IsolationFiles/RemoteAddIsolationFileServiceTest.cs @@ -0,0 +1,193 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Razor.IsolationFiles; +using Microsoft.CodeAnalysis.Razor.Remote; +using Xunit; +using Xunit.Abstractions; + +namespace Microsoft.VisualStudio.Razor.LanguageClient.Cohost.IsolationFiles; + +public class RemoteAddIsolationFileServiceTest(ITestOutputHelper testOutputHelper) : CohostEndpointTestBase(testOutputHelper) +{ + [Fact] + public async Task CssIsolationFile_CreatesEmptyFile() + { + var document = CreateProjectAndRazorDocument("
"); + var razorFileUri = new Uri(document.FilePath!); + + var result = await RemoteServiceInvoker.TryInvokeAsync( + document.Project.Solution, + (service, solutionInfo, ct) => service.AddIsolationFileAsync(solutionInfo, razorFileUri, IsolationFileKind.Css, ct), + DisposalToken); + + Assert.NotNull(result); + var changes = GetDocumentChanges(result); + Assert.Equal(2, changes.Length); + + // First change: CreateFile + Assert.True(changes[0].TryGetSecond(out var createFile)); + Assert.Contains(".razor.css", createFile!.DocumentUri.ToString()); + + // Second change: TextDocumentEdit with CSS comment content + Assert.True(changes[1].TryGetFirst(out var textEdit)); + Assert.Single(textEdit!.Edits); + Assert.Contains("Scoped CSS styles for File1 component", ((TextEdit)textEdit.Edits[0]).NewText); + } + + [Fact] + public async Task JavaScriptIsolationFile_CreatesFileWithTemplate() + { + var document = CreateProjectAndRazorDocument("
"); + var razorFileUri = new Uri(document.FilePath!); + + var result = await RemoteServiceInvoker.TryInvokeAsync( + document.Project.Solution, + (service, solutionInfo, ct) => service.AddIsolationFileAsync(solutionInfo, razorFileUri, IsolationFileKind.JavaScript, ct), + DisposalToken); + + Assert.NotNull(result); + var changes = GetDocumentChanges(result); + Assert.Equal(2, changes.Length); + + // First change: CreateFile + Assert.True(changes[0].TryGetSecond(out var createFile)); + Assert.Contains(".razor.js", createFile!.DocumentUri.ToString()); + + // Second change: TextDocumentEdit with JS comment content + Assert.True(changes[1].TryGetFirst(out var textEdit)); + Assert.Single(textEdit!.Edits); + var content = ((TextEdit)textEdit.Edits[0]).NewText; + Assert.Contains("JavaScript isolation for File1 component", content); + } + + [Fact] + public async Task CSharpIsolationFile_GeneratesCodeBehind() + { + var document = CreateProjectAndRazorDocument("
"); + var razorFileUri = new Uri(document.FilePath!); + + var result = await RemoteServiceInvoker.TryInvokeAsync( + document.Project.Solution, + (service, solutionInfo, ct) => service.AddIsolationFileAsync(solutionInfo, razorFileUri, IsolationFileKind.CSharp, ct), + DisposalToken); + + Assert.NotNull(result); + var changes = GetDocumentChanges(result); + Assert.Equal(2, changes.Length); + + // First change: CreateFile with .razor.cs extension + Assert.True(changes[0].TryGetSecond(out var createFile)); + Assert.Contains(".razor.cs", createFile!.DocumentUri.ToString()); + + // Second change: TextDocumentEdit with C# content + Assert.True(changes[1].TryGetFirst(out var textEdit)); + Assert.Single(textEdit!.Edits); + var content = ((TextEdit)textEdit.Edits[0]).NewText; + + // Should contain namespace from test project (SomeProject) + Assert.Contains("namespace SomeProject", content); + // Should contain partial class matching the file name + Assert.Contains("partial class File1", content); + } + + [Fact] + public async Task CSharpIsolationFile_WithNamespaceDirective() + { + var document = CreateProjectAndRazorDocument(""" + @namespace My.Custom.Namespace + +
+ """); + var razorFileUri = new Uri(document.FilePath!); + + var result = await RemoteServiceInvoker.TryInvokeAsync( + document.Project.Solution, + (service, solutionInfo, ct) => service.AddIsolationFileAsync(solutionInfo, razorFileUri, IsolationFileKind.CSharp, ct), + DisposalToken); + + Assert.NotNull(result); + var changes = GetDocumentChanges(result); + Assert.True(changes[1].TryGetFirst(out var textEdit)); + var content = ((TextEdit)textEdit!.Edits[0]).NewText; + + // Should use the @namespace directive value + Assert.Contains("namespace My.Custom.Namespace", content); + Assert.Contains("partial class File1", content); + } + + [Fact] + public async Task CSharpIsolationFile_IncludesUsingDirectives() + { + var document = CreateProjectAndRazorDocument( + """ + @using System.Diagnostics + +
+ """); + var razorFileUri = new Uri(document.FilePath!); + + var result = await RemoteServiceInvoker.TryInvokeAsync( + document.Project.Solution, + (service, solutionInfo, ct) => service.AddIsolationFileAsync(solutionInfo, razorFileUri, IsolationFileKind.CSharp, ct), + DisposalToken); + + Assert.NotNull(result); + var changes = GetDocumentChanges(result); + Assert.True(changes[1].TryGetFirst(out var textEdit)); + var content = ((TextEdit)textEdit!.Edits[0]).NewText; + + // The @using directive from the Razor file should appear in the generated code-behind. + // The test infrastructure may include default imports from _Imports.razor. + Assert.Contains("partial class File1", content); + Assert.Contains("namespace SomeProject", content); + } + + [Fact] + public async Task CshtmlFile_CssIsolation() + { + var document = CreateProjectAndRazorDocument( + "
", + fileKind: AspNetCore.Razor.Language.RazorFileKind.Legacy, + documentFilePath: FilePath("Page1.cshtml")); + var razorFileUri = new Uri(document.FilePath!); + + var result = await RemoteServiceInvoker.TryInvokeAsync( + document.Project.Solution, + (service, solutionInfo, ct) => service.AddIsolationFileAsync(solutionInfo, razorFileUri, IsolationFileKind.Css, ct), + DisposalToken); + + Assert.NotNull(result); + var changes = GetDocumentChanges(result); + Assert.True(changes[0].TryGetSecond(out var createFile)); + Assert.Contains(".cshtml.css", createFile!.DocumentUri.ToString()); + + // .cshtml files should say "view" not "component" + Assert.True(changes[1].TryGetFirst(out var textEdit)); + Assert.Contains("Page1 view", ((TextEdit)textEdit!.Edits[0]).NewText); + } + + [Fact] + public async Task InvalidFileKind_ReturnsNull() + { + var document = CreateProjectAndRazorDocument("
"); + var razorFileUri = new Uri(document.FilePath!); + + var result = await RemoteServiceInvoker.TryInvokeAsync( + document.Project.Solution, + (service, solutionInfo, ct) => service.AddIsolationFileAsync(solutionInfo, razorFileUri, "invalid", ct), + DisposalToken); + + Assert.Null(result); + } + + private static SumType[] GetDocumentChanges(WorkspaceEdit edit) + { + Assert.NotNull(edit.DocumentChanges); + Assert.True(edit.DocumentChanges.Value.TryGetSecond(out var changes), + "Expected DocumentChanges to contain SumType[] (not TextDocumentEdit[])"); + return changes; + } +} diff --git a/src/Razor/test/Microsoft.CodeAnalysis.Razor.CohostingShared.Test/Microsoft.CodeAnalysis.Razor.CohostingShared.Test.projitems b/src/Razor/test/Microsoft.CodeAnalysis.Razor.CohostingShared.Test/Microsoft.CodeAnalysis.Razor.CohostingShared.Test.projitems index d4a52d8b442..ca1b93d67db 100644 --- a/src/Razor/test/Microsoft.CodeAnalysis.Razor.CohostingShared.Test/Microsoft.CodeAnalysis.Razor.CohostingShared.Test.projitems +++ b/src/Razor/test/Microsoft.CodeAnalysis.Razor.CohostingShared.Test/Microsoft.CodeAnalysis.Razor.CohostingShared.Test.projitems @@ -54,6 +54,7 @@ + From 065812dc00be33e79d9b15f2fa9be7ef13ee4125 Mon Sep 17 00:00:00 2001 From: Todd Grunke Date: Wed, 1 Apr 2026 10:04:34 -0700 Subject: [PATCH 06/13] Rename from Isolation => Nested --- eng/targets/Services.props | 2 +- .../RazorLSPConstants.cs | 2 +- .../NestedFileKind.cs} | 6 +- ...vice.cs => IRemoteAddNestedFileService.cs} | 6 +- .../Remote/RazorServices.cs | 2 +- ...Microsoft.CodeAnalysis.Remote.Razor.csproj | 2 +- .../RemoteAddNestedFileService.cs} | 61 ++++++++++--------- ...onFileParams.cs => AddNestedFileParams.cs} | 10 +-- ...oint.cs => CohostAddNestedFileEndpoint.cs} | 22 +++---- .../CSharpIsolationFileCommandHandler.cs | 21 ------- .../CssIsolationFileCommandHandler.cs | 21 ------- .../CSharpNestedFileCommandHandler.cs | 21 +++++++ .../CssNestedFileCommandHandler.cs | 21 +++++++ .../JavascriptNestedFileCommandHandler.cs} | 16 ++--- .../NestedFileCommandHandler.cs} | 52 ++++++++-------- .../RazorContextMenu.vsct | 60 +++++++++--------- .../RazorPackage.cs | 40 ++++++------ .../Resources.resx | 26 ++++---- .../xlf/RazorContextMenu.vsct.cs.xlf | 50 +++++++-------- .../xlf/RazorContextMenu.vsct.de.xlf | 50 +++++++-------- .../xlf/RazorContextMenu.vsct.es.xlf | 50 +++++++-------- .../xlf/RazorContextMenu.vsct.fr.xlf | 50 +++++++-------- .../xlf/RazorContextMenu.vsct.it.xlf | 50 +++++++-------- .../xlf/RazorContextMenu.vsct.ja.xlf | 50 +++++++-------- .../xlf/RazorContextMenu.vsct.ko.xlf | 50 +++++++-------- .../xlf/RazorContextMenu.vsct.pl.xlf | 50 +++++++-------- .../xlf/RazorContextMenu.vsct.pt-BR.xlf | 50 +++++++-------- .../xlf/RazorContextMenu.vsct.ru.xlf | 50 +++++++-------- .../xlf/RazorContextMenu.vsct.tr.xlf | 50 +++++++-------- .../xlf/RazorContextMenu.vsct.zh-Hans.xlf | 50 +++++++-------- .../xlf/RazorContextMenu.vsct.zh-Hant.xlf | 50 +++++++-------- .../xlf/Resources.cs.xlf | 36 +++++------ .../xlf/Resources.de.xlf | 36 +++++------ .../xlf/Resources.es.xlf | 36 +++++------ .../xlf/Resources.fr.xlf | 36 +++++------ .../xlf/Resources.it.xlf | 36 +++++------ .../xlf/Resources.ja.xlf | 36 +++++------ .../xlf/Resources.ko.xlf | 36 +++++------ .../xlf/Resources.pl.xlf | 36 +++++------ .../xlf/Resources.pt-BR.xlf | 36 +++++------ .../xlf/Resources.ru.xlf | 36 +++++------ .../xlf/Resources.tr.xlf | 36 +++++------ .../xlf/Resources.zh-Hans.xlf | 36 +++++------ .../xlf/Resources.zh-Hant.xlf | 36 +++++------ ...lysis.Razor.CohostingShared.Test.projitems | 2 +- .../RemoteAddNestedFileServiceTest.cs} | 50 +++++++-------- 46 files changed, 781 insertions(+), 780 deletions(-) rename src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/{IsolationFiles/IsolationFileKind.cs => NestedFiles/NestedFileKind.cs} (64%) rename src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Remote/{IRemoteAddIsolationFileService.cs => IRemoteAddNestedFileService.cs} (75%) rename src/Razor/src/Microsoft.CodeAnalysis.Remote.Razor/{IsolationFiles/RemoteAddIsolationFileService.cs => NestedFiles/RemoteAddNestedFileService.cs} (72%) rename src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/LanguageClient/Cohost/{AddIsolationFileParams.cs => AddNestedFileParams.cs} (68%) rename src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/LanguageClient/Cohost/{CohostAddIsolationFileEndpoint.cs => CohostAddNestedFileEndpoint.cs} (78%) delete mode 100644 src/Razor/src/Microsoft.VisualStudio.RazorExtension/IsolationFiles/CSharpIsolationFileCommandHandler.cs delete mode 100644 src/Razor/src/Microsoft.VisualStudio.RazorExtension/IsolationFiles/CssIsolationFileCommandHandler.cs create mode 100644 src/Razor/src/Microsoft.VisualStudio.RazorExtension/NestedFiles/CSharpNestedFileCommandHandler.cs create mode 100644 src/Razor/src/Microsoft.VisualStudio.RazorExtension/NestedFiles/CssNestedFileCommandHandler.cs rename src/Razor/src/Microsoft.VisualStudio.RazorExtension/{IsolationFiles/JavascriptIsolationFileCommandHandler.cs => NestedFiles/JavascriptNestedFileCommandHandler.cs} (56%) rename src/Razor/src/Microsoft.VisualStudio.RazorExtension/{IsolationFiles/IsolationFileCommandHandler.cs => NestedFiles/NestedFileCommandHandler.cs} (79%) rename src/Razor/test/Microsoft.CodeAnalysis.Razor.CohostingShared.Test/{IsolationFiles/RemoteAddIsolationFileServiceTest.cs => NestedFiles/RemoteAddNestedFileServiceTest.cs} (75%) diff --git a/eng/targets/Services.props b/eng/targets/Services.props index c21769a8a9a..eaaf0039015 100644 --- a/eng/targets/Services.props +++ b/eng/targets/Services.props @@ -37,7 +37,7 @@ - + diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.CohostingShared/RazorLSPConstants.cs b/src/Razor/src/Microsoft.CodeAnalysis.Razor.CohostingShared/RazorLSPConstants.cs index 634192d8a84..455dffb3879 100644 --- a/src/Razor/src/Microsoft.CodeAnalysis.Razor.CohostingShared/RazorLSPConstants.cs +++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.CohostingShared/RazorLSPConstants.cs @@ -35,5 +35,5 @@ internal static class RazorLSPConstants public const string ApplyRenameEditName = "razor/applyRenameEdit"; - public const string AddIsolationFileName = "razor/addIsolationFile"; + public const string AddNestedFileName = "razor/addNestedFile"; } diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/IsolationFiles/IsolationFileKind.cs b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/NestedFiles/NestedFileKind.cs similarity index 64% rename from src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/IsolationFiles/IsolationFileKind.cs rename to src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/NestedFiles/NestedFileKind.cs index 64c51cf7d61..dc8a655cd72 100644 --- a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/IsolationFiles/IsolationFileKind.cs +++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/NestedFiles/NestedFileKind.cs @@ -1,12 +1,12 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -namespace Microsoft.CodeAnalysis.Razor.IsolationFiles; +namespace Microsoft.CodeAnalysis.Razor.NestedFiles; /// -/// Specifies which type of isolation file to create for a Razor component. +/// Specifies which type of nested file to create for a Razor component. /// -internal static class IsolationFileKind +internal static class NestedFileKind { public const string Css = "css"; public const string CSharp = "csharp"; diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Remote/IRemoteAddIsolationFileService.cs b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Remote/IRemoteAddNestedFileService.cs similarity index 75% rename from src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Remote/IRemoteAddIsolationFileService.cs rename to src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Remote/IRemoteAddNestedFileService.cs index 23b1de42a3c..a05769504ca 100644 --- a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Remote/IRemoteAddIsolationFileService.cs +++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Remote/IRemoteAddNestedFileService.cs @@ -8,14 +8,14 @@ namespace Microsoft.CodeAnalysis.Razor.Remote; -internal interface IRemoteAddIsolationFileService : IRemoteJsonService +internal interface IRemoteAddNestedFileService : IRemoteJsonService { /// - /// Creates an isolation file (CSS, C# code-behind, or JavaScript) for a Razor file. + /// Creates a nested file (CSS, C# code-behind, or JavaScript) for a Razor file. /// Returns a containing CreateFile + TextDocumentEdit operations, /// or null if the operation could not be completed. /// - ValueTask AddIsolationFileAsync( + ValueTask AddNestedFileAsync( JsonSerializableRazorPinnedSolutionInfoWrapper solutionInfo, Uri razorFileUri, string fileKind, diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Remote/RazorServices.cs b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Remote/RazorServices.cs index 9a2363a1333..e7b2fe3a848 100644 --- a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Remote/RazorServices.cs +++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Remote/RazorServices.cs @@ -47,7 +47,7 @@ internal static class RazorServices (typeof(IRemoteDiagnosticsService), null), (typeof(IRemoteCompletionService), null), (typeof(IRemoteCodeActionsService), null), - (typeof(IRemoteAddIsolationFileService), null), + (typeof(IRemoteAddNestedFileService), null), (typeof(IRemoteFindAllReferencesService), null), (typeof(IRemoteMEFInitializationService), null), (typeof(IRemoteCodeLensService), null), diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Remote.Razor/Microsoft.CodeAnalysis.Remote.Razor.csproj b/src/Razor/src/Microsoft.CodeAnalysis.Remote.Razor/Microsoft.CodeAnalysis.Remote.Razor.csproj index 809e7efa340..91a1e3aec53 100644 --- a/src/Razor/src/Microsoft.CodeAnalysis.Remote.Razor/Microsoft.CodeAnalysis.Remote.Razor.csproj +++ b/src/Razor/src/Microsoft.CodeAnalysis.Remote.Razor/Microsoft.CodeAnalysis.Remote.Razor.csproj @@ -84,7 +84,7 @@ <_ExcludedFiles Include="$(PublishDir)**\Microsoft.CodeAnalysis.Remote.Razor.CoreComponents.*" /> <_PublishedFiles Include="$(PublishDir)**\Microsoft.CodeAnalysis.Razor.*" /> - <_PublishedFiles Include="$(PublishDir)**\Microsoft.CodeAnalysis.Remote.Razor.*" Exclude="@(_ExcludedFiles)"/> + <_PublishedFiles Include="$(PublishDir)**\Microsoft.CodeAnalysis.Remote.Razor.*" Exclude="@(_ExcludedFiles)" /> <_PublishedFiles Include="$(PublishDir)**\Microsoft.AspNetCore.*" /> <_PublishedFiles Include="$(PublishDir)**\Microsoft.Extensions.ObjectPool.dll" /> diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Remote.Razor/IsolationFiles/RemoteAddIsolationFileService.cs b/src/Razor/src/Microsoft.CodeAnalysis.Remote.Razor/NestedFiles/RemoteAddNestedFileService.cs similarity index 72% rename from src/Razor/src/Microsoft.CodeAnalysis.Remote.Razor/IsolationFiles/RemoteAddIsolationFileService.cs rename to src/Razor/src/Microsoft.CodeAnalysis.Remote.Razor/NestedFiles/RemoteAddNestedFileService.cs index 305d27ce169..435360213bc 100644 --- a/src/Razor/src/Microsoft.CodeAnalysis.Remote.Razor/IsolationFiles/RemoteAddIsolationFileService.cs +++ b/src/Razor/src/Microsoft.CodeAnalysis.Remote.Razor/NestedFiles/RemoteAddNestedFileService.cs @@ -11,8 +11,8 @@ using Microsoft.CodeAnalysis.ExternalAccess.Razor; using Microsoft.CodeAnalysis.Razor; using Microsoft.CodeAnalysis.Razor.CodeActions; -using Microsoft.CodeAnalysis.Razor.IsolationFiles; using Microsoft.CodeAnalysis.Razor.Logging; +using Microsoft.CodeAnalysis.Razor.NestedFiles; using Microsoft.CodeAnalysis.Razor.Remote; using Microsoft.CodeAnalysis.Razor.Utilities; using Microsoft.CodeAnalysis.Razor.Workspaces; @@ -20,13 +20,13 @@ namespace Microsoft.CodeAnalysis.Remote.Razor; -internal sealed class RemoteAddIsolationFileService(in ServiceArgs args) - : RazorDocumentServiceBase(in args), IRemoteAddIsolationFileService +internal sealed class RemoteAddNestedFileService(in ServiceArgs args) + : RazorDocumentServiceBase(in args), IRemoteAddNestedFileService { - internal sealed class Factory : FactoryBase + internal sealed class Factory : FactoryBase { - protected override IRemoteAddIsolationFileService CreateService(in ServiceArgs args) - => new RemoteAddIsolationFileService(in args); + protected override IRemoteAddNestedFileService CreateService(in ServiceArgs args) + => new RemoteAddNestedFileService(in args); } private readonly IRoslynCodeActionHelpers _roslynCodeActionHelpers = @@ -35,17 +35,17 @@ protected override IRemoteAddIsolationFileService CreateService(in ServiceArgs a private readonly LanguageServerFeatureOptions _languageServerFeatureOptions = args.ExportProvider.GetExportedValue(); - public ValueTask AddIsolationFileAsync( + public ValueTask AddNestedFileAsync( JsonSerializableRazorPinnedSolutionInfoWrapper solutionInfo, Uri razorFileUri, string fileKind, CancellationToken cancellationToken) => RunServiceAsync( solutionInfo, - solution => AddIsolationFileAsync(solution, razorFileUri, fileKind, cancellationToken), + solution => AddNestedFileAsync(solution, razorFileUri, fileKind, cancellationToken), cancellationToken); - private async ValueTask AddIsolationFileAsync( + private async ValueTask AddNestedFileAsync( Solution solution, Uri razorFileUri, string fileKind, @@ -65,28 +65,28 @@ protected override IRemoteAddIsolationFileService CreateService(in ServiceArgs a } var razorFilePath = FilePathNormalizer.Normalize(razorFileUri.GetAbsoluteOrUNCPath()); - var isolationFilePath = GetIsolationFilePath(razorFilePath, fileKind); - if (isolationFilePath is null) + var nestedFilePath = GetNestedFilePath(razorFilePath, fileKind); + if (nestedFilePath is null) { return null; } - var isolationFileUri = LspFactory.CreateFilePathUri(isolationFilePath, _languageServerFeatureOptions); + var nestedFileUri = LspFactory.CreateFilePathUri(nestedFilePath, _languageServerFeatureOptions); var content = await GenerateContentAsync( - fileKind, documentContext, razorFilePath, isolationFileUri, cancellationToken).ConfigureAwait(false); + fileKind, documentContext, razorFilePath, nestedFileUri, cancellationToken).ConfigureAwait(false); - var isolationFileDocumentIdentifier = new OptionalVersionedTextDocumentIdentifier + var nestedFileDocumentIdentifier = new OptionalVersionedTextDocumentIdentifier { - DocumentUri = new DocumentUri(isolationFileUri) + DocumentUri = new DocumentUri(nestedFileUri) }; var documentChanges = new SumType[] { - new CreateFile { DocumentUri = isolationFileDocumentIdentifier.DocumentUri }, + new CreateFile { DocumentUri = nestedFileDocumentIdentifier.DocumentUri }, new TextDocumentEdit { - TextDocument = isolationFileDocumentIdentifier, + TextDocument = nestedFileDocumentIdentifier, Edits = [LspFactory.CreateTextEdit(position: (0, 0), content)] } }; @@ -97,13 +97,13 @@ protected override IRemoteAddIsolationFileService CreateService(in ServiceArgs a }; } - private static string? GetIsolationFilePath(string razorFilePath, string fileKind) + private static string? GetNestedFilePath(string razorFilePath, string fileKind) { return fileKind switch { - IsolationFileKind.Css => razorFilePath + ".css", - IsolationFileKind.CSharp => razorFilePath + ".cs", - IsolationFileKind.JavaScript => razorFilePath + ".js", + NestedFileKind.Css => razorFilePath + ".css", + NestedFileKind.CSharp => razorFilePath + ".cs", + NestedFileKind.JavaScript => razorFilePath + ".js", _ => null }; } @@ -112,15 +112,15 @@ private async Task GenerateContentAsync( string fileKind, RemoteDocumentContext documentContext, string razorFilePath, - Uri isolationFileUri, + Uri nestedFileUri, CancellationToken cancellationToken) { return fileKind switch { - IsolationFileKind.CSharp => await GenerateCSharpContentAsync( - documentContext, razorFilePath, isolationFileUri, cancellationToken).ConfigureAwait(false), - IsolationFileKind.Css => GenerateCssContent(razorFilePath), - IsolationFileKind.JavaScript => GenerateJavaScriptContent(razorFilePath), + NestedFileKind.CSharp => await GenerateCSharpContentAsync( + documentContext, razorFilePath, nestedFileUri, cancellationToken).ConfigureAwait(false), + NestedFileKind.Css => GenerateCssContent(razorFilePath), + NestedFileKind.JavaScript => GenerateJavaScriptContent(razorFilePath), _ => string.Empty }; } @@ -129,19 +129,20 @@ private static string GenerateCssContent(string razorFilePath) { var componentName = Path.GetFileNameWithoutExtension(razorFilePath); var fileType = FileKinds.GetFileKindFromPath(razorFilePath).IsComponent() ? "component" : "view"; - return $"/* Scoped CSS styles for {componentName} {fileType} */\r\n"; + return $"/* CSS for {componentName} {fileType} */\r\n"; } private static string GenerateJavaScriptContent(string razorFilePath) { var componentName = Path.GetFileNameWithoutExtension(razorFilePath); - return $"// JavaScript isolation for {componentName} component\r\n"; + var fileType = FileKinds.GetFileKindFromPath(razorFilePath).IsComponent() ? "component" : "view"; + return $"// JavaScript for {componentName} {fileType}\r\n"; } private async Task GenerateCSharpContentAsync( RemoteDocumentContext documentContext, string razorFilePath, - Uri isolationFileUri, + Uri nestedFileUri, CancellationToken cancellationToken) { var codeDocument = await documentContext.GetCodeDocumentAsync(cancellationToken).ConfigureAwait(false); @@ -160,7 +161,7 @@ private async Task GenerateCSharpContentAsync( // Format via Roslyn (handles file-scoped namespaces, indentation, etc.) content = await _roslynCodeActionHelpers.GetFormattedNewFileContentsAsync( documentContext.Snapshot.Project, - isolationFileUri, + nestedFileUri, content, cancellationToken).ConfigureAwait(false); diff --git a/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/LanguageClient/Cohost/AddIsolationFileParams.cs b/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/LanguageClient/Cohost/AddNestedFileParams.cs similarity index 68% rename from src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/LanguageClient/Cohost/AddIsolationFileParams.cs rename to src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/LanguageClient/Cohost/AddNestedFileParams.cs index 22451de6038..75e5400100b 100644 --- a/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/LanguageClient/Cohost/AddIsolationFileParams.cs +++ b/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/LanguageClient/Cohost/AddNestedFileParams.cs @@ -3,23 +3,23 @@ using System; using System.Text.Json.Serialization; -using Microsoft.CodeAnalysis.Razor.IsolationFiles; +using Microsoft.CodeAnalysis.Razor.NestedFiles; namespace Microsoft.VisualStudio.Razor.LanguageClient.Cohost; /// -/// Parameters for the razor/addIsolationFile endpoint. +/// Parameters for the razor/addNestedFile endpoint. /// -internal sealed class AddIsolationFileParams +internal sealed class AddNestedFileParams { /// - /// The URI of the Razor file (.razor or .cshtml) to create an isolation file for. + /// The URI of the Razor file (.razor or .cshtml) to create a nested file for. /// [JsonPropertyName("razorFileUri")] public required Uri RazorFileUri { get; set; } /// - /// The kind of isolation file to create (one of constants). + /// The kind of nested file to create (one of constants). /// [JsonPropertyName("fileKind")] public required string FileKind { get; set; } diff --git a/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/LanguageClient/Cohost/CohostAddIsolationFileEndpoint.cs b/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/LanguageClient/Cohost/CohostAddNestedFileEndpoint.cs similarity index 78% rename from src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/LanguageClient/Cohost/CohostAddIsolationFileEndpoint.cs rename to src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/LanguageClient/Cohost/CohostAddNestedFileEndpoint.cs index 742750ccc00..47dde2a969b 100644 --- a/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/LanguageClient/Cohost/CohostAddIsolationFileEndpoint.cs +++ b/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/LanguageClient/Cohost/CohostAddNestedFileEndpoint.cs @@ -15,37 +15,37 @@ namespace Microsoft.VisualStudio.Razor.LanguageClient.Cohost; #pragma warning disable RS0030 // Do not use banned APIs [Shared] -[RazorMethod(RazorLSPConstants.AddIsolationFileName)] -[ExportRazorStatelessLspService(typeof(CohostAddIsolationFileEndpoint))] +[RazorMethod(RazorLSPConstants.AddNestedFileName)] +[ExportRazorStatelessLspService(typeof(CohostAddNestedFileEndpoint))] [method: ImportingConstructor] #pragma warning restore RS0030 // Do not use banned APIs -internal sealed class CohostAddIsolationFileEndpoint( +internal sealed class CohostAddNestedFileEndpoint( IRemoteServiceInvoker remoteServiceInvoker, ILoggerFactory loggerFactory) - : AbstractRazorCohostRequestHandler + : AbstractRazorCohostRequestHandler { private readonly IRemoteServiceInvoker _remoteServiceInvoker = remoteServiceInvoker; - private readonly ILogger _logger = loggerFactory.GetOrCreateLogger(); + private readonly ILogger _logger = loggerFactory.GetOrCreateLogger(); protected override bool MutatesSolutionState => true; protected override bool RequiresLSPSolution => true; protected override async Task HandleRequestAsync( - AddIsolationFileParams request, + AddNestedFileParams request, RazorCohostRequestContext context, CancellationToken cancellationToken) { var solution = context.Solution; if (solution is null) { - _logger.LogWarning($"No solution available for addIsolationFile request."); + _logger.LogWarning($"No solution available for addNestedFile request."); return new(); } - var workspaceEdit = await _remoteServiceInvoker.TryInvokeAsync( + var workspaceEdit = await _remoteServiceInvoker.TryInvokeAsync( solution, - (service, solutionInfo, ct) => service.AddIsolationFileAsync( + (service, solutionInfo, ct) => service.AddNestedFileAsync( solutionInfo, request.RazorFileUri, request.FileKind, @@ -54,7 +54,7 @@ protected override async Task HandleRequestAsync( if (workspaceEdit is null) { - _logger.LogWarning($"Remote service returned no edit for addIsolationFile."); + _logger.LogWarning($"Remote service returned no edit for addNestedFile."); return new(); } @@ -66,7 +66,7 @@ protected override async Task HandleRequestAsync( if (!response.Applied) { - _logger.LogWarning($"Failed to apply workspace edit for addIsolationFile: {response.FailureReason}"); + _logger.LogWarning($"Failed to apply workspace edit for addNestedFile: {response.FailureReason}"); } return new(); diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/IsolationFiles/CSharpIsolationFileCommandHandler.cs b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/IsolationFiles/CSharpIsolationFileCommandHandler.cs deleted file mode 100644 index 1edd37e7132..00000000000 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/IsolationFiles/CSharpIsolationFileCommandHandler.cs +++ /dev/null @@ -1,21 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using Microsoft.CodeAnalysis.Razor.IsolationFiles; -using Microsoft.VisualStudio.Razor.ProjectSystem; - -namespace Microsoft.VisualStudio.RazorExtension.IsolationFiles; - -/// -/// Command handler for C# code-behind isolation files (.razor.cs). -/// -internal sealed class CSharpIsolationFileCommandHandler( - IServiceProvider serviceProvider, - Lazy requestInvoker) - : IsolationFileCommandHandler(serviceProvider, ".cs", IsolationFileKind.CSharp, requestInvoker) -{ - protected override string AddText => Resources.Add_Code_Behind_File; - - protected override string ViewText => Resources.View_Code_Behind_File; -} diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/IsolationFiles/CssIsolationFileCommandHandler.cs b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/IsolationFiles/CssIsolationFileCommandHandler.cs deleted file mode 100644 index be803759a5a..00000000000 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/IsolationFiles/CssIsolationFileCommandHandler.cs +++ /dev/null @@ -1,21 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using Microsoft.CodeAnalysis.Razor.IsolationFiles; -using Microsoft.VisualStudio.Razor.ProjectSystem; - -namespace Microsoft.VisualStudio.RazorExtension.IsolationFiles; - -/// -/// Command handler for CSS isolation files (.razor.css). -/// -internal sealed class CssIsolationFileCommandHandler( - IServiceProvider serviceProvider, - Lazy requestInvoker) - : IsolationFileCommandHandler(serviceProvider, ".css", IsolationFileKind.Css, requestInvoker) -{ - protected override string AddText => Resources.Add_CSS_Isolation_File; - - protected override string ViewText => Resources.View_CSS_Isolation_File; -} diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/NestedFiles/CSharpNestedFileCommandHandler.cs b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/NestedFiles/CSharpNestedFileCommandHandler.cs new file mode 100644 index 00000000000..2d23411bb1f --- /dev/null +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/NestedFiles/CSharpNestedFileCommandHandler.cs @@ -0,0 +1,21 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using Microsoft.CodeAnalysis.Razor.NestedFiles; +using Microsoft.VisualStudio.Razor.ProjectSystem; + +namespace Microsoft.VisualStudio.RazorExtension.NestedFiles; + +/// +/// Command handler for C# code-behind nested files (.razor.cs). +/// +internal sealed class CSharpNestedFileCommandHandler( + IServiceProvider serviceProvider, + Lazy requestInvoker) + : NestedFileCommandHandler(serviceProvider, ".cs", NestedFileKind.CSharp, requestInvoker) +{ + protected override string AddText => Resources.Add_CS_Nested_File; + + protected override string ViewText => Resources.View_CS_Nested_File; +} diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/NestedFiles/CssNestedFileCommandHandler.cs b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/NestedFiles/CssNestedFileCommandHandler.cs new file mode 100644 index 00000000000..9c9e6d63c0d --- /dev/null +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/NestedFiles/CssNestedFileCommandHandler.cs @@ -0,0 +1,21 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using Microsoft.CodeAnalysis.Razor.NestedFiles; +using Microsoft.VisualStudio.Razor.ProjectSystem; + +namespace Microsoft.VisualStudio.RazorExtension.NestedFiles; + +/// +/// Command handler for CSS nested files (.razor.css). +/// +internal sealed class CssNestedFileCommandHandler( + IServiceProvider serviceProvider, + Lazy requestInvoker) + : NestedFileCommandHandler(serviceProvider, ".css", NestedFileKind.Css, requestInvoker) +{ + protected override string AddText => Resources.Add_CSS_Nested_File; + + protected override string ViewText => Resources.View_CSS_Nested_File; +} diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/IsolationFiles/JavascriptIsolationFileCommandHandler.cs b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/NestedFiles/JavascriptNestedFileCommandHandler.cs similarity index 56% rename from src/Razor/src/Microsoft.VisualStudio.RazorExtension/IsolationFiles/JavascriptIsolationFileCommandHandler.cs rename to src/Razor/src/Microsoft.VisualStudio.RazorExtension/NestedFiles/JavascriptNestedFileCommandHandler.cs index c206cd69f2e..7bd9127c064 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/IsolationFiles/JavascriptIsolationFileCommandHandler.cs +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/NestedFiles/JavascriptNestedFileCommandHandler.cs @@ -3,24 +3,24 @@ using System; using Microsoft.AspNetCore.Razor.Language; -using Microsoft.CodeAnalysis.Razor.IsolationFiles; +using Microsoft.CodeAnalysis.Razor.NestedFiles; using Microsoft.VisualStudio.Razor.ProjectSystem; -namespace Microsoft.VisualStudio.RazorExtension.IsolationFiles; +namespace Microsoft.VisualStudio.RazorExtension.NestedFiles; /// -/// Command handler for JavaScript isolation files (.razor.js). +/// Command handler for JavaScript nested files (.razor.js). /// -internal sealed class JavascriptIsolationFileCommandHandler( +internal sealed class JavascriptNestedFileCommandHandler( IServiceProvider serviceProvider, Lazy requestInvoker) - : IsolationFileCommandHandler(serviceProvider, ".js", IsolationFileKind.JavaScript, requestInvoker) + : NestedFileCommandHandler(serviceProvider, ".js", NestedFileKind.JavaScript, requestInvoker) { - protected override string AddText => Resources.Add_JavaScript_Isolation_File; + protected override string AddText => Resources.Add_JavaScript_Nested_File; - protected override string ViewText => Resources.View_JavaScript_Isolation_File; + protected override string ViewText => Resources.View_JavaScript_Nested_File; - // JS isolation files are only applicable to Razor components (.razor), not MVC views (.cshtml). + // JS nested files are only applicable to Razor components (.razor), not MVC views (.cshtml). protected override bool IsApplicable(string razorFilePath) => FileKinds.TryGetFileKindFromPath(razorFilePath, out _); } diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/IsolationFiles/IsolationFileCommandHandler.cs b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/NestedFiles/NestedFileCommandHandler.cs similarity index 79% rename from src/Razor/src/Microsoft.VisualStudio.RazorExtension/IsolationFiles/IsolationFileCommandHandler.cs rename to src/Razor/src/Microsoft.VisualStudio.RazorExtension/NestedFiles/NestedFileCommandHandler.cs index eeb2d139e94..4dac78d5307 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/IsolationFiles/IsolationFileCommandHandler.cs +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/NestedFiles/NestedFileCommandHandler.cs @@ -13,14 +13,14 @@ using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Shell.Interop; -namespace Microsoft.VisualStudio.RazorExtension.IsolationFiles; +namespace Microsoft.VisualStudio.RazorExtension.NestedFiles; /// -/// Base class for handling Add/View isolation file commands for Razor documents. +/// Base class for handling Add/View nested file commands for Razor documents. /// When the file doesn't exist, sends an LSP request to the Razor language server /// to create it via workspace/applyEdit. When the file exists, just opens it. /// -internal abstract class IsolationFileCommandHandler( +internal abstract class NestedFileCommandHandler( IServiceProvider serviceProvider, string fileExtension, string fileKind, @@ -32,12 +32,12 @@ internal abstract class IsolationFileCommandHandler( private readonly Lazy _requestInvoker = requestInvoker; /// - /// Gets the display text when the file doesn't exist (e.g., "Add CSS Isolation File"). + /// Gets the display text when the file doesn't exist (e.g., "Add Home.razor.css"). /// protected abstract string AddText { get; } /// - /// Gets the display text when the file exists (e.g., "View CSS Isolation File"). + /// Gets the display text when the file exists (e.g., "View Home.razor.css"). /// protected abstract string ViewText { get; } @@ -48,7 +48,7 @@ internal abstract class IsolationFileCommandHandler( protected virtual bool IsApplicable(string razorFilePath) => true; /// - /// Configures the command status and text based on whether the isolation file exists. + /// Configures the command status and text based on whether the nested file exists. /// public void OnBeforeQueryStatus(object sender, EventArgs e) { @@ -66,12 +66,12 @@ public void OnBeforeQueryStatus(object sender, EventArgs e) return; } - var isolationFilePath = GetIsolationFilePath(razorFilePath); - var isolationFileExists = File.Exists(isolationFilePath); + var nestedFilePath = GetNestedFilePath(razorFilePath); + var nestedFileExists = File.Exists(nestedFilePath); command.Visible = true; command.Enabled = true; - command.Text = isolationFileExists ? ViewText : AddText; + command.Text = nestedFileExists ? ViewText : AddText; } private bool IsRazorFileUIContextActive() @@ -87,7 +87,7 @@ private bool IsRazorFileUIContextActive() } /// - /// Executes the command - either opens an existing isolation file or creates a new one + /// Executes the command - either opens an existing nested file or creates a new one /// via the LSP server and then opens it. /// public void Execute(object sender, EventArgs e) @@ -99,12 +99,12 @@ public void Execute(object sender, EventArgs e) return; } - var isolationFilePath = GetIsolationFilePath(razorFilePath); + var nestedFilePath = GetNestedFilePath(razorFilePath); - if (File.Exists(isolationFilePath)) + if (File.Exists(nestedFilePath)) { // View: just open the existing file - VsShellUtilities.OpenDocument(_serviceProvider, isolationFilePath); + VsShellUtilities.OpenDocument(_serviceProvider, nestedFilePath); } else { @@ -112,43 +112,43 @@ public void Execute(object sender, EventArgs e) // FileAndForget ensures exceptions are reported to telemetry rather than silently swallowed. #pragma warning disable VSSDK007 // Fire-and-forget from synchronous EventHandler is intentional ThreadHelper.JoinableTaskFactory.RunAsync( - () => CreateAndOpenIsolationFileAsync(razorFilePath, isolationFilePath, CancellationToken.None)).FileAndForget("IsolationFileCommandHandler.Execute"); + () => CreateAndOpenNestedFileAsync(razorFilePath, nestedFilePath, CancellationToken.None)).FileAndForget("NestedFileCommandHandler.Execute"); #pragma warning restore VSSDK007 } } - private async Task CreateAndOpenIsolationFileAsync( + private async Task CreateAndOpenNestedFileAsync( string razorFilePath, - string isolationFilePath, + string nestedFilePath, CancellationToken cancellationToken) { // The cohost endpoint will create the file via workspace/applyEdit. // By the time this returns, the file should exist on disk. - await _requestInvoker.Value.ReinvokeRequestOnServerAsync( - RazorLSPConstants.AddIsolationFileName, + await _requestInvoker.Value.ReinvokeRequestOnServerAsync( + RazorLSPConstants.AddNestedFileName, RazorLSPConstants.RoslynLanguageServerName, - new AddIsolationFileRequest + new AddNestedFileRequest { RazorFileUri = new Uri(razorFilePath), FileKind = _fileKind, }, cancellationToken); - if (File.Exists(isolationFilePath)) + if (File.Exists(nestedFilePath)) { await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); // The workspace/applyEdit creates the file and inserts content via TextDocumentEdit, // which leaves the buffer dirty. Save it so the user sees a clean document. - VsShellUtilities.SaveFileIfDirty(_serviceProvider, isolationFilePath); - VsShellUtilities.OpenDocument(_serviceProvider, isolationFilePath); + VsShellUtilities.SaveFileIfDirty(_serviceProvider, nestedFilePath); + VsShellUtilities.OpenDocument(_serviceProvider, nestedFilePath); } } /// - /// Gets the path to the isolation file based on the Razor file path. + /// Gets the path to the nested file based on the Razor file path. /// - protected virtual string GetIsolationFilePath(string razorFilePath) + protected virtual string GetNestedFilePath(string razorFilePath) { return razorFilePath + _fileExtension; } @@ -205,10 +205,10 @@ protected virtual string GetIsolationFilePath(string razorFilePath) } /// - /// Local request type matching AddIsolationFileParams on the server side. + /// Local request type matching AddNestedFileParams on the server side. /// JSON property names must match the server's expected format. /// - private sealed class AddIsolationFileRequest + private sealed class AddNestedFileRequest { [JsonPropertyName("razorFileUri")] public required Uri RazorFileUri { get; set; } diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/RazorContextMenu.vsct b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/RazorContextMenu.vsct index 8df37420876..778b532947a 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/RazorContextMenu.vsct +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/RazorContextMenu.vsct @@ -23,12 +23,12 @@ - - - - - - + + + + + + @@ -42,8 +42,8 @@ - - + + @@ -111,53 +111,53 @@ - - - - - + - - - + + + diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/RazorPackage.cs b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/RazorPackage.cs index d0cc103c88c..8bea13b634c 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/RazorPackage.cs +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/RazorPackage.cs @@ -16,7 +16,7 @@ using Microsoft.VisualStudio.Razor.Logging; using Microsoft.VisualStudio.Razor.ProjectSystem; using Microsoft.VisualStudio.Razor.Snippets; -using Microsoft.VisualStudio.RazorExtension.IsolationFiles; +using Microsoft.VisualStudio.RazorExtension.NestedFiles; using Microsoft.VisualStudio.RazorExtension.Snippets; using Microsoft.VisualStudio.RazorExtension.SyntaxVisualizer; using Microsoft.VisualStudio.Shell; @@ -59,12 +59,12 @@ internal sealed class RazorPackage : AsyncPackage internal static readonly Guid GuidSyntaxVisualizerMenuCmdSet = new Guid(GuidSyntaxVisualizerMenuCmdSetString); internal const uint CmdIDRazorSyntaxVisualizer = 0x101; - // Razor isolation files command set - internal const string GuidRazorIsolationFilesCmdSetString = "8B2B3C5D-6E4A-4F9B-9C8D-1A2B3C4D5E6F"; - internal static readonly Guid GuidRazorIsolationFilesCmdSet = new Guid(GuidRazorIsolationFilesCmdSetString); - internal const uint CmdIdAddOrViewCssIsolationFile = 0x0100; - internal const uint CmdIdAddOrViewCsIsolationFile = 0x0101; - internal const uint CmdIdAddOrViewJsIsolationFile = 0x0102; + // Razor nested files command set + internal const string GuidRazorNestedFilesCmdSetString = "8B2B3C5D-6E4A-4F9B-9C8D-1A2B3C4D5E6F"; + internal static readonly Guid GuidRazorNestedFilesCmdSet = new Guid(GuidRazorNestedFilesCmdSetString); + internal const uint CmdIdAddOrViewCssNestedFile = 0x0100; + internal const uint CmdIdAddOrViewCsNestedFile = 0x0101; + internal const uint CmdIdAddOrViewJsNestedFile = 0x0102; // UI Context for when a .razor or .cshtml file is selected internal const string GuidRazorFileContextString = "7C3F2F9E-8D4A-4B6C-9E1F-5A8D7C6B3E2D"; @@ -100,8 +100,8 @@ protected override async Task InitializeAsync(CancellationToken cancellationToke var menuToolWin = new MenuCommand(ShowToolWindow, toolwndCommandID); mcs.AddCommand(menuToolWin); - // Register isolation file commands - RegisterIsolationFileCommands(mcs); + // Register nested file commands + RegisterNestedFileCommands(mcs); } var componentModel = (IComponentModel)GetGlobalService(typeof(SComponentModel)); @@ -161,9 +161,9 @@ private void ShowToolWindow(object sender, EventArgs e) } /// - /// Registers the isolation file commands (CSS, C#, Javascript) in the menu command service. + /// Registers the nested file commands (CSS, C#, Javascript) in the menu command service. /// - private void RegisterIsolationFileCommands(OleMenuCommandService mcs) + private void RegisterNestedFileCommands(OleMenuCommandService mcs) { ThreadHelper.ThrowIfNotOnUIThread(); @@ -171,24 +171,24 @@ private void RegisterIsolationFileCommands(OleMenuCommandService mcs) var requestInvoker = new Lazy(() => componentModel.GetService()); // Create command handlers - var cssHandler = new CssIsolationFileCommandHandler(this, requestInvoker); - var csharpHandler = new CSharpIsolationFileCommandHandler(this, requestInvoker); - var javascriptHandler = new JavascriptIsolationFileCommandHandler(this, requestInvoker); + var cssHandler = new CssNestedFileCommandHandler(this, requestInvoker); + var csharpHandler = new CSharpNestedFileCommandHandler(this, requestInvoker); + var javascriptHandler = new JavascriptNestedFileCommandHandler(this, requestInvoker); - // CSS Isolation File Command - var cssCommandId = new CommandID(GuidRazorIsolationFilesCmdSet, (int)CmdIdAddOrViewCssIsolationFile); + // CSS Nested File Command + var cssCommandId = new CommandID(GuidRazorNestedFilesCmdSet, (int)CmdIdAddOrViewCssNestedFile); var cssCommand = new OleMenuCommand(cssHandler.Execute, cssCommandId); cssCommand.BeforeQueryStatus += cssHandler.OnBeforeQueryStatus; mcs.AddCommand(cssCommand); - // C# Code-Behind Isolation File Command - var csharpCommandId = new CommandID(GuidRazorIsolationFilesCmdSet, (int)CmdIdAddOrViewCsIsolationFile); + // C# Code-Behind Nested File Command + var csharpCommandId = new CommandID(GuidRazorNestedFilesCmdSet, (int)CmdIdAddOrViewCsNestedFile); var csharpCommand = new OleMenuCommand(csharpHandler.Execute, csharpCommandId); csharpCommand.BeforeQueryStatus += csharpHandler.OnBeforeQueryStatus; mcs.AddCommand(csharpCommand); - // JS Isolation File Command - var javascriptCommandId = new CommandID(GuidRazorIsolationFilesCmdSet, (int)CmdIdAddOrViewJsIsolationFile); + // JS Nested File Command + var javascriptCommandId = new CommandID(GuidRazorNestedFilesCmdSet, (int)CmdIdAddOrViewJsNestedFile); var javascriptCommand = new OleMenuCommand(javascriptHandler.Execute, javascriptCommandId); javascriptCommand.BeforeQueryStatus += javascriptHandler.OnBeforeQueryStatus; mcs.AddCommand(javascriptCommand); diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/Resources.resx b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/Resources.resx index 8be71c0373d..9d04518ffa7 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/Resources.resx +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/Resources.resx @@ -120,22 +120,22 @@ LSP Razor Editor - - Add CSS Isolation File + + Add CSS Nested File - - View CSS Isolation File + + View CSS Nested File - - Add C# Code-Behind File + + Add C# Nested File - - View C# Code-Behind File + + View C# Nested File - - Add JavaScript Isolation File + + Add Javascript Nested File - - View JavaScript Isolation File + + View Javascript Nested File - + \ No newline at end of file diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.cs.xlf b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.cs.xlf index 52e9ec1de1d..11762e286f1 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.cs.xlf +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.cs.xlf @@ -17,49 +17,49 @@ Show Referenced Tag Helpers - + Add or View C# Code-Behind File Add or View C# Code-Behind File - - AddOrViewCsIsolationFile - AddOrViewCsIsolationFile + + AddOrViewCsNestedFile + AddOrViewCsNestedFile - - AddOrViewCsIsolationFile - AddOrViewCsIsolationFile + + AddOrViewCsNestedFile + AddOrViewCsNestedFile - - Add or View CSS Isolation File - Add or View CSS Isolation File + + Add or View CSS Nested File + Add or View CSS Nested File - - AddOrViewCssIsolationFile - AddOrViewCssIsolationFile + + AddOrViewCssNestedFile + AddOrViewCssNestedFile - - AddOrViewCssIsolationFile - AddOrViewCssIsolationFile + + AddOrViewCssNestedFile + AddOrViewCssNestedFile - - Add or View JavaScript Isolation File - Add or View JavaScript Isolation File + + Add or View JavaScript Nested File + Add or View JavaScript Nested File - - AddOrViewJsIsolationFile - AddOrViewJsIsolationFile + + AddOrViewJsNestedFile + AddOrViewJsNestedFile - - AddOrViewJsIsolationFile - AddOrViewJsIsolationFile + + AddOrViewJsNestedFile + AddOrViewJsNestedFile diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.de.xlf b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.de.xlf index 0ea157700bd..f95d205e009 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.de.xlf +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.de.xlf @@ -17,49 +17,49 @@ Show Referenced Tag Helpers - + Add or View C# Code-Behind File Add or View C# Code-Behind File - - AddOrViewCsIsolationFile - AddOrViewCsIsolationFile + + AddOrViewCsNestedFile + AddOrViewCsNestedFile - - AddOrViewCsIsolationFile - AddOrViewCsIsolationFile + + AddOrViewCsNestedFile + AddOrViewCsNestedFile - - Add or View CSS Isolation File - Add or View CSS Isolation File + + Add or View CSS Nested File + Add or View CSS Nested File - - AddOrViewCssIsolationFile - AddOrViewCssIsolationFile + + AddOrViewCssNestedFile + AddOrViewCssNestedFile - - AddOrViewCssIsolationFile - AddOrViewCssIsolationFile + + AddOrViewCssNestedFile + AddOrViewCssNestedFile - - Add or View JavaScript Isolation File - Add or View JavaScript Isolation File + + Add or View JavaScript Nested File + Add or View JavaScript Nested File - - AddOrViewJsIsolationFile - AddOrViewJsIsolationFile + + AddOrViewJsNestedFile + AddOrViewJsNestedFile - - AddOrViewJsIsolationFile - AddOrViewJsIsolationFile + + AddOrViewJsNestedFile + AddOrViewJsNestedFile diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.es.xlf b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.es.xlf index a32fd0baa25..61451673083 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.es.xlf +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.es.xlf @@ -17,49 +17,49 @@ Show Referenced Tag Helpers - + Add or View C# Code-Behind File Add or View C# Code-Behind File - - AddOrViewCsIsolationFile - AddOrViewCsIsolationFile + + AddOrViewCsNestedFile + AddOrViewCsNestedFile - - AddOrViewCsIsolationFile - AddOrViewCsIsolationFile + + AddOrViewCsNestedFile + AddOrViewCsNestedFile - - Add or View CSS Isolation File - Add or View CSS Isolation File + + Add or View CSS Nested File + Add or View CSS Nested File - - AddOrViewCssIsolationFile - AddOrViewCssIsolationFile + + AddOrViewCssNestedFile + AddOrViewCssNestedFile - - AddOrViewCssIsolationFile - AddOrViewCssIsolationFile + + AddOrViewCssNestedFile + AddOrViewCssNestedFile - - Add or View JavaScript Isolation File - Add or View JavaScript Isolation File + + Add or View JavaScript Nested File + Add or View JavaScript Nested File - - AddOrViewJsIsolationFile - AddOrViewJsIsolationFile + + AddOrViewJsNestedFile + AddOrViewJsNestedFile - - AddOrViewJsIsolationFile - AddOrViewJsIsolationFile + + AddOrViewJsNestedFile + AddOrViewJsNestedFile diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.fr.xlf b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.fr.xlf index c4057c494bb..a7d94ed12d5 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.fr.xlf +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.fr.xlf @@ -17,49 +17,49 @@ Show Referenced Tag Helpers - + Add or View C# Code-Behind File Add or View C# Code-Behind File - - AddOrViewCsIsolationFile - AddOrViewCsIsolationFile + + AddOrViewCsNestedFile + AddOrViewCsNestedFile - - AddOrViewCsIsolationFile - AddOrViewCsIsolationFile + + AddOrViewCsNestedFile + AddOrViewCsNestedFile - - Add or View CSS Isolation File - Add or View CSS Isolation File + + Add or View CSS Nested File + Add or View CSS Nested File - - AddOrViewCssIsolationFile - AddOrViewCssIsolationFile + + AddOrViewCssNestedFile + AddOrViewCssNestedFile - - AddOrViewCssIsolationFile - AddOrViewCssIsolationFile + + AddOrViewCssNestedFile + AddOrViewCssNestedFile - - Add or View JavaScript Isolation File - Add or View JavaScript Isolation File + + Add or View JavaScript Nested File + Add or View JavaScript Nested File - - AddOrViewJsIsolationFile - AddOrViewJsIsolationFile + + AddOrViewJsNestedFile + AddOrViewJsNestedFile - - AddOrViewJsIsolationFile - AddOrViewJsIsolationFile + + AddOrViewJsNestedFile + AddOrViewJsNestedFile diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.it.xlf b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.it.xlf index 28e7d3305a0..8b7824c7e6c 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.it.xlf +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.it.xlf @@ -17,49 +17,49 @@ Show Referenced Tag Helpers - + Add or View C# Code-Behind File Add or View C# Code-Behind File - - AddOrViewCsIsolationFile - AddOrViewCsIsolationFile + + AddOrViewCsNestedFile + AddOrViewCsNestedFile - - AddOrViewCsIsolationFile - AddOrViewCsIsolationFile + + AddOrViewCsNestedFile + AddOrViewCsNestedFile - - Add or View CSS Isolation File - Add or View CSS Isolation File + + Add or View CSS Nested File + Add or View CSS Nested File - - AddOrViewCssIsolationFile - AddOrViewCssIsolationFile + + AddOrViewCssNestedFile + AddOrViewCssNestedFile - - AddOrViewCssIsolationFile - AddOrViewCssIsolationFile + + AddOrViewCssNestedFile + AddOrViewCssNestedFile - - Add or View JavaScript Isolation File - Add or View JavaScript Isolation File + + Add or View JavaScript Nested File + Add or View JavaScript Nested File - - AddOrViewJsIsolationFile - AddOrViewJsIsolationFile + + AddOrViewJsNestedFile + AddOrViewJsNestedFile - - AddOrViewJsIsolationFile - AddOrViewJsIsolationFile + + AddOrViewJsNestedFile + AddOrViewJsNestedFile diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.ja.xlf b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.ja.xlf index fd82032c029..9e643ca4200 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.ja.xlf +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.ja.xlf @@ -17,49 +17,49 @@ Show Referenced Tag Helpers - + Add or View C# Code-Behind File Add or View C# Code-Behind File - - AddOrViewCsIsolationFile - AddOrViewCsIsolationFile + + AddOrViewCsNestedFile + AddOrViewCsNestedFile - - AddOrViewCsIsolationFile - AddOrViewCsIsolationFile + + AddOrViewCsNestedFile + AddOrViewCsNestedFile - - Add or View CSS Isolation File - Add or View CSS Isolation File + + Add or View CSS Nested File + Add or View CSS Nested File - - AddOrViewCssIsolationFile - AddOrViewCssIsolationFile + + AddOrViewCssNestedFile + AddOrViewCssNestedFile - - AddOrViewCssIsolationFile - AddOrViewCssIsolationFile + + AddOrViewCssNestedFile + AddOrViewCssNestedFile - - Add or View JavaScript Isolation File - Add or View JavaScript Isolation File + + Add or View JavaScript Nested File + Add or View JavaScript Nested File - - AddOrViewJsIsolationFile - AddOrViewJsIsolationFile + + AddOrViewJsNestedFile + AddOrViewJsNestedFile - - AddOrViewJsIsolationFile - AddOrViewJsIsolationFile + + AddOrViewJsNestedFile + AddOrViewJsNestedFile diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.ko.xlf b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.ko.xlf index 8ea6d8ea12e..142d3482857 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.ko.xlf +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.ko.xlf @@ -17,49 +17,49 @@ Show Referenced Tag Helpers - + Add or View C# Code-Behind File Add or View C# Code-Behind File - - AddOrViewCsIsolationFile - AddOrViewCsIsolationFile + + AddOrViewCsNestedFile + AddOrViewCsNestedFile - - AddOrViewCsIsolationFile - AddOrViewCsIsolationFile + + AddOrViewCsNestedFile + AddOrViewCsNestedFile - - Add or View CSS Isolation File - Add or View CSS Isolation File + + Add or View CSS Nested File + Add or View CSS Nested File - - AddOrViewCssIsolationFile - AddOrViewCssIsolationFile + + AddOrViewCssNestedFile + AddOrViewCssNestedFile - - AddOrViewCssIsolationFile - AddOrViewCssIsolationFile + + AddOrViewCssNestedFile + AddOrViewCssNestedFile - - Add or View JavaScript Isolation File - Add or View JavaScript Isolation File + + Add or View JavaScript Nested File + Add or View JavaScript Nested File - - AddOrViewJsIsolationFile - AddOrViewJsIsolationFile + + AddOrViewJsNestedFile + AddOrViewJsNestedFile - - AddOrViewJsIsolationFile - AddOrViewJsIsolationFile + + AddOrViewJsNestedFile + AddOrViewJsNestedFile diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.pl.xlf b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.pl.xlf index 2e3251705e4..34a5486a5ec 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.pl.xlf +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.pl.xlf @@ -17,49 +17,49 @@ Show Referenced Tag Helpers - + Add or View C# Code-Behind File Add or View C# Code-Behind File - - AddOrViewCsIsolationFile - AddOrViewCsIsolationFile + + AddOrViewCsNestedFile + AddOrViewCsNestedFile - - AddOrViewCsIsolationFile - AddOrViewCsIsolationFile + + AddOrViewCsNestedFile + AddOrViewCsNestedFile - - Add or View CSS Isolation File - Add or View CSS Isolation File + + Add or View CSS Nested File + Add or View CSS Nested File - - AddOrViewCssIsolationFile - AddOrViewCssIsolationFile + + AddOrViewCssNestedFile + AddOrViewCssNestedFile - - AddOrViewCssIsolationFile - AddOrViewCssIsolationFile + + AddOrViewCssNestedFile + AddOrViewCssNestedFile - - Add or View JavaScript Isolation File - Add or View JavaScript Isolation File + + Add or View JavaScript Nested File + Add or View JavaScript Nested File - - AddOrViewJsIsolationFile - AddOrViewJsIsolationFile + + AddOrViewJsNestedFile + AddOrViewJsNestedFile - - AddOrViewJsIsolationFile - AddOrViewJsIsolationFile + + AddOrViewJsNestedFile + AddOrViewJsNestedFile diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.pt-BR.xlf b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.pt-BR.xlf index 1c78267cebb..6198df502bc 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.pt-BR.xlf +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.pt-BR.xlf @@ -17,49 +17,49 @@ Show Referenced Tag Helpers - + Add or View C# Code-Behind File Add or View C# Code-Behind File - - AddOrViewCsIsolationFile - AddOrViewCsIsolationFile + + AddOrViewCsNestedFile + AddOrViewCsNestedFile - - AddOrViewCsIsolationFile - AddOrViewCsIsolationFile + + AddOrViewCsNestedFile + AddOrViewCsNestedFile - - Add or View CSS Isolation File - Add or View CSS Isolation File + + Add or View CSS Nested File + Add or View CSS Nested File - - AddOrViewCssIsolationFile - AddOrViewCssIsolationFile + + AddOrViewCssNestedFile + AddOrViewCssNestedFile - - AddOrViewCssIsolationFile - AddOrViewCssIsolationFile + + AddOrViewCssNestedFile + AddOrViewCssNestedFile - - Add or View JavaScript Isolation File - Add or View JavaScript Isolation File + + Add or View JavaScript Nested File + Add or View JavaScript Nested File - - AddOrViewJsIsolationFile - AddOrViewJsIsolationFile + + AddOrViewJsNestedFile + AddOrViewJsNestedFile - - AddOrViewJsIsolationFile - AddOrViewJsIsolationFile + + AddOrViewJsNestedFile + AddOrViewJsNestedFile diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.ru.xlf b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.ru.xlf index 59450993693..e139823d6ed 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.ru.xlf +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.ru.xlf @@ -17,49 +17,49 @@ Show Referenced Tag Helpers - + Add or View C# Code-Behind File Add or View C# Code-Behind File - - AddOrViewCsIsolationFile - AddOrViewCsIsolationFile + + AddOrViewCsNestedFile + AddOrViewCsNestedFile - - AddOrViewCsIsolationFile - AddOrViewCsIsolationFile + + AddOrViewCsNestedFile + AddOrViewCsNestedFile - - Add or View CSS Isolation File - Add or View CSS Isolation File + + Add or View CSS Nested File + Add or View CSS Nested File - - AddOrViewCssIsolationFile - AddOrViewCssIsolationFile + + AddOrViewCssNestedFile + AddOrViewCssNestedFile - - AddOrViewCssIsolationFile - AddOrViewCssIsolationFile + + AddOrViewCssNestedFile + AddOrViewCssNestedFile - - Add or View JavaScript Isolation File - Add or View JavaScript Isolation File + + Add or View JavaScript Nested File + Add or View JavaScript Nested File - - AddOrViewJsIsolationFile - AddOrViewJsIsolationFile + + AddOrViewJsNestedFile + AddOrViewJsNestedFile - - AddOrViewJsIsolationFile - AddOrViewJsIsolationFile + + AddOrViewJsNestedFile + AddOrViewJsNestedFile diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.tr.xlf b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.tr.xlf index 39938843386..b1b71cd28a7 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.tr.xlf +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.tr.xlf @@ -17,49 +17,49 @@ Show Referenced Tag Helpers - + Add or View C# Code-Behind File Add or View C# Code-Behind File - - AddOrViewCsIsolationFile - AddOrViewCsIsolationFile + + AddOrViewCsNestedFile + AddOrViewCsNestedFile - - AddOrViewCsIsolationFile - AddOrViewCsIsolationFile + + AddOrViewCsNestedFile + AddOrViewCsNestedFile - - Add or View CSS Isolation File - Add or View CSS Isolation File + + Add or View CSS Nested File + Add or View CSS Nested File - - AddOrViewCssIsolationFile - AddOrViewCssIsolationFile + + AddOrViewCssNestedFile + AddOrViewCssNestedFile - - AddOrViewCssIsolationFile - AddOrViewCssIsolationFile + + AddOrViewCssNestedFile + AddOrViewCssNestedFile - - Add or View JavaScript Isolation File - Add or View JavaScript Isolation File + + Add or View JavaScript Nested File + Add or View JavaScript Nested File - - AddOrViewJsIsolationFile - AddOrViewJsIsolationFile + + AddOrViewJsNestedFile + AddOrViewJsNestedFile - - AddOrViewJsIsolationFile - AddOrViewJsIsolationFile + + AddOrViewJsNestedFile + AddOrViewJsNestedFile diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.zh-Hans.xlf b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.zh-Hans.xlf index b5c4f7943cf..7b26d0c4a12 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.zh-Hans.xlf +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.zh-Hans.xlf @@ -17,49 +17,49 @@ Show Referenced Tag Helpers - + Add or View C# Code-Behind File Add or View C# Code-Behind File - - AddOrViewCsIsolationFile - AddOrViewCsIsolationFile + + AddOrViewCsNestedFile + AddOrViewCsNestedFile - - AddOrViewCsIsolationFile - AddOrViewCsIsolationFile + + AddOrViewCsNestedFile + AddOrViewCsNestedFile - - Add or View CSS Isolation File - Add or View CSS Isolation File + + Add or View CSS Nested File + Add or View CSS Nested File - - AddOrViewCssIsolationFile - AddOrViewCssIsolationFile + + AddOrViewCssNestedFile + AddOrViewCssNestedFile - - AddOrViewCssIsolationFile - AddOrViewCssIsolationFile + + AddOrViewCssNestedFile + AddOrViewCssNestedFile - - Add or View JavaScript Isolation File - Add or View JavaScript Isolation File + + Add or View JavaScript Nested File + Add or View JavaScript Nested File - - AddOrViewJsIsolationFile - AddOrViewJsIsolationFile + + AddOrViewJsNestedFile + AddOrViewJsNestedFile - - AddOrViewJsIsolationFile - AddOrViewJsIsolationFile + + AddOrViewJsNestedFile + AddOrViewJsNestedFile diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.zh-Hant.xlf b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.zh-Hant.xlf index 64424e0b5ff..ccec12f21a1 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.zh-Hant.xlf +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.zh-Hant.xlf @@ -17,49 +17,49 @@ Show Referenced Tag Helpers - + Add or View C# Code-Behind File Add or View C# Code-Behind File - - AddOrViewCsIsolationFile - AddOrViewCsIsolationFile + + AddOrViewCsNestedFile + AddOrViewCsNestedFile - - AddOrViewCsIsolationFile - AddOrViewCsIsolationFile + + AddOrViewCsNestedFile + AddOrViewCsNestedFile - - Add or View CSS Isolation File - Add or View CSS Isolation File + + Add or View CSS Nested File + Add or View CSS Nested File - - AddOrViewCssIsolationFile - AddOrViewCssIsolationFile + + AddOrViewCssNestedFile + AddOrViewCssNestedFile - - AddOrViewCssIsolationFile - AddOrViewCssIsolationFile + + AddOrViewCssNestedFile + AddOrViewCssNestedFile - - Add or View JavaScript Isolation File - Add or View JavaScript Isolation File + + Add or View JavaScript Nested File + Add or View JavaScript Nested File - - AddOrViewJsIsolationFile - AddOrViewJsIsolationFile + + AddOrViewJsNestedFile + AddOrViewJsNestedFile - - AddOrViewJsIsolationFile - AddOrViewJsIsolationFile + + AddOrViewJsNestedFile + AddOrViewJsNestedFile diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.cs.xlf b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.cs.xlf index c371f5dc24a..9269b2c3f50 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.cs.xlf +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.cs.xlf @@ -2,19 +2,19 @@ - - Add CSS Isolation File - Add CSS Isolation File + + Add CSS Nested File + Add CSS Nested File - - Add C# Code-Behind File - Add C# Code-Behind File + + Add C# Nested File + Add C# Nested File - - Add JavaScript Isolation File - Add JavaScript Isolation File + + Add Javascript Nested File + Add Javascript Nested File @@ -22,19 +22,19 @@ LSP Razor Editor - - View CSS Isolation File - View CSS Isolation File + + View CSS Nested File + View CSS Nested File - - View C# Code-Behind File - View C# Code-Behind File + + View C# Nested File + View C# Nested File - - View JavaScript Isolation File - View JavaScript Isolation File + + View Javascript Nested File + View Javascript Nested File diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.de.xlf b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.de.xlf index 0c65bffd549..197403db022 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.de.xlf +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.de.xlf @@ -2,19 +2,19 @@ - - Add CSS Isolation File - Add CSS Isolation File + + Add CSS Nested File + Add CSS Nested File - - Add C# Code-Behind File - Add C# Code-Behind File + + Add C# Nested File + Add C# Nested File - - Add JavaScript Isolation File - Add JavaScript Isolation File + + Add Javascript Nested File + Add Javascript Nested File @@ -22,19 +22,19 @@ LSP-Razor-Editor - - View CSS Isolation File - View CSS Isolation File + + View CSS Nested File + View CSS Nested File - - View C# Code-Behind File - View C# Code-Behind File + + View C# Nested File + View C# Nested File - - View JavaScript Isolation File - View JavaScript Isolation File + + View Javascript Nested File + View Javascript Nested File diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.es.xlf b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.es.xlf index da1866ffa45..1b352d16a2c 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.es.xlf +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.es.xlf @@ -2,19 +2,19 @@ - - Add CSS Isolation File - Add CSS Isolation File + + Add CSS Nested File + Add CSS Nested File - - Add C# Code-Behind File - Add C# Code-Behind File + + Add C# Nested File + Add C# Nested File - - Add JavaScript Isolation File - Add JavaScript Isolation File + + Add Javascript Nested File + Add Javascript Nested File @@ -22,19 +22,19 @@ LSP Razor Editor - - View CSS Isolation File - View CSS Isolation File + + View CSS Nested File + View CSS Nested File - - View C# Code-Behind File - View C# Code-Behind File + + View C# Nested File + View C# Nested File - - View JavaScript Isolation File - View JavaScript Isolation File + + View Javascript Nested File + View Javascript Nested File diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.fr.xlf b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.fr.xlf index 1ae3ec5b639..eade1280b25 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.fr.xlf +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.fr.xlf @@ -2,19 +2,19 @@ - - Add CSS Isolation File - Add CSS Isolation File + + Add CSS Nested File + Add CSS Nested File - - Add C# Code-Behind File - Add C# Code-Behind File + + Add C# Nested File + Add C# Nested File - - Add JavaScript Isolation File - Add JavaScript Isolation File + + Add Javascript Nested File + Add Javascript Nested File @@ -22,19 +22,19 @@ Éditeur Razor LSP - - View CSS Isolation File - View CSS Isolation File + + View CSS Nested File + View CSS Nested File - - View C# Code-Behind File - View C# Code-Behind File + + View C# Nested File + View C# Nested File - - View JavaScript Isolation File - View JavaScript Isolation File + + View Javascript Nested File + View Javascript Nested File diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.it.xlf b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.it.xlf index e7c1976fc26..9b5ccf65cb4 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.it.xlf +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.it.xlf @@ -2,19 +2,19 @@ - - Add CSS Isolation File - Add CSS Isolation File + + Add CSS Nested File + Add CSS Nested File - - Add C# Code-Behind File - Add C# Code-Behind File + + Add C# Nested File + Add C# Nested File - - Add JavaScript Isolation File - Add JavaScript Isolation File + + Add Javascript Nested File + Add Javascript Nested File @@ -22,19 +22,19 @@ Editor LSP Razor - - View CSS Isolation File - View CSS Isolation File + + View CSS Nested File + View CSS Nested File - - View C# Code-Behind File - View C# Code-Behind File + + View C# Nested File + View C# Nested File - - View JavaScript Isolation File - View JavaScript Isolation File + + View Javascript Nested File + View Javascript Nested File diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.ja.xlf b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.ja.xlf index a09bc084054..226cf925ebe 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.ja.xlf +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.ja.xlf @@ -2,19 +2,19 @@ - - Add CSS Isolation File - Add CSS Isolation File + + Add CSS Nested File + Add CSS Nested File - - Add C# Code-Behind File - Add C# Code-Behind File + + Add C# Nested File + Add C# Nested File - - Add JavaScript Isolation File - Add JavaScript Isolation File + + Add Javascript Nested File + Add Javascript Nested File @@ -22,19 +22,19 @@ LSP Razor エディター - - View CSS Isolation File - View CSS Isolation File + + View CSS Nested File + View CSS Nested File - - View C# Code-Behind File - View C# Code-Behind File + + View C# Nested File + View C# Nested File - - View JavaScript Isolation File - View JavaScript Isolation File + + View Javascript Nested File + View Javascript Nested File diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.ko.xlf b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.ko.xlf index f8b2748d753..a993e54f3e5 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.ko.xlf +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.ko.xlf @@ -2,19 +2,19 @@ - - Add CSS Isolation File - Add CSS Isolation File + + Add CSS Nested File + Add CSS Nested File - - Add C# Code-Behind File - Add C# Code-Behind File + + Add C# Nested File + Add C# Nested File - - Add JavaScript Isolation File - Add JavaScript Isolation File + + Add Javascript Nested File + Add Javascript Nested File @@ -22,19 +22,19 @@ LSP Razor 편집기 - - View CSS Isolation File - View CSS Isolation File + + View CSS Nested File + View CSS Nested File - - View C# Code-Behind File - View C# Code-Behind File + + View C# Nested File + View C# Nested File - - View JavaScript Isolation File - View JavaScript Isolation File + + View Javascript Nested File + View Javascript Nested File diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.pl.xlf b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.pl.xlf index 959fa81dbfe..725364842f0 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.pl.xlf +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.pl.xlf @@ -2,19 +2,19 @@ - - Add CSS Isolation File - Add CSS Isolation File + + Add CSS Nested File + Add CSS Nested File - - Add C# Code-Behind File - Add C# Code-Behind File + + Add C# Nested File + Add C# Nested File - - Add JavaScript Isolation File - Add JavaScript Isolation File + + Add Javascript Nested File + Add Javascript Nested File @@ -22,19 +22,19 @@ Edytor składni Razor LSP - - View CSS Isolation File - View CSS Isolation File + + View CSS Nested File + View CSS Nested File - - View C# Code-Behind File - View C# Code-Behind File + + View C# Nested File + View C# Nested File - - View JavaScript Isolation File - View JavaScript Isolation File + + View Javascript Nested File + View Javascript Nested File diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.pt-BR.xlf b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.pt-BR.xlf index 2e140ecbd73..f6a96496bf0 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.pt-BR.xlf +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.pt-BR.xlf @@ -2,19 +2,19 @@ - - Add CSS Isolation File - Add CSS Isolation File + + Add CSS Nested File + Add CSS Nested File - - Add C# Code-Behind File - Add C# Code-Behind File + + Add C# Nested File + Add C# Nested File - - Add JavaScript Isolation File - Add JavaScript Isolation File + + Add Javascript Nested File + Add Javascript Nested File @@ -22,19 +22,19 @@ Editor LSP Razor - - View CSS Isolation File - View CSS Isolation File + + View CSS Nested File + View CSS Nested File - - View C# Code-Behind File - View C# Code-Behind File + + View C# Nested File + View C# Nested File - - View JavaScript Isolation File - View JavaScript Isolation File + + View Javascript Nested File + View Javascript Nested File diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.ru.xlf b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.ru.xlf index ea65d453e40..f5bc43722a7 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.ru.xlf +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.ru.xlf @@ -2,19 +2,19 @@ - - Add CSS Isolation File - Add CSS Isolation File + + Add CSS Nested File + Add CSS Nested File - - Add C# Code-Behind File - Add C# Code-Behind File + + Add C# Nested File + Add C# Nested File - - Add JavaScript Isolation File - Add JavaScript Isolation File + + Add Javascript Nested File + Add Javascript Nested File @@ -22,19 +22,19 @@ Редактор Razor с поддержкой LSP - - View CSS Isolation File - View CSS Isolation File + + View CSS Nested File + View CSS Nested File - - View C# Code-Behind File - View C# Code-Behind File + + View C# Nested File + View C# Nested File - - View JavaScript Isolation File - View JavaScript Isolation File + + View Javascript Nested File + View Javascript Nested File diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.tr.xlf b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.tr.xlf index 8dbadf6088f..1f8c7ad58f5 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.tr.xlf +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.tr.xlf @@ -2,19 +2,19 @@ - - Add CSS Isolation File - Add CSS Isolation File + + Add CSS Nested File + Add CSS Nested File - - Add C# Code-Behind File - Add C# Code-Behind File + + Add C# Nested File + Add C# Nested File - - Add JavaScript Isolation File - Add JavaScript Isolation File + + Add Javascript Nested File + Add Javascript Nested File @@ -22,19 +22,19 @@ LSP Razor Editor - - View CSS Isolation File - View CSS Isolation File + + View CSS Nested File + View CSS Nested File - - View C# Code-Behind File - View C# Code-Behind File + + View C# Nested File + View C# Nested File - - View JavaScript Isolation File - View JavaScript Isolation File + + View Javascript Nested File + View Javascript Nested File diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.zh-Hans.xlf b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.zh-Hans.xlf index c4423f7c412..dd7d96533b9 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.zh-Hans.xlf +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.zh-Hans.xlf @@ -2,19 +2,19 @@ - - Add CSS Isolation File - Add CSS Isolation File + + Add CSS Nested File + Add CSS Nested File - - Add C# Code-Behind File - Add C# Code-Behind File + + Add C# Nested File + Add C# Nested File - - Add JavaScript Isolation File - Add JavaScript Isolation File + + Add Javascript Nested File + Add Javascript Nested File @@ -22,19 +22,19 @@ LSP Razor 编辑器 - - View CSS Isolation File - View CSS Isolation File + + View CSS Nested File + View CSS Nested File - - View C# Code-Behind File - View C# Code-Behind File + + View C# Nested File + View C# Nested File - - View JavaScript Isolation File - View JavaScript Isolation File + + View Javascript Nested File + View Javascript Nested File diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.zh-Hant.xlf b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.zh-Hant.xlf index 65794cb9dcf..47451d54eaf 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.zh-Hant.xlf +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.zh-Hant.xlf @@ -2,19 +2,19 @@ - - Add CSS Isolation File - Add CSS Isolation File + + Add CSS Nested File + Add CSS Nested File - - Add C# Code-Behind File - Add C# Code-Behind File + + Add C# Nested File + Add C# Nested File - - Add JavaScript Isolation File - Add JavaScript Isolation File + + Add Javascript Nested File + Add Javascript Nested File @@ -22,19 +22,19 @@ LSP Razor 編輯器 - - View CSS Isolation File - View CSS Isolation File + + View CSS Nested File + View CSS Nested File - - View C# Code-Behind File - View C# Code-Behind File + + View C# Nested File + View C# Nested File - - View JavaScript Isolation File - View JavaScript Isolation File + + View Javascript Nested File + View Javascript Nested File diff --git a/src/Razor/test/Microsoft.CodeAnalysis.Razor.CohostingShared.Test/Microsoft.CodeAnalysis.Razor.CohostingShared.Test.projitems b/src/Razor/test/Microsoft.CodeAnalysis.Razor.CohostingShared.Test/Microsoft.CodeAnalysis.Razor.CohostingShared.Test.projitems index ca1b93d67db..f2f3328af3b 100644 --- a/src/Razor/test/Microsoft.CodeAnalysis.Razor.CohostingShared.Test/Microsoft.CodeAnalysis.Razor.CohostingShared.Test.projitems +++ b/src/Razor/test/Microsoft.CodeAnalysis.Razor.CohostingShared.Test/Microsoft.CodeAnalysis.Razor.CohostingShared.Test.projitems @@ -54,7 +54,7 @@ - + diff --git a/src/Razor/test/Microsoft.CodeAnalysis.Razor.CohostingShared.Test/IsolationFiles/RemoteAddIsolationFileServiceTest.cs b/src/Razor/test/Microsoft.CodeAnalysis.Razor.CohostingShared.Test/NestedFiles/RemoteAddNestedFileServiceTest.cs similarity index 75% rename from src/Razor/test/Microsoft.CodeAnalysis.Razor.CohostingShared.Test/IsolationFiles/RemoteAddIsolationFileServiceTest.cs rename to src/Razor/test/Microsoft.CodeAnalysis.Razor.CohostingShared.Test/NestedFiles/RemoteAddNestedFileServiceTest.cs index 1c1baf943a8..bc4fec9a355 100644 --- a/src/Razor/test/Microsoft.CodeAnalysis.Razor.CohostingShared.Test/IsolationFiles/RemoteAddIsolationFileServiceTest.cs +++ b/src/Razor/test/Microsoft.CodeAnalysis.Razor.CohostingShared.Test/NestedFiles/RemoteAddNestedFileServiceTest.cs @@ -1,26 +1,26 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Razor.IsolationFiles; +using Microsoft.CodeAnalysis.Razor.NestedFiles; using Microsoft.CodeAnalysis.Razor.Remote; using Xunit; using Xunit.Abstractions; -namespace Microsoft.VisualStudio.Razor.LanguageClient.Cohost.IsolationFiles; +namespace Microsoft.VisualStudio.Razor.LanguageClient.Cohost.NestedFiles; -public class RemoteAddIsolationFileServiceTest(ITestOutputHelper testOutputHelper) : CohostEndpointTestBase(testOutputHelper) +public class RemoteAddNestedFileServiceTest(ITestOutputHelper testOutputHelper) : CohostEndpointTestBase(testOutputHelper) { [Fact] - public async Task CssIsolationFile_CreatesEmptyFile() + public async Task CssNestedFile_CreatesEmptyFile() { var document = CreateProjectAndRazorDocument("
"); var razorFileUri = new Uri(document.FilePath!); - var result = await RemoteServiceInvoker.TryInvokeAsync( + var result = await RemoteServiceInvoker.TryInvokeAsync( document.Project.Solution, - (service, solutionInfo, ct) => service.AddIsolationFileAsync(solutionInfo, razorFileUri, IsolationFileKind.Css, ct), + (service, solutionInfo, ct) => service.AddNestedFileAsync(solutionInfo, razorFileUri, NestedFileKind.Css, ct), DisposalToken); Assert.NotNull(result); @@ -38,14 +38,14 @@ public async Task CssIsolationFile_CreatesEmptyFile() } [Fact] - public async Task JavaScriptIsolationFile_CreatesFileWithTemplate() + public async Task JavaScriptNestedFile_CreatesFileWithTemplate() { var document = CreateProjectAndRazorDocument("
"); var razorFileUri = new Uri(document.FilePath!); - var result = await RemoteServiceInvoker.TryInvokeAsync( + var result = await RemoteServiceInvoker.TryInvokeAsync( document.Project.Solution, - (service, solutionInfo, ct) => service.AddIsolationFileAsync(solutionInfo, razorFileUri, IsolationFileKind.JavaScript, ct), + (service, solutionInfo, ct) => service.AddNestedFileAsync(solutionInfo, razorFileUri, NestedFileKind.JavaScript, ct), DisposalToken); Assert.NotNull(result); @@ -60,18 +60,18 @@ public async Task JavaScriptIsolationFile_CreatesFileWithTemplate() Assert.True(changes[1].TryGetFirst(out var textEdit)); Assert.Single(textEdit!.Edits); var content = ((TextEdit)textEdit.Edits[0]).NewText; - Assert.Contains("JavaScript isolation for File1 component", content); + Assert.Contains("JavaScript nested file for File1 component", content); } [Fact] - public async Task CSharpIsolationFile_GeneratesCodeBehind() + public async Task CSharpNestedFile_GeneratesCodeBehind() { var document = CreateProjectAndRazorDocument("
"); var razorFileUri = new Uri(document.FilePath!); - var result = await RemoteServiceInvoker.TryInvokeAsync( + var result = await RemoteServiceInvoker.TryInvokeAsync( document.Project.Solution, - (service, solutionInfo, ct) => service.AddIsolationFileAsync(solutionInfo, razorFileUri, IsolationFileKind.CSharp, ct), + (service, solutionInfo, ct) => service.AddNestedFileAsync(solutionInfo, razorFileUri, NestedFileKind.CSharp, ct), DisposalToken); Assert.NotNull(result); @@ -94,7 +94,7 @@ public async Task CSharpIsolationFile_GeneratesCodeBehind() } [Fact] - public async Task CSharpIsolationFile_WithNamespaceDirective() + public async Task CSharpNestedFile_WithNamespaceDirective() { var document = CreateProjectAndRazorDocument(""" @namespace My.Custom.Namespace @@ -103,9 +103,9 @@ @namespace My.Custom.Namespace """); var razorFileUri = new Uri(document.FilePath!); - var result = await RemoteServiceInvoker.TryInvokeAsync( + var result = await RemoteServiceInvoker.TryInvokeAsync( document.Project.Solution, - (service, solutionInfo, ct) => service.AddIsolationFileAsync(solutionInfo, razorFileUri, IsolationFileKind.CSharp, ct), + (service, solutionInfo, ct) => service.AddNestedFileAsync(solutionInfo, razorFileUri, NestedFileKind.CSharp, ct), DisposalToken); Assert.NotNull(result); @@ -119,7 +119,7 @@ @namespace My.Custom.Namespace } [Fact] - public async Task CSharpIsolationFile_IncludesUsingDirectives() + public async Task CSharpNestedFile_IncludesUsingDirectives() { var document = CreateProjectAndRazorDocument( """ @@ -129,9 +129,9 @@ @using System.Diagnostics """); var razorFileUri = new Uri(document.FilePath!); - var result = await RemoteServiceInvoker.TryInvokeAsync( + var result = await RemoteServiceInvoker.TryInvokeAsync( document.Project.Solution, - (service, solutionInfo, ct) => service.AddIsolationFileAsync(solutionInfo, razorFileUri, IsolationFileKind.CSharp, ct), + (service, solutionInfo, ct) => service.AddNestedFileAsync(solutionInfo, razorFileUri, NestedFileKind.CSharp, ct), DisposalToken); Assert.NotNull(result); @@ -146,7 +146,7 @@ @using System.Diagnostics } [Fact] - public async Task CshtmlFile_CssIsolation() + public async Task CshtmlFile_CssNestedFile() { var document = CreateProjectAndRazorDocument( "
", @@ -154,9 +154,9 @@ public async Task CshtmlFile_CssIsolation() documentFilePath: FilePath("Page1.cshtml")); var razorFileUri = new Uri(document.FilePath!); - var result = await RemoteServiceInvoker.TryInvokeAsync( + var result = await RemoteServiceInvoker.TryInvokeAsync( document.Project.Solution, - (service, solutionInfo, ct) => service.AddIsolationFileAsync(solutionInfo, razorFileUri, IsolationFileKind.Css, ct), + (service, solutionInfo, ct) => service.AddNestedFileAsync(solutionInfo, razorFileUri, NestedFileKind.Css, ct), DisposalToken); Assert.NotNull(result); @@ -175,9 +175,9 @@ public async Task InvalidFileKind_ReturnsNull() var document = CreateProjectAndRazorDocument("
"); var razorFileUri = new Uri(document.FilePath!); - var result = await RemoteServiceInvoker.TryInvokeAsync( + var result = await RemoteServiceInvoker.TryInvokeAsync( document.Project.Solution, - (service, solutionInfo, ct) => service.AddIsolationFileAsync(solutionInfo, razorFileUri, "invalid", ct), + (service, solutionInfo, ct) => service.AddNestedFileAsync(solutionInfo, razorFileUri, "invalid", ct), DisposalToken); Assert.Null(result); From 6b6020261ccb562441abc3dfe69fb1911cfe3e33 Mon Sep 17 00:00:00 2001 From: Todd Grunke Date: Wed, 1 Apr 2026 10:19:02 -0700 Subject: [PATCH 07/13] Change menu text to something like "Add Weather.razor.css" Remove applicable check that was special casing js --- .../CSharpNestedFileCommandHandler.cs | 3 -- .../CssNestedFileCommandHandler.cs | 3 -- .../JavascriptNestedFileCommandHandler.cs | 8 ----- .../NestedFiles/NestedFileCommandHandler.cs | 22 ++----------- .../Resources.resx | 20 +++--------- .../xlf/Resources.cs.xlf | 32 ++++--------------- .../xlf/Resources.de.xlf | 32 ++++--------------- .../xlf/Resources.es.xlf | 32 ++++--------------- .../xlf/Resources.fr.xlf | 32 ++++--------------- .../xlf/Resources.it.xlf | 32 ++++--------------- .../xlf/Resources.ja.xlf | 32 ++++--------------- .../xlf/Resources.ko.xlf | 32 ++++--------------- .../xlf/Resources.pl.xlf | 32 ++++--------------- .../xlf/Resources.pt-BR.xlf | 32 ++++--------------- .../xlf/Resources.ru.xlf | 32 ++++--------------- .../xlf/Resources.tr.xlf | 32 ++++--------------- .../xlf/Resources.zh-Hans.xlf | 32 ++++--------------- .../xlf/Resources.zh-Hant.xlf | 32 ++++--------------- 18 files changed, 85 insertions(+), 387 deletions(-) diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/NestedFiles/CSharpNestedFileCommandHandler.cs b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/NestedFiles/CSharpNestedFileCommandHandler.cs index 2d23411bb1f..56135f4a17d 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/NestedFiles/CSharpNestedFileCommandHandler.cs +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/NestedFiles/CSharpNestedFileCommandHandler.cs @@ -15,7 +15,4 @@ internal sealed class CSharpNestedFileCommandHandler( Lazy requestInvoker) : NestedFileCommandHandler(serviceProvider, ".cs", NestedFileKind.CSharp, requestInvoker) { - protected override string AddText => Resources.Add_CS_Nested_File; - - protected override string ViewText => Resources.View_CS_Nested_File; } diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/NestedFiles/CssNestedFileCommandHandler.cs b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/NestedFiles/CssNestedFileCommandHandler.cs index 9c9e6d63c0d..0c8f4bc499a 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/NestedFiles/CssNestedFileCommandHandler.cs +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/NestedFiles/CssNestedFileCommandHandler.cs @@ -15,7 +15,4 @@ internal sealed class CssNestedFileCommandHandler( Lazy requestInvoker) : NestedFileCommandHandler(serviceProvider, ".css", NestedFileKind.Css, requestInvoker) { - protected override string AddText => Resources.Add_CSS_Nested_File; - - protected override string ViewText => Resources.View_CSS_Nested_File; } diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/NestedFiles/JavascriptNestedFileCommandHandler.cs b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/NestedFiles/JavascriptNestedFileCommandHandler.cs index 7bd9127c064..dc5ba05f86d 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/NestedFiles/JavascriptNestedFileCommandHandler.cs +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/NestedFiles/JavascriptNestedFileCommandHandler.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using Microsoft.AspNetCore.Razor.Language; using Microsoft.CodeAnalysis.Razor.NestedFiles; using Microsoft.VisualStudio.Razor.ProjectSystem; @@ -16,11 +15,4 @@ internal sealed class JavascriptNestedFileCommandHandler( Lazy requestInvoker) : NestedFileCommandHandler(serviceProvider, ".js", NestedFileKind.JavaScript, requestInvoker) { - protected override string AddText => Resources.Add_JavaScript_Nested_File; - - protected override string ViewText => Resources.View_JavaScript_Nested_File; - - // JS nested files are only applicable to Razor components (.razor), not MVC views (.cshtml). - protected override bool IsApplicable(string razorFilePath) - => FileKinds.TryGetFileKindFromPath(razorFilePath, out _); } diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/NestedFiles/NestedFileCommandHandler.cs b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/NestedFiles/NestedFileCommandHandler.cs index 4dac78d5307..590290ffe99 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/NestedFiles/NestedFileCommandHandler.cs +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/NestedFiles/NestedFileCommandHandler.cs @@ -31,22 +31,6 @@ internal abstract class NestedFileCommandHandler( private readonly string _fileKind = fileKind; private readonly Lazy _requestInvoker = requestInvoker; - /// - /// Gets the display text when the file doesn't exist (e.g., "Add Home.razor.css"). - /// - protected abstract string AddText { get; } - - /// - /// Gets the display text when the file exists (e.g., "View Home.razor.css"). - /// - protected abstract string ViewText { get; } - - /// - /// Returns whether this command is applicable for the given Razor file. - /// Override to hide the command for certain file types. - /// - protected virtual bool IsApplicable(string razorFilePath) => true; - /// /// Configures the command status and text based on whether the nested file exists. /// @@ -59,8 +43,7 @@ public void OnBeforeQueryStatus(object sender, EventArgs e) // Check if the Razor file context is active before doing expensive hierarchy queries if (!IsRazorFileUIContextActive() - || GetSelectedRazorFilePath() is not string razorFilePath - || !IsApplicable(razorFilePath)) + || GetSelectedRazorFilePath() is not string razorFilePath) { command.Visible = false; return; @@ -68,10 +51,11 @@ public void OnBeforeQueryStatus(object sender, EventArgs e) var nestedFilePath = GetNestedFilePath(razorFilePath); var nestedFileExists = File.Exists(nestedFilePath); + var nestedFileName = Path.GetFileName(nestedFilePath); command.Visible = true; command.Enabled = true; - command.Text = nestedFileExists ? ViewText : AddText; + command.Text = string.Format(nestedFileExists ? Resources.View_Nested_File : Resources.Add_Nested_File, nestedFileName); } private bool IsRazorFileUIContextActive() diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/Resources.resx b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/Resources.resx index 9d04518ffa7..6f971598ce3 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/Resources.resx +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/Resources.resx @@ -120,22 +120,10 @@ LSP Razor Editor - - Add CSS Nested File + + Add {0} - - View CSS Nested File - - - Add C# Nested File - - - View C# Nested File - - - Add Javascript Nested File - - - View Javascript Nested File + + View {0} \ No newline at end of file diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.cs.xlf b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.cs.xlf index 9269b2c3f50..e2f00a34505 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.cs.xlf +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.cs.xlf @@ -2,19 +2,9 @@ - - Add CSS Nested File - Add CSS Nested File - - - - Add C# Nested File - Add C# Nested File - - - - Add Javascript Nested File - Add Javascript Nested File + + Add {0} + Add {0} @@ -22,19 +12,9 @@ LSP Razor Editor - - View CSS Nested File - View CSS Nested File - - - - View C# Nested File - View C# Nested File - - - - View Javascript Nested File - View Javascript Nested File + + View {0} + View {0} diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.de.xlf b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.de.xlf index 197403db022..da8c27011f5 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.de.xlf +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.de.xlf @@ -2,19 +2,9 @@ - - Add CSS Nested File - Add CSS Nested File - - - - Add C# Nested File - Add C# Nested File - - - - Add Javascript Nested File - Add Javascript Nested File + + Add {0} + Add {0} @@ -22,19 +12,9 @@ LSP-Razor-Editor - - View CSS Nested File - View CSS Nested File - - - - View C# Nested File - View C# Nested File - - - - View Javascript Nested File - View Javascript Nested File + + View {0} + View {0} diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.es.xlf b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.es.xlf index 1b352d16a2c..4c78dd87aeb 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.es.xlf +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.es.xlf @@ -2,19 +2,9 @@ - - Add CSS Nested File - Add CSS Nested File - - - - Add C# Nested File - Add C# Nested File - - - - Add Javascript Nested File - Add Javascript Nested File + + Add {0} + Add {0} @@ -22,19 +12,9 @@ LSP Razor Editor - - View CSS Nested File - View CSS Nested File - - - - View C# Nested File - View C# Nested File - - - - View Javascript Nested File - View Javascript Nested File + + View {0} + View {0} diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.fr.xlf b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.fr.xlf index eade1280b25..5e70ad78626 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.fr.xlf +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.fr.xlf @@ -2,19 +2,9 @@ - - Add CSS Nested File - Add CSS Nested File - - - - Add C# Nested File - Add C# Nested File - - - - Add Javascript Nested File - Add Javascript Nested File + + Add {0} + Add {0} @@ -22,19 +12,9 @@ Éditeur Razor LSP - - View CSS Nested File - View CSS Nested File - - - - View C# Nested File - View C# Nested File - - - - View Javascript Nested File - View Javascript Nested File + + View {0} + View {0} diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.it.xlf b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.it.xlf index 9b5ccf65cb4..f24a7a527bd 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.it.xlf +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.it.xlf @@ -2,19 +2,9 @@ - - Add CSS Nested File - Add CSS Nested File - - - - Add C# Nested File - Add C# Nested File - - - - Add Javascript Nested File - Add Javascript Nested File + + Add {0} + Add {0} @@ -22,19 +12,9 @@ Editor LSP Razor - - View CSS Nested File - View CSS Nested File - - - - View C# Nested File - View C# Nested File - - - - View Javascript Nested File - View Javascript Nested File + + View {0} + View {0} diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.ja.xlf b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.ja.xlf index 226cf925ebe..1f6f866bc65 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.ja.xlf +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.ja.xlf @@ -2,19 +2,9 @@ - - Add CSS Nested File - Add CSS Nested File - - - - Add C# Nested File - Add C# Nested File - - - - Add Javascript Nested File - Add Javascript Nested File + + Add {0} + Add {0} @@ -22,19 +12,9 @@ LSP Razor エディター - - View CSS Nested File - View CSS Nested File - - - - View C# Nested File - View C# Nested File - - - - View Javascript Nested File - View Javascript Nested File + + View {0} + View {0} diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.ko.xlf b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.ko.xlf index a993e54f3e5..735cc4ef152 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.ko.xlf +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.ko.xlf @@ -2,19 +2,9 @@ - - Add CSS Nested File - Add CSS Nested File - - - - Add C# Nested File - Add C# Nested File - - - - Add Javascript Nested File - Add Javascript Nested File + + Add {0} + Add {0} @@ -22,19 +12,9 @@ LSP Razor 편집기 - - View CSS Nested File - View CSS Nested File - - - - View C# Nested File - View C# Nested File - - - - View Javascript Nested File - View Javascript Nested File + + View {0} + View {0} diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.pl.xlf b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.pl.xlf index 725364842f0..c892474217f 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.pl.xlf +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.pl.xlf @@ -2,19 +2,9 @@ - - Add CSS Nested File - Add CSS Nested File - - - - Add C# Nested File - Add C# Nested File - - - - Add Javascript Nested File - Add Javascript Nested File + + Add {0} + Add {0} @@ -22,19 +12,9 @@ Edytor składni Razor LSP - - View CSS Nested File - View CSS Nested File - - - - View C# Nested File - View C# Nested File - - - - View Javascript Nested File - View Javascript Nested File + + View {0} + View {0} diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.pt-BR.xlf b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.pt-BR.xlf index f6a96496bf0..a45477f9060 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.pt-BR.xlf +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.pt-BR.xlf @@ -2,19 +2,9 @@ - - Add CSS Nested File - Add CSS Nested File - - - - Add C# Nested File - Add C# Nested File - - - - Add Javascript Nested File - Add Javascript Nested File + + Add {0} + Add {0} @@ -22,19 +12,9 @@ Editor LSP Razor - - View CSS Nested File - View CSS Nested File - - - - View C# Nested File - View C# Nested File - - - - View Javascript Nested File - View Javascript Nested File + + View {0} + View {0} diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.ru.xlf b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.ru.xlf index f5bc43722a7..10c32a2e310 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.ru.xlf +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.ru.xlf @@ -2,19 +2,9 @@ - - Add CSS Nested File - Add CSS Nested File - - - - Add C# Nested File - Add C# Nested File - - - - Add Javascript Nested File - Add Javascript Nested File + + Add {0} + Add {0} @@ -22,19 +12,9 @@ Редактор Razor с поддержкой LSP - - View CSS Nested File - View CSS Nested File - - - - View C# Nested File - View C# Nested File - - - - View Javascript Nested File - View Javascript Nested File + + View {0} + View {0} diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.tr.xlf b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.tr.xlf index 1f8c7ad58f5..c76f96840df 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.tr.xlf +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.tr.xlf @@ -2,19 +2,9 @@ - - Add CSS Nested File - Add CSS Nested File - - - - Add C# Nested File - Add C# Nested File - - - - Add Javascript Nested File - Add Javascript Nested File + + Add {0} + Add {0} @@ -22,19 +12,9 @@ LSP Razor Editor - - View CSS Nested File - View CSS Nested File - - - - View C# Nested File - View C# Nested File - - - - View Javascript Nested File - View Javascript Nested File + + View {0} + View {0} diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.zh-Hans.xlf b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.zh-Hans.xlf index dd7d96533b9..94f44765fb2 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.zh-Hans.xlf +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.zh-Hans.xlf @@ -2,19 +2,9 @@ - - Add CSS Nested File - Add CSS Nested File - - - - Add C# Nested File - Add C# Nested File - - - - Add Javascript Nested File - Add Javascript Nested File + + Add {0} + Add {0} @@ -22,19 +12,9 @@ LSP Razor 编辑器 - - View CSS Nested File - View CSS Nested File - - - - View C# Nested File - View C# Nested File - - - - View Javascript Nested File - View Javascript Nested File + + View {0} + View {0} diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.zh-Hant.xlf b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.zh-Hant.xlf index 47451d54eaf..0bbb04d3628 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.zh-Hant.xlf +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/Resources.zh-Hant.xlf @@ -2,19 +2,9 @@ - - Add CSS Nested File - Add CSS Nested File - - - - Add C# Nested File - Add C# Nested File - - - - Add Javascript Nested File - Add Javascript Nested File + + Add {0} + Add {0} @@ -22,19 +12,9 @@ LSP Razor 編輯器 - - View CSS Nested File - View CSS Nested File - - - - View C# Nested File - View C# Nested File - - - - View Javascript Nested File - View Javascript Nested File + + View {0} + View {0} From 7d4a1c1cae40777fb43694a58f59a46ec0034115 Mon Sep 17 00:00:00 2001 From: Todd Grunke Date: Wed, 1 Apr 2026 11:07:37 -0700 Subject: [PATCH 08/13] Fix two failing tests Add unit tests to validate add C# file uses editor config --- .../RazorProjectBuilder.cs | 16 +++++ .../RemoteAddNestedFileServiceTest.cs | 60 ++++++++++++++++++- 2 files changed, 74 insertions(+), 2 deletions(-) diff --git a/src/Razor/test/Microsoft.AspNetCore.Razor.Test.Common.Cohosting/RazorProjectBuilder.cs b/src/Razor/test/Microsoft.AspNetCore.Razor.Test.Common.Cohosting/RazorProjectBuilder.cs index 6e6ee72d14b..57873199879 100644 --- a/src/Razor/test/Microsoft.AspNetCore.Razor.Test.Common.Cohosting/RazorProjectBuilder.cs +++ b/src/Razor/test/Microsoft.AspNetCore.Razor.Test.Common.Cohosting/RazorProjectBuilder.cs @@ -46,6 +46,7 @@ public string? ProjectFilePath private readonly List _references = []; private readonly List<(DocumentId id, string name, SourceText text, string filePath)> _documents = []; private readonly List<(DocumentId id, string name, SourceText text, string filePath)> _additionalDocuments = []; + private readonly List<(string name, SourceText text, string filePath)> _analyzerConfigDocuments = []; internal void AddReferences(IEnumerable enumerable) { @@ -74,6 +75,12 @@ internal void AddAdditionalDocument(DocumentId id, string filePath, SourceText t _additionalDocuments.Add((id, name, text, filePath)); } + internal void AddAnalyzerConfigDocument(string filePath, SourceText text) + { + var name = Path.GetFileName(filePath); + _analyzerConfigDocuments.Add((name, text, filePath)); + } + public Solution Build(Solution solution) { var sgAssembly = typeof(RazorSourceGenerator).Assembly; @@ -156,6 +163,15 @@ public Solution Build(Solution solution) filePath: Path.Combine(projectBasePath, ".globalconfig")); } + foreach (var analyzerConfigDocument in _analyzerConfigDocuments) + { + solution = solution.AddAnalyzerConfigDocument( + DocumentId.CreateNewId(Id), + name: analyzerConfigDocument.name, + text: analyzerConfigDocument.text, + filePath: analyzerConfigDocument.filePath); + } + return solution; } } diff --git a/src/Razor/test/Microsoft.CodeAnalysis.Razor.CohostingShared.Test/NestedFiles/RemoteAddNestedFileServiceTest.cs b/src/Razor/test/Microsoft.CodeAnalysis.Razor.CohostingShared.Test/NestedFiles/RemoteAddNestedFileServiceTest.cs index bc4fec9a355..423150a3f0f 100644 --- a/src/Razor/test/Microsoft.CodeAnalysis.Razor.CohostingShared.Test/NestedFiles/RemoteAddNestedFileServiceTest.cs +++ b/src/Razor/test/Microsoft.CodeAnalysis.Razor.CohostingShared.Test/NestedFiles/RemoteAddNestedFileServiceTest.cs @@ -34,7 +34,7 @@ public async Task CssNestedFile_CreatesEmptyFile() // Second change: TextDocumentEdit with CSS comment content Assert.True(changes[1].TryGetFirst(out var textEdit)); Assert.Single(textEdit!.Edits); - Assert.Contains("Scoped CSS styles for File1 component", ((TextEdit)textEdit.Edits[0]).NewText); + Assert.Contains("CSS for File1 component", ((TextEdit)textEdit.Edits[0]).NewText); } [Fact] @@ -60,7 +60,7 @@ public async Task JavaScriptNestedFile_CreatesFileWithTemplate() Assert.True(changes[1].TryGetFirst(out var textEdit)); Assert.Single(textEdit!.Edits); var content = ((TextEdit)textEdit.Edits[0]).NewText; - Assert.Contains("JavaScript nested file for File1 component", content); + Assert.Contains("JavaScript for File1 component", content); } [Fact] @@ -169,6 +169,62 @@ public async Task CshtmlFile_CssNestedFile() Assert.Contains("Page1 view", ((TextEdit)textEdit!.Edits[0]).NewText); } + [Fact] + public async Task CSharpNestedFile_DefaultsToBlockScopedNamespace() + { + // No editorconfig present — should use block-scoped namespace (with braces) + var document = CreateProjectAndRazorDocument("
"); + var razorFileUri = new Uri(document.FilePath!); + + var result = await RemoteServiceInvoker.TryInvokeAsync( + document.Project.Solution, + (service, solutionInfo, ct) => service.AddNestedFileAsync(solutionInfo, razorFileUri, NestedFileKind.CSharp, ct), + DisposalToken); + + Assert.NotNull(result); + var changes = GetDocumentChanges(result); + Assert.True(changes[1].TryGetFirst(out var textEdit)); + var content = ((TextEdit)textEdit!.Edits[0]).NewText; + + Assert.Contains("partial class File1", content); + // Without editorconfig, namespace should be block-scoped (not file-scoped) + Assert.DoesNotContain("namespace SomeProject;", content); + Assert.Contains("namespace SomeProject", content); + } + + [Fact] + public async Task CSharpNestedFile_WithFileScopedNamespaceEditorConfig() + { + var editorConfigPath = FilePath(".editorconfig"); + var editorConfigContent = """ + root = true + + [*.cs] + csharp_style_namespace_declarations = file_scoped + """; + + var document = CreateProjectAndRazorDocument( + "
", + projectConfigure: builder => builder.AddAnalyzerConfigDocument( + editorConfigPath, + Microsoft.CodeAnalysis.Text.SourceText.From(editorConfigContent))); + var razorFileUri = new Uri(document.FilePath!); + + var result = await RemoteServiceInvoker.TryInvokeAsync( + document.Project.Solution, + (service, solutionInfo, ct) => service.AddNestedFileAsync(solutionInfo, razorFileUri, NestedFileKind.CSharp, ct), + DisposalToken); + + Assert.NotNull(result); + var changes = GetDocumentChanges(result); + Assert.True(changes[1].TryGetFirst(out var textEdit)); + var content = ((TextEdit)textEdit!.Edits[0]).NewText; + + // With file-scoped namespace editorconfig, Roslyn formatting should produce "namespace X;" + Assert.Contains("namespace SomeProject;", content); + Assert.Contains("partial class File1", content); + } + [Fact] public async Task InvalidFileKind_ReturnsNull() { From 2abc42360727a459fc684a8a780f60c229493f93 Mon Sep 17 00:00:00 2001 From: Todd Grunke Date: Wed, 1 Apr 2026 21:37:23 -0700 Subject: [PATCH 09/13] Address most of the easy PR comments --- .../src/Language/FileKinds.cs | 4 +- .../Remote/IRemoteAddNestedFileService.cs | 7 +- .../NestedFiles/RemoteAddNestedFileService.cs | 83 +++++-------------- .../Cohost/CohostAddNestedFileEndpoint.cs | 13 ++- .../NestedFiles/NestedFileCommandHandler.cs | 46 +++++----- .../RazorContextMenu.vsct | 42 +++++----- .../RazorPackage.cs | 28 +++---- .../xlf/RazorContextMenu.vsct.cs.xlf | 54 ++++++------ .../xlf/RazorContextMenu.vsct.de.xlf | 54 ++++++------ .../xlf/RazorContextMenu.vsct.es.xlf | 54 ++++++------ .../xlf/RazorContextMenu.vsct.fr.xlf | 54 ++++++------ .../xlf/RazorContextMenu.vsct.it.xlf | 54 ++++++------ .../xlf/RazorContextMenu.vsct.ja.xlf | 54 ++++++------ .../xlf/RazorContextMenu.vsct.ko.xlf | 54 ++++++------ .../xlf/RazorContextMenu.vsct.pl.xlf | 54 ++++++------ .../xlf/RazorContextMenu.vsct.pt-BR.xlf | 54 ++++++------ .../xlf/RazorContextMenu.vsct.ru.xlf | 54 ++++++------ .../xlf/RazorContextMenu.vsct.tr.xlf | 54 ++++++------ .../xlf/RazorContextMenu.vsct.zh-Hans.xlf | 54 ++++++------ .../xlf/RazorContextMenu.vsct.zh-Hant.xlf | 54 ++++++------ .../RemoteAddNestedFileServiceTest.cs | 39 +++------ 21 files changed, 460 insertions(+), 504 deletions(-) diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/FileKinds.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/FileKinds.cs index 5d23f63f489..daf2b3e162f 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/FileKinds.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/FileKinds.cs @@ -9,8 +9,8 @@ namespace Microsoft.AspNetCore.Razor.Language; public static class FileKinds { - private const string ComponentFileExtension = ".razor"; - private const string LegacyFileExtension = ".cshtml"; + public const string ComponentFileExtension = ".razor"; + public const string LegacyFileExtension = ".cshtml"; /// /// Returns if the specified value represents a component or component import. diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Remote/IRemoteAddNestedFileService.cs b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Remote/IRemoteAddNestedFileService.cs index a05769504ca..cf91816615a 100644 --- a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Remote/IRemoteAddNestedFileService.cs +++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Remote/IRemoteAddNestedFileService.cs @@ -1,7 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.ExternalAccess.Razor; @@ -11,13 +10,13 @@ namespace Microsoft.CodeAnalysis.Razor.Remote; internal interface IRemoteAddNestedFileService : IRemoteJsonService { /// - /// Creates a nested file (CSS, C# code-behind, or JavaScript) for a Razor file. + /// Gets an edit to create a nested file (CSS, C# code-behind, or JavaScript) for a Razor file. /// Returns a containing CreateFile + TextDocumentEdit operations, /// or null if the operation could not be completed. /// - ValueTask AddNestedFileAsync( + ValueTask GetNewNestedFileWorkspaceEditAsync( JsonSerializableRazorPinnedSolutionInfoWrapper solutionInfo, - Uri razorFileUri, + JsonSerializableDocumentId documentId, string fileKind, CancellationToken cancellationToken); } diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Remote.Razor/NestedFiles/RemoteAddNestedFileService.cs b/src/Razor/src/Microsoft.CodeAnalysis.Remote.Razor/NestedFiles/RemoteAddNestedFileService.cs index 435360213bc..5ff68feaf79 100644 --- a/src/Razor/src/Microsoft.CodeAnalysis.Remote.Razor/NestedFiles/RemoteAddNestedFileService.cs +++ b/src/Razor/src/Microsoft.CodeAnalysis.Remote.Razor/NestedFiles/RemoteAddNestedFileService.cs @@ -6,15 +6,12 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Razor.Language; -using Microsoft.AspNetCore.Razor.Language.Intermediate; using Microsoft.AspNetCore.Razor.PooledObjects; using Microsoft.CodeAnalysis.ExternalAccess.Razor; -using Microsoft.CodeAnalysis.Razor; using Microsoft.CodeAnalysis.Razor.CodeActions; using Microsoft.CodeAnalysis.Razor.Logging; using Microsoft.CodeAnalysis.Razor.NestedFiles; using Microsoft.CodeAnalysis.Razor.Remote; -using Microsoft.CodeAnalysis.Razor.Utilities; using Microsoft.CodeAnalysis.Razor.Workspaces; using Microsoft.CodeAnalysis.Remote.Razor.ProjectSystem; @@ -35,38 +32,26 @@ protected override IRemoteAddNestedFileService CreateService(in ServiceArgs args private readonly LanguageServerFeatureOptions _languageServerFeatureOptions = args.ExportProvider.GetExportedValue(); - public ValueTask AddNestedFileAsync( + public ValueTask GetNewNestedFileWorkspaceEditAsync( JsonSerializableRazorPinnedSolutionInfoWrapper solutionInfo, - Uri razorFileUri, + JsonSerializableDocumentId documentId, string fileKind, CancellationToken cancellationToken) => RunServiceAsync( solutionInfo, - solution => AddNestedFileAsync(solution, razorFileUri, fileKind, cancellationToken), + documentId, + context => GetNewNestedFileWorkspaceEditAsync(context, fileKind, cancellationToken), cancellationToken); - private async ValueTask AddNestedFileAsync( - Solution solution, - Uri razorFileUri, + private async ValueTask GetNewNestedFileWorkspaceEditAsync( + RemoteDocumentContext context, string fileKind, CancellationToken cancellationToken) { - if (!solution.TryGetRazorDocument(razorFileUri, out var razorDocument)) - { - Logger.LogWarning($"Could not find Razor document for URI: {razorFileUri}"); - return null; - } - - var documentContext = CreateRazorDocumentContext(solution, razorDocument.Id); - if (documentContext is null) - { - Logger.LogWarning($"Could not create document context for: {razorFileUri}"); - return null; - } + System.Diagnostics.Debugger.Launch(); - var razorFilePath = FilePathNormalizer.Normalize(razorFileUri.GetAbsoluteOrUNCPath()); - var nestedFilePath = GetNestedFilePath(razorFilePath, fileKind); - if (nestedFilePath is null) + var razorFilePath = context.FilePath; + if (GetNestedFilePath(razorFilePath, fileKind) is not string nestedFilePath) { return null; } @@ -74,7 +59,7 @@ protected override IRemoteAddNestedFileService CreateService(in ServiceArgs args var nestedFileUri = LspFactory.CreateFilePathUri(nestedFilePath, _languageServerFeatureOptions); var content = await GenerateContentAsync( - fileKind, documentContext, razorFilePath, nestedFileUri, cancellationToken).ConfigureAwait(false); + fileKind, context, razorFilePath, nestedFileUri, cancellationToken).ConfigureAwait(false); var nestedFileDocumentIdentifier = new OptionalVersionedTextDocumentIdentifier { @@ -128,14 +113,14 @@ private async Task GenerateContentAsync( private static string GenerateCssContent(string razorFilePath) { var componentName = Path.GetFileNameWithoutExtension(razorFilePath); - var fileType = FileKinds.GetFileKindFromPath(razorFilePath).IsComponent() ? "component" : "view"; + var fileType = FileKinds.GetFileKindFromPath(razorFilePath).IsLegacy() ? "view" : "component"; return $"/* CSS for {componentName} {fileType} */\r\n"; } private static string GenerateJavaScriptContent(string razorFilePath) { var componentName = Path.GetFileNameWithoutExtension(razorFilePath); - var fileType = FileKinds.GetFileKindFromPath(razorFilePath).IsComponent() ? "component" : "view"; + var fileType = FileKinds.GetFileKindFromPath(razorFilePath).IsLegacy() ? "view" : "component"; return $"// JavaScript for {componentName} {fileType}\r\n"; } @@ -150,13 +135,13 @@ private async Task GenerateCSharpContentAsync( // Use the Razor compiler's namespace resolution which handles @namespace directives, // _Imports.razor, and SDK-provided root namespace - if (!codeDocument.TryGetNamespace(fallbackToRootNamespace: true, out var @namespace)) + if (!codeDocument.TryGetNamespace(fallbackToRootNamespace: true, out var ns)) { Logger.LogWarning($"Could not determine namespace for: {razorFilePath}"); - @namespace = "Unknown"; + ns = "Unknown"; } - var content = GenerateCodeBehindClass(className, @namespace, codeDocument); + var content = GenerateCodeBehindClass(className, ns); // Format via Roslyn (handles file-scoped namespaces, indentation, etc.) content = await _roslynCodeActionHelpers.GetFormattedNewFileContentsAsync( @@ -168,40 +153,16 @@ private async Task GenerateCSharpContentAsync( return content; } - private static string GenerateCodeBehindClass(string className, string namespaceName, RazorCodeDocument razorCodeDocument) + private static string GenerateCodeBehindClass(string className, string namespaceName) { using var _ = StringBuilderPool.GetPooledObject(out var builder); - var usingDirectives = razorCodeDocument - .GetRequiredDocumentNode() - .FindDescendantNodes(); - - foreach (var usingDirective in usingDirectives) - { - builder.Append("using "); - - var content = usingDirective.Content; - var startIndex = content.StartsWith("global::", StringComparison.Ordinal) - ? 8 - : 0; - - builder.Append(content, startIndex, content.Length - startIndex); - builder.Append(';'); - builder.AppendLine(); - } - - builder.AppendLine(); - builder.Append("namespace "); - builder.AppendLine(namespaceName); - builder.Append('{'); - builder.AppendLine(); - builder.Append("public partial class "); - builder.AppendLine(className); - builder.Append('{'); - builder.AppendLine(); - builder.Append('}'); - builder.AppendLine(); - builder.Append('}'); + builder.AppendLine($"namespace {namespaceName}"); + builder.AppendLine("{"); + builder.AppendLine($"public partial class {className}"); + builder.AppendLine("{"); + builder.AppendLine("}"); + builder.AppendLine("}"); return builder.ToString(); } diff --git a/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/LanguageClient/Cohost/CohostAddNestedFileEndpoint.cs b/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/LanguageClient/Cohost/CohostAddNestedFileEndpoint.cs index 47dde2a969b..b346dc6dc1d 100644 --- a/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/LanguageClient/Cohost/CohostAddNestedFileEndpoint.cs +++ b/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/LanguageClient/Cohost/CohostAddNestedFileEndpoint.cs @@ -5,6 +5,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Razor; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.ExternalAccess.Razor; using Microsoft.CodeAnalysis.ExternalAccess.Razor.Cohost; using Microsoft.CodeAnalysis.ExternalAccess.Razor.Features; @@ -27,7 +28,7 @@ internal sealed class CohostAddNestedFileEndpoint( private readonly IRemoteServiceInvoker _remoteServiceInvoker = remoteServiceInvoker; private readonly ILogger _logger = loggerFactory.GetOrCreateLogger(); - protected override bool MutatesSolutionState => true; + protected override bool MutatesSolutionState => false; protected override bool RequiresLSPSolution => true; @@ -43,11 +44,17 @@ protected override async Task HandleRequestAsync( return new(); } + if (!solution.TryGetRazorDocument(request.RazorFileUri, out var razorDocument)) + { + _logger.LogWarning($"Could not find Razor document for URI: {request.RazorFileUri}"); + return new(); + } + var workspaceEdit = await _remoteServiceInvoker.TryInvokeAsync( solution, - (service, solutionInfo, ct) => service.AddNestedFileAsync( + (service, solutionInfo, ct) => service.GetNewNestedFileWorkspaceEditAsync( solutionInfo, - request.RazorFileUri, + razorDocument.Id, request.FileKind, ct), cancellationToken).ConfigureAwait(false); diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/NestedFiles/NestedFileCommandHandler.cs b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/NestedFiles/NestedFileCommandHandler.cs index 590290ffe99..2d5729a826a 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/NestedFiles/NestedFileCommandHandler.cs +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/NestedFiles/NestedFileCommandHandler.cs @@ -2,13 +2,16 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Diagnostics; using System.IO; using System.Runtime.InteropServices; -using System.Text.Json.Serialization; using System.Threading; using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc.Razor.Extensions; using Microsoft.AspNetCore.Razor.Language; +using Microsoft.AspNetCore.Razor.Language.Components; using Microsoft.VisualStudio.Razor.LanguageClient; +using Microsoft.VisualStudio.Razor.LanguageClient.Cohost; using Microsoft.VisualStudio.Razor.ProjectSystem; using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Shell.Interop; @@ -55,7 +58,7 @@ public void OnBeforeQueryStatus(object sender, EventArgs e) command.Visible = true; command.Enabled = true; - command.Text = string.Format(nestedFileExists ? Resources.View_Nested_File : Resources.Add_Nested_File, nestedFileName); + command.Text = nestedFileExists ? Resources.FormatView_Nested_File(nestedFileName) : Resources.FormatAdd_Nested_File(nestedFileName); } private bool IsRazorFileUIContextActive() @@ -108,10 +111,10 @@ private async Task CreateAndOpenNestedFileAsync( { // The cohost endpoint will create the file via workspace/applyEdit. // By the time this returns, the file should exist on disk. - await _requestInvoker.Value.ReinvokeRequestOnServerAsync( + await _requestInvoker.Value.ReinvokeRequestOnServerAsync( RazorLSPConstants.AddNestedFileName, RazorLSPConstants.RoslynLanguageServerName, - new AddNestedFileRequest + new AddNestedFileParams { RazorFileUri = new Uri(razorFilePath), FileKind = _fileKind, @@ -134,6 +137,7 @@ private async Task CreateAndOpenNestedFileAsync( /// protected virtual string GetNestedFilePath(string razorFilePath) { + Debug.Assert(_fileExtension.StartsWith('.')); return razorFilePath + _fileExtension; } @@ -165,9 +169,26 @@ protected virtual string GetNestedFilePath(string razorFilePath) { project.GetMkDocument(itemId, out var filePath); - if (!string.IsNullOrEmpty(filePath) && FileKinds.TryGetFileKindFromPath(filePath, out _)) + if (filePath is not null) { - return filePath; + if (filePath.EndsWith(FileKinds.ComponentFileExtension, StringComparison.OrdinalIgnoreCase)) + { + // return all paths with extension .razor except for the special imports file + var fileName = Path.GetFileNameWithoutExtension(filePath); + if (!string.Equals(fileName, ComponentHelpers.ImportsFileName, StringComparison.OrdinalIgnoreCase)) + { + return filePath; + } + } + else if (filePath.EndsWith(FileKinds.LegacyFileExtension, StringComparison.OrdinalIgnoreCase)) + { + // return all paths with extension .cshtml except for the special imports file + var fileName = Path.GetFileNameWithoutExtension(filePath); + if (!string.Equals(fileName, MvcImportProjectFeature.ImportsFileName, StringComparison.OrdinalIgnoreCase)) + { + return filePath; + } + } } } } @@ -187,17 +208,4 @@ protected virtual string GetNestedFilePath(string razorFilePath) } } } - - /// - /// Local request type matching AddNestedFileParams on the server side. - /// JSON property names must match the server's expected format. - /// - private sealed class AddNestedFileRequest - { - [JsonPropertyName("razorFileUri")] - public required Uri RazorFileUri { get; set; } - - [JsonPropertyName("fileKind")] - public required string FileKind { get; set; } - } } diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/RazorContextMenu.vsct b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/RazorContextMenu.vsct index 778b532947a..631a9f57ba7 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/RazorContextMenu.vsct +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/RazorContextMenu.vsct @@ -26,9 +26,9 @@ - - - + + + @@ -112,42 +112,42 @@ - - - @@ -155,9 +155,9 @@ - - - + + + diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/RazorPackage.cs b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/RazorPackage.cs index 8bea13b634c..9f2b7c0cd47 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/RazorPackage.cs +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/RazorPackage.cs @@ -62,9 +62,9 @@ internal sealed class RazorPackage : AsyncPackage // Razor nested files command set internal const string GuidRazorNestedFilesCmdSetString = "8B2B3C5D-6E4A-4F9B-9C8D-1A2B3C4D5E6F"; internal static readonly Guid GuidRazorNestedFilesCmdSet = new Guid(GuidRazorNestedFilesCmdSetString); - internal const uint CmdIdAddOrViewCssNestedFile = 0x0100; - internal const uint CmdIdAddOrViewCsNestedFile = 0x0101; - internal const uint CmdIdAddOrViewJsNestedFile = 0x0102; + internal const uint CmdIdAddOrViewNestedCsFile = 0x0100; + internal const uint CmdIdAddOrViewNestedCssFile = 0x0101; + internal const uint CmdIdAddOrViewNestedJsFile = 0x0102; // UI Context for when a .razor or .cshtml file is selected internal const string GuidRazorFileContextString = "7C3F2F9E-8D4A-4B6C-9E1F-5A8D7C6B3E2D"; @@ -171,24 +171,24 @@ private void RegisterNestedFileCommands(OleMenuCommandService mcs) var requestInvoker = new Lazy(() => componentModel.GetService()); // Create command handlers - var cssHandler = new CssNestedFileCommandHandler(this, requestInvoker); var csharpHandler = new CSharpNestedFileCommandHandler(this, requestInvoker); + var cssHandler = new CssNestedFileCommandHandler(this, requestInvoker); var javascriptHandler = new JavascriptNestedFileCommandHandler(this, requestInvoker); - // CSS Nested File Command - var cssCommandId = new CommandID(GuidRazorNestedFilesCmdSet, (int)CmdIdAddOrViewCssNestedFile); - var cssCommand = new OleMenuCommand(cssHandler.Execute, cssCommandId); - cssCommand.BeforeQueryStatus += cssHandler.OnBeforeQueryStatus; - mcs.AddCommand(cssCommand); - - // C# Code-Behind Nested File Command - var csharpCommandId = new CommandID(GuidRazorNestedFilesCmdSet, (int)CmdIdAddOrViewCsNestedFile); + // .cs Nested File Command + var csharpCommandId = new CommandID(GuidRazorNestedFilesCmdSet, (int)CmdIdAddOrViewNestedCsFile); var csharpCommand = new OleMenuCommand(csharpHandler.Execute, csharpCommandId); csharpCommand.BeforeQueryStatus += csharpHandler.OnBeforeQueryStatus; mcs.AddCommand(csharpCommand); - // JS Nested File Command - var javascriptCommandId = new CommandID(GuidRazorNestedFilesCmdSet, (int)CmdIdAddOrViewJsNestedFile); + // .css Nested File Command + var cssCommandId = new CommandID(GuidRazorNestedFilesCmdSet, (int)CmdIdAddOrViewNestedCssFile); + var cssCommand = new OleMenuCommand(cssHandler.Execute, cssCommandId); + cssCommand.BeforeQueryStatus += cssHandler.OnBeforeQueryStatus; + mcs.AddCommand(cssCommand); + + // .js Nested File Command + var javascriptCommandId = new CommandID(GuidRazorNestedFilesCmdSet, (int)CmdIdAddOrViewNestedJsFile); var javascriptCommand = new OleMenuCommand(javascriptHandler.Execute, javascriptCommandId); javascriptCommand.BeforeQueryStatus += javascriptHandler.OnBeforeQueryStatus; mcs.AddCommand(javascriptCommand); diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.cs.xlf b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.cs.xlf index 11762e286f1..441f4f6ac56 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.cs.xlf +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.cs.xlf @@ -17,49 +17,49 @@ Show Referenced Tag Helpers
- - Add or View C# Code-Behind File - Add or View C# Code-Behind File + + Add or View Nested .cs File + Add or View Nested .cs File - - AddOrViewCsNestedFile - AddOrViewCsNestedFile + + AddOrViewNestedCsFile + AddOrViewNestedCsFile - - AddOrViewCsNestedFile - AddOrViewCsNestedFile + + AddOrViewNestedCsFile + AddOrViewNestedCsFile - - Add or View CSS Nested File - Add or View CSS Nested File + + Add or View Nested .css File + Add or View Nested .css File - - AddOrViewCssNestedFile - AddOrViewCssNestedFile + + AddOrViewNestedCssFile + AddOrViewNestedCssFile - - AddOrViewCssNestedFile - AddOrViewCssNestedFile + + AddOrViewNestedCssFile + AddOrViewNestedCssFile - - Add or View JavaScript Nested File - Add or View JavaScript Nested File + + Add or View Nested .js File + Add or View Nested .js File - - AddOrViewJsNestedFile - AddOrViewJsNestedFile + + AddOrViewNestedJsFile + AddOrViewNestedJsFile - - AddOrViewJsNestedFile - AddOrViewJsNestedFile + + AddOrViewNestedJsFile + AddOrViewNestedJsFile diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.de.xlf b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.de.xlf index f95d205e009..2ffc8649b2e 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.de.xlf +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.de.xlf @@ -17,49 +17,49 @@ Show Referenced Tag Helpers - - Add or View C# Code-Behind File - Add or View C# Code-Behind File + + Add or View Nested .cs File + Add or View Nested .cs File - - AddOrViewCsNestedFile - AddOrViewCsNestedFile + + AddOrViewNestedCsFile + AddOrViewNestedCsFile - - AddOrViewCsNestedFile - AddOrViewCsNestedFile + + AddOrViewNestedCsFile + AddOrViewNestedCsFile - - Add or View CSS Nested File - Add or View CSS Nested File + + Add or View Nested .css File + Add or View Nested .css File - - AddOrViewCssNestedFile - AddOrViewCssNestedFile + + AddOrViewNestedCssFile + AddOrViewNestedCssFile - - AddOrViewCssNestedFile - AddOrViewCssNestedFile + + AddOrViewNestedCssFile + AddOrViewNestedCssFile - - Add or View JavaScript Nested File - Add or View JavaScript Nested File + + Add or View Nested .js File + Add or View Nested .js File - - AddOrViewJsNestedFile - AddOrViewJsNestedFile + + AddOrViewNestedJsFile + AddOrViewNestedJsFile - - AddOrViewJsNestedFile - AddOrViewJsNestedFile + + AddOrViewNestedJsFile + AddOrViewNestedJsFile diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.es.xlf b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.es.xlf index 61451673083..e145f0904a1 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.es.xlf +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.es.xlf @@ -17,49 +17,49 @@ Show Referenced Tag Helpers - - Add or View C# Code-Behind File - Add or View C# Code-Behind File + + Add or View Nested .cs File + Add or View Nested .cs File - - AddOrViewCsNestedFile - AddOrViewCsNestedFile + + AddOrViewNestedCsFile + AddOrViewNestedCsFile - - AddOrViewCsNestedFile - AddOrViewCsNestedFile + + AddOrViewNestedCsFile + AddOrViewNestedCsFile - - Add or View CSS Nested File - Add or View CSS Nested File + + Add or View Nested .css File + Add or View Nested .css File - - AddOrViewCssNestedFile - AddOrViewCssNestedFile + + AddOrViewNestedCssFile + AddOrViewNestedCssFile - - AddOrViewCssNestedFile - AddOrViewCssNestedFile + + AddOrViewNestedCssFile + AddOrViewNestedCssFile - - Add or View JavaScript Nested File - Add or View JavaScript Nested File + + Add or View Nested .js File + Add or View Nested .js File - - AddOrViewJsNestedFile - AddOrViewJsNestedFile + + AddOrViewNestedJsFile + AddOrViewNestedJsFile - - AddOrViewJsNestedFile - AddOrViewJsNestedFile + + AddOrViewNestedJsFile + AddOrViewNestedJsFile diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.fr.xlf b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.fr.xlf index a7d94ed12d5..0ec4b1986b7 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.fr.xlf +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.fr.xlf @@ -17,49 +17,49 @@ Show Referenced Tag Helpers - - Add or View C# Code-Behind File - Add or View C# Code-Behind File + + Add or View Nested .cs File + Add or View Nested .cs File - - AddOrViewCsNestedFile - AddOrViewCsNestedFile + + AddOrViewNestedCsFile + AddOrViewNestedCsFile - - AddOrViewCsNestedFile - AddOrViewCsNestedFile + + AddOrViewNestedCsFile + AddOrViewNestedCsFile - - Add or View CSS Nested File - Add or View CSS Nested File + + Add or View Nested .css File + Add or View Nested .css File - - AddOrViewCssNestedFile - AddOrViewCssNestedFile + + AddOrViewNestedCssFile + AddOrViewNestedCssFile - - AddOrViewCssNestedFile - AddOrViewCssNestedFile + + AddOrViewNestedCssFile + AddOrViewNestedCssFile - - Add or View JavaScript Nested File - Add or View JavaScript Nested File + + Add or View Nested .js File + Add or View Nested .js File - - AddOrViewJsNestedFile - AddOrViewJsNestedFile + + AddOrViewNestedJsFile + AddOrViewNestedJsFile - - AddOrViewJsNestedFile - AddOrViewJsNestedFile + + AddOrViewNestedJsFile + AddOrViewNestedJsFile diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.it.xlf b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.it.xlf index 8b7824c7e6c..450b0af7cdf 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.it.xlf +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.it.xlf @@ -17,49 +17,49 @@ Show Referenced Tag Helpers - - Add or View C# Code-Behind File - Add or View C# Code-Behind File + + Add or View Nested .cs File + Add or View Nested .cs File - - AddOrViewCsNestedFile - AddOrViewCsNestedFile + + AddOrViewNestedCsFile + AddOrViewNestedCsFile - - AddOrViewCsNestedFile - AddOrViewCsNestedFile + + AddOrViewNestedCsFile + AddOrViewNestedCsFile - - Add or View CSS Nested File - Add or View CSS Nested File + + Add or View Nested .css File + Add or View Nested .css File - - AddOrViewCssNestedFile - AddOrViewCssNestedFile + + AddOrViewNestedCssFile + AddOrViewNestedCssFile - - AddOrViewCssNestedFile - AddOrViewCssNestedFile + + AddOrViewNestedCssFile + AddOrViewNestedCssFile - - Add or View JavaScript Nested File - Add or View JavaScript Nested File + + Add or View Nested .js File + Add or View Nested .js File - - AddOrViewJsNestedFile - AddOrViewJsNestedFile + + AddOrViewNestedJsFile + AddOrViewNestedJsFile - - AddOrViewJsNestedFile - AddOrViewJsNestedFile + + AddOrViewNestedJsFile + AddOrViewNestedJsFile diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.ja.xlf b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.ja.xlf index 9e643ca4200..fa1ef6debc1 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.ja.xlf +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.ja.xlf @@ -17,49 +17,49 @@ Show Referenced Tag Helpers - - Add or View C# Code-Behind File - Add or View C# Code-Behind File + + Add or View Nested .cs File + Add or View Nested .cs File - - AddOrViewCsNestedFile - AddOrViewCsNestedFile + + AddOrViewNestedCsFile + AddOrViewNestedCsFile - - AddOrViewCsNestedFile - AddOrViewCsNestedFile + + AddOrViewNestedCsFile + AddOrViewNestedCsFile - - Add or View CSS Nested File - Add or View CSS Nested File + + Add or View Nested .css File + Add or View Nested .css File - - AddOrViewCssNestedFile - AddOrViewCssNestedFile + + AddOrViewNestedCssFile + AddOrViewNestedCssFile - - AddOrViewCssNestedFile - AddOrViewCssNestedFile + + AddOrViewNestedCssFile + AddOrViewNestedCssFile - - Add or View JavaScript Nested File - Add or View JavaScript Nested File + + Add or View Nested .js File + Add or View Nested .js File - - AddOrViewJsNestedFile - AddOrViewJsNestedFile + + AddOrViewNestedJsFile + AddOrViewNestedJsFile - - AddOrViewJsNestedFile - AddOrViewJsNestedFile + + AddOrViewNestedJsFile + AddOrViewNestedJsFile diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.ko.xlf b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.ko.xlf index 142d3482857..3df606b4734 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.ko.xlf +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.ko.xlf @@ -17,49 +17,49 @@ Show Referenced Tag Helpers - - Add or View C# Code-Behind File - Add or View C# Code-Behind File + + Add or View Nested .cs File + Add or View Nested .cs File - - AddOrViewCsNestedFile - AddOrViewCsNestedFile + + AddOrViewNestedCsFile + AddOrViewNestedCsFile - - AddOrViewCsNestedFile - AddOrViewCsNestedFile + + AddOrViewNestedCsFile + AddOrViewNestedCsFile - - Add or View CSS Nested File - Add or View CSS Nested File + + Add or View Nested .css File + Add or View Nested .css File - - AddOrViewCssNestedFile - AddOrViewCssNestedFile + + AddOrViewNestedCssFile + AddOrViewNestedCssFile - - AddOrViewCssNestedFile - AddOrViewCssNestedFile + + AddOrViewNestedCssFile + AddOrViewNestedCssFile - - Add or View JavaScript Nested File - Add or View JavaScript Nested File + + Add or View Nested .js File + Add or View Nested .js File - - AddOrViewJsNestedFile - AddOrViewJsNestedFile + + AddOrViewNestedJsFile + AddOrViewNestedJsFile - - AddOrViewJsNestedFile - AddOrViewJsNestedFile + + AddOrViewNestedJsFile + AddOrViewNestedJsFile diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.pl.xlf b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.pl.xlf index 34a5486a5ec..648a9379993 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.pl.xlf +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.pl.xlf @@ -17,49 +17,49 @@ Show Referenced Tag Helpers - - Add or View C# Code-Behind File - Add or View C# Code-Behind File + + Add or View Nested .cs File + Add or View Nested .cs File - - AddOrViewCsNestedFile - AddOrViewCsNestedFile + + AddOrViewNestedCsFile + AddOrViewNestedCsFile - - AddOrViewCsNestedFile - AddOrViewCsNestedFile + + AddOrViewNestedCsFile + AddOrViewNestedCsFile - - Add or View CSS Nested File - Add or View CSS Nested File + + Add or View Nested .css File + Add or View Nested .css File - - AddOrViewCssNestedFile - AddOrViewCssNestedFile + + AddOrViewNestedCssFile + AddOrViewNestedCssFile - - AddOrViewCssNestedFile - AddOrViewCssNestedFile + + AddOrViewNestedCssFile + AddOrViewNestedCssFile - - Add or View JavaScript Nested File - Add or View JavaScript Nested File + + Add or View Nested .js File + Add or View Nested .js File - - AddOrViewJsNestedFile - AddOrViewJsNestedFile + + AddOrViewNestedJsFile + AddOrViewNestedJsFile - - AddOrViewJsNestedFile - AddOrViewJsNestedFile + + AddOrViewNestedJsFile + AddOrViewNestedJsFile diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.pt-BR.xlf b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.pt-BR.xlf index 6198df502bc..c53377fdd04 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.pt-BR.xlf +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.pt-BR.xlf @@ -17,49 +17,49 @@ Show Referenced Tag Helpers - - Add or View C# Code-Behind File - Add or View C# Code-Behind File + + Add or View Nested .cs File + Add or View Nested .cs File - - AddOrViewCsNestedFile - AddOrViewCsNestedFile + + AddOrViewNestedCsFile + AddOrViewNestedCsFile - - AddOrViewCsNestedFile - AddOrViewCsNestedFile + + AddOrViewNestedCsFile + AddOrViewNestedCsFile - - Add or View CSS Nested File - Add or View CSS Nested File + + Add or View Nested .css File + Add or View Nested .css File - - AddOrViewCssNestedFile - AddOrViewCssNestedFile + + AddOrViewNestedCssFile + AddOrViewNestedCssFile - - AddOrViewCssNestedFile - AddOrViewCssNestedFile + + AddOrViewNestedCssFile + AddOrViewNestedCssFile - - Add or View JavaScript Nested File - Add or View JavaScript Nested File + + Add or View Nested .js File + Add or View Nested .js File - - AddOrViewJsNestedFile - AddOrViewJsNestedFile + + AddOrViewNestedJsFile + AddOrViewNestedJsFile - - AddOrViewJsNestedFile - AddOrViewJsNestedFile + + AddOrViewNestedJsFile + AddOrViewNestedJsFile diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.ru.xlf b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.ru.xlf index e139823d6ed..540469d8c9a 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.ru.xlf +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.ru.xlf @@ -17,49 +17,49 @@ Show Referenced Tag Helpers - - Add or View C# Code-Behind File - Add or View C# Code-Behind File + + Add or View Nested .cs File + Add or View Nested .cs File - - AddOrViewCsNestedFile - AddOrViewCsNestedFile + + AddOrViewNestedCsFile + AddOrViewNestedCsFile - - AddOrViewCsNestedFile - AddOrViewCsNestedFile + + AddOrViewNestedCsFile + AddOrViewNestedCsFile - - Add or View CSS Nested File - Add or View CSS Nested File + + Add or View Nested .css File + Add or View Nested .css File - - AddOrViewCssNestedFile - AddOrViewCssNestedFile + + AddOrViewNestedCssFile + AddOrViewNestedCssFile - - AddOrViewCssNestedFile - AddOrViewCssNestedFile + + AddOrViewNestedCssFile + AddOrViewNestedCssFile - - Add or View JavaScript Nested File - Add or View JavaScript Nested File + + Add or View Nested .js File + Add or View Nested .js File - - AddOrViewJsNestedFile - AddOrViewJsNestedFile + + AddOrViewNestedJsFile + AddOrViewNestedJsFile - - AddOrViewJsNestedFile - AddOrViewJsNestedFile + + AddOrViewNestedJsFile + AddOrViewNestedJsFile diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.tr.xlf b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.tr.xlf index b1b71cd28a7..9b3bccc1f18 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.tr.xlf +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.tr.xlf @@ -17,49 +17,49 @@ Show Referenced Tag Helpers - - Add or View C# Code-Behind File - Add or View C# Code-Behind File + + Add or View Nested .cs File + Add or View Nested .cs File - - AddOrViewCsNestedFile - AddOrViewCsNestedFile + + AddOrViewNestedCsFile + AddOrViewNestedCsFile - - AddOrViewCsNestedFile - AddOrViewCsNestedFile + + AddOrViewNestedCsFile + AddOrViewNestedCsFile - - Add or View CSS Nested File - Add or View CSS Nested File + + Add or View Nested .css File + Add or View Nested .css File - - AddOrViewCssNestedFile - AddOrViewCssNestedFile + + AddOrViewNestedCssFile + AddOrViewNestedCssFile - - AddOrViewCssNestedFile - AddOrViewCssNestedFile + + AddOrViewNestedCssFile + AddOrViewNestedCssFile - - Add or View JavaScript Nested File - Add or View JavaScript Nested File + + Add or View Nested .js File + Add or View Nested .js File - - AddOrViewJsNestedFile - AddOrViewJsNestedFile + + AddOrViewNestedJsFile + AddOrViewNestedJsFile - - AddOrViewJsNestedFile - AddOrViewJsNestedFile + + AddOrViewNestedJsFile + AddOrViewNestedJsFile diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.zh-Hans.xlf b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.zh-Hans.xlf index 7b26d0c4a12..9dc61d87f92 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.zh-Hans.xlf +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.zh-Hans.xlf @@ -17,49 +17,49 @@ Show Referenced Tag Helpers - - Add or View C# Code-Behind File - Add or View C# Code-Behind File + + Add or View Nested .cs File + Add or View Nested .cs File - - AddOrViewCsNestedFile - AddOrViewCsNestedFile + + AddOrViewNestedCsFile + AddOrViewNestedCsFile - - AddOrViewCsNestedFile - AddOrViewCsNestedFile + + AddOrViewNestedCsFile + AddOrViewNestedCsFile - - Add or View CSS Nested File - Add or View CSS Nested File + + Add or View Nested .css File + Add or View Nested .css File - - AddOrViewCssNestedFile - AddOrViewCssNestedFile + + AddOrViewNestedCssFile + AddOrViewNestedCssFile - - AddOrViewCssNestedFile - AddOrViewCssNestedFile + + AddOrViewNestedCssFile + AddOrViewNestedCssFile - - Add or View JavaScript Nested File - Add or View JavaScript Nested File + + Add or View Nested .js File + Add or View Nested .js File - - AddOrViewJsNestedFile - AddOrViewJsNestedFile + + AddOrViewNestedJsFile + AddOrViewNestedJsFile - - AddOrViewJsNestedFile - AddOrViewJsNestedFile + + AddOrViewNestedJsFile + AddOrViewNestedJsFile diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.zh-Hant.xlf b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.zh-Hant.xlf index ccec12f21a1..1f93e32e77d 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.zh-Hant.xlf +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/xlf/RazorContextMenu.vsct.zh-Hant.xlf @@ -17,49 +17,49 @@ Show Referenced Tag Helpers - - Add or View C# Code-Behind File - Add or View C# Code-Behind File + + Add or View Nested .cs File + Add or View Nested .cs File - - AddOrViewCsNestedFile - AddOrViewCsNestedFile + + AddOrViewNestedCsFile + AddOrViewNestedCsFile - - AddOrViewCsNestedFile - AddOrViewCsNestedFile + + AddOrViewNestedCsFile + AddOrViewNestedCsFile - - Add or View CSS Nested File - Add or View CSS Nested File + + Add or View Nested .css File + Add or View Nested .css File - - AddOrViewCssNestedFile - AddOrViewCssNestedFile + + AddOrViewNestedCssFile + AddOrViewNestedCssFile - - AddOrViewCssNestedFile - AddOrViewCssNestedFile + + AddOrViewNestedCssFile + AddOrViewNestedCssFile - - Add or View JavaScript Nested File - Add or View JavaScript Nested File + + Add or View Nested .js File + Add or View Nested .js File - - AddOrViewJsNestedFile - AddOrViewJsNestedFile + + AddOrViewNestedJsFile + AddOrViewNestedJsFile - - AddOrViewJsNestedFile - AddOrViewJsNestedFile + + AddOrViewNestedJsFile + AddOrViewNestedJsFile diff --git a/src/Razor/test/Microsoft.CodeAnalysis.Razor.CohostingShared.Test/NestedFiles/RemoteAddNestedFileServiceTest.cs b/src/Razor/test/Microsoft.CodeAnalysis.Razor.CohostingShared.Test/NestedFiles/RemoteAddNestedFileServiceTest.cs index 423150a3f0f..308eb215ba4 100644 --- a/src/Razor/test/Microsoft.CodeAnalysis.Razor.CohostingShared.Test/NestedFiles/RemoteAddNestedFileServiceTest.cs +++ b/src/Razor/test/Microsoft.CodeAnalysis.Razor.CohostingShared.Test/NestedFiles/RemoteAddNestedFileServiceTest.cs @@ -1,7 +1,6 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Razor.NestedFiles; using Microsoft.CodeAnalysis.Razor.Remote; @@ -16,11 +15,9 @@ public class RemoteAddNestedFileServiceTest(ITestOutputHelper testOutputHelper) public async Task CssNestedFile_CreatesEmptyFile() { var document = CreateProjectAndRazorDocument("
"); - var razorFileUri = new Uri(document.FilePath!); - var result = await RemoteServiceInvoker.TryInvokeAsync( document.Project.Solution, - (service, solutionInfo, ct) => service.AddNestedFileAsync(solutionInfo, razorFileUri, NestedFileKind.Css, ct), + (service, solutionInfo, ct) => service.GetNewNestedFileWorkspaceEditAsync(solutionInfo, document.Id, NestedFileKind.Css, ct), DisposalToken); Assert.NotNull(result); @@ -41,11 +38,9 @@ public async Task CssNestedFile_CreatesEmptyFile() public async Task JavaScriptNestedFile_CreatesFileWithTemplate() { var document = CreateProjectAndRazorDocument("
"); - var razorFileUri = new Uri(document.FilePath!); - var result = await RemoteServiceInvoker.TryInvokeAsync( document.Project.Solution, - (service, solutionInfo, ct) => service.AddNestedFileAsync(solutionInfo, razorFileUri, NestedFileKind.JavaScript, ct), + (service, solutionInfo, ct) => service.GetNewNestedFileWorkspaceEditAsync(solutionInfo, document.Id, NestedFileKind.JavaScript, ct), DisposalToken); Assert.NotNull(result); @@ -67,11 +62,9 @@ public async Task JavaScriptNestedFile_CreatesFileWithTemplate() public async Task CSharpNestedFile_GeneratesCodeBehind() { var document = CreateProjectAndRazorDocument("
"); - var razorFileUri = new Uri(document.FilePath!); - var result = await RemoteServiceInvoker.TryInvokeAsync( document.Project.Solution, - (service, solutionInfo, ct) => service.AddNestedFileAsync(solutionInfo, razorFileUri, NestedFileKind.CSharp, ct), + (service, solutionInfo, ct) => service.GetNewNestedFileWorkspaceEditAsync(solutionInfo, document.Id, NestedFileKind.CSharp, ct), DisposalToken); Assert.NotNull(result); @@ -101,11 +94,9 @@ @namespace My.Custom.Namespace
"""); - var razorFileUri = new Uri(document.FilePath!); - var result = await RemoteServiceInvoker.TryInvokeAsync( document.Project.Solution, - (service, solutionInfo, ct) => service.AddNestedFileAsync(solutionInfo, razorFileUri, NestedFileKind.CSharp, ct), + (service, solutionInfo, ct) => service.GetNewNestedFileWorkspaceEditAsync(solutionInfo, document.Id, NestedFileKind.CSharp, ct), DisposalToken); Assert.NotNull(result); @@ -127,11 +118,9 @@ @using System.Diagnostics
"""); - var razorFileUri = new Uri(document.FilePath!); - var result = await RemoteServiceInvoker.TryInvokeAsync( document.Project.Solution, - (service, solutionInfo, ct) => service.AddNestedFileAsync(solutionInfo, razorFileUri, NestedFileKind.CSharp, ct), + (service, solutionInfo, ct) => service.GetNewNestedFileWorkspaceEditAsync(solutionInfo, document.Id, NestedFileKind.CSharp, ct), DisposalToken); Assert.NotNull(result); @@ -152,11 +141,9 @@ public async Task CshtmlFile_CssNestedFile() "
", fileKind: AspNetCore.Razor.Language.RazorFileKind.Legacy, documentFilePath: FilePath("Page1.cshtml")); - var razorFileUri = new Uri(document.FilePath!); - var result = await RemoteServiceInvoker.TryInvokeAsync( document.Project.Solution, - (service, solutionInfo, ct) => service.AddNestedFileAsync(solutionInfo, razorFileUri, NestedFileKind.Css, ct), + (service, solutionInfo, ct) => service.GetNewNestedFileWorkspaceEditAsync(solutionInfo, document.Id, NestedFileKind.Css, ct), DisposalToken); Assert.NotNull(result); @@ -174,11 +161,9 @@ public async Task CSharpNestedFile_DefaultsToBlockScopedNamespace() { // No editorconfig present — should use block-scoped namespace (with braces) var document = CreateProjectAndRazorDocument("
"); - var razorFileUri = new Uri(document.FilePath!); - var result = await RemoteServiceInvoker.TryInvokeAsync( document.Project.Solution, - (service, solutionInfo, ct) => service.AddNestedFileAsync(solutionInfo, razorFileUri, NestedFileKind.CSharp, ct), + (service, solutionInfo, ct) => service.GetNewNestedFileWorkspaceEditAsync(solutionInfo, document.Id, NestedFileKind.CSharp, ct), DisposalToken); Assert.NotNull(result); @@ -208,11 +193,9 @@ public async Task CSharpNestedFile_WithFileScopedNamespaceEditorConfig() projectConfigure: builder => builder.AddAnalyzerConfigDocument( editorConfigPath, Microsoft.CodeAnalysis.Text.SourceText.From(editorConfigContent))); - var razorFileUri = new Uri(document.FilePath!); - var result = await RemoteServiceInvoker.TryInvokeAsync( document.Project.Solution, - (service, solutionInfo, ct) => service.AddNestedFileAsync(solutionInfo, razorFileUri, NestedFileKind.CSharp, ct), + (service, solutionInfo, ct) => service.GetNewNestedFileWorkspaceEditAsync(solutionInfo, document.Id, NestedFileKind.CSharp, ct), DisposalToken); Assert.NotNull(result); @@ -229,11 +212,9 @@ public async Task CSharpNestedFile_WithFileScopedNamespaceEditorConfig() public async Task InvalidFileKind_ReturnsNull() { var document = CreateProjectAndRazorDocument("
"); - var razorFileUri = new Uri(document.FilePath!); - var result = await RemoteServiceInvoker.TryInvokeAsync( document.Project.Solution, - (service, solutionInfo, ct) => service.AddNestedFileAsync(solutionInfo, razorFileUri, "invalid", ct), + (service, solutionInfo, ct) => service.GetNewNestedFileWorkspaceEditAsync(solutionInfo, document.Id, "invalid", ct), DisposalToken); Assert.Null(result); From df12c464fe4fda84297c27d0d428496f67e2dac2 Mon Sep 17 00:00:00 2001 From: Todd Grunke Date: Wed, 1 Apr 2026 22:09:03 -0700 Subject: [PATCH 10/13] Convert CohostAddNestedFileEndpoint to document-scoped endpoint pattern Refactor CohostAddNestedFileEndpoint to extend AbstractCohostDocumentEndpoint instead of AbstractRazorCohostRequestHandler, per PR review feedback. The base class now handles document resolution automatically. - Move AddNestedFileParams to Razor.Workspaces/Protocol/NestedFiles so both the cohost endpoint and RazorExtension command handler can share it. Add a Create() factory method (matching DocumentContentsRequest pattern) so callers don't need direct access to TextDocumentIdentifier. - Implement ITextDocumentParams on AddNestedFileParams with TextDocumentIdentifier. - Switch to [CohostEndpoint]/[ExportCohostStatelessLspService] attributes. - Use Assumed.Unreachable for the abstract 3-param HandleRequestAsync override (same pattern as CohostInlineCompletionEndpoint). - Remove unused code from RemoteAddNestedFileService. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../NestedFiles/AddNestedFileParams.cs | 35 +++++++++++++++++++ .../NestedFiles/RemoteAddNestedFileService.cs | 2 -- .../Cohost/AddNestedFileParams.cs | 26 -------------- .../Cohost/CohostAddNestedFileEndpoint.cs | 34 +++++++++--------- .../NestedFiles/NestedFileCommandHandler.cs | 8 ++--- 5 files changed, 54 insertions(+), 51 deletions(-) create mode 100644 src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Protocol/NestedFiles/AddNestedFileParams.cs delete mode 100644 src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/LanguageClient/Cohost/AddNestedFileParams.cs diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Protocol/NestedFiles/AddNestedFileParams.cs b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Protocol/NestedFiles/AddNestedFileParams.cs new file mode 100644 index 00000000000..69c535840b6 --- /dev/null +++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Protocol/NestedFiles/AddNestedFileParams.cs @@ -0,0 +1,35 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Text.Json.Serialization; +using Microsoft.CodeAnalysis.Razor.NestedFiles; + +namespace Microsoft.CodeAnalysis.Razor.Protocol.NestedFiles; + +/// +/// Parameters for the razor/addNestedFile endpoint. +/// +internal sealed class AddNestedFileParams : ITextDocumentParams +{ + /// + /// The text document identifier for the Razor file (.razor or .cshtml) to create a nested file for. + /// + [JsonPropertyName("textDocument")] + public required TextDocumentIdentifier TextDocument { get; set; } + + /// + /// The kind of nested file to create (one of constants). + /// + [JsonPropertyName("fileKind")] + public required string FileKind { get; set; } + + public static AddNestedFileParams Create(Uri razorFileUri, string fileKind) + { + return new AddNestedFileParams + { + TextDocument = new TextDocumentIdentifier { DocumentUri = new(razorFileUri) }, + FileKind = fileKind + }; + } +} diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Remote.Razor/NestedFiles/RemoteAddNestedFileService.cs b/src/Razor/src/Microsoft.CodeAnalysis.Remote.Razor/NestedFiles/RemoteAddNestedFileService.cs index 5ff68feaf79..67d2703acf0 100644 --- a/src/Razor/src/Microsoft.CodeAnalysis.Remote.Razor/NestedFiles/RemoteAddNestedFileService.cs +++ b/src/Razor/src/Microsoft.CodeAnalysis.Remote.Razor/NestedFiles/RemoteAddNestedFileService.cs @@ -48,8 +48,6 @@ protected override IRemoteAddNestedFileService CreateService(in ServiceArgs args string fileKind, CancellationToken cancellationToken) { - System.Diagnostics.Debugger.Launch(); - var razorFilePath = context.FilePath; if (GetNestedFilePath(razorFilePath, fileKind) is not string nestedFilePath) { diff --git a/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/LanguageClient/Cohost/AddNestedFileParams.cs b/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/LanguageClient/Cohost/AddNestedFileParams.cs deleted file mode 100644 index 75e5400100b..00000000000 --- a/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/LanguageClient/Cohost/AddNestedFileParams.cs +++ /dev/null @@ -1,26 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Text.Json.Serialization; -using Microsoft.CodeAnalysis.Razor.NestedFiles; - -namespace Microsoft.VisualStudio.Razor.LanguageClient.Cohost; - -/// -/// Parameters for the razor/addNestedFile endpoint. -/// -internal sealed class AddNestedFileParams -{ - /// - /// The URI of the Razor file (.razor or .cshtml) to create a nested file for. - /// - [JsonPropertyName("razorFileUri")] - public required Uri RazorFileUri { get; set; } - - /// - /// The kind of nested file to create (one of constants). - /// - [JsonPropertyName("fileKind")] - public required string FileKind { get; set; } -} diff --git a/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/LanguageClient/Cohost/CohostAddNestedFileEndpoint.cs b/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/LanguageClient/Cohost/CohostAddNestedFileEndpoint.cs index b346dc6dc1d..1a652e9ae97 100644 --- a/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/LanguageClient/Cohost/CohostAddNestedFileEndpoint.cs +++ b/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/LanguageClient/Cohost/CohostAddNestedFileEndpoint.cs @@ -9,21 +9,24 @@ using Microsoft.CodeAnalysis.ExternalAccess.Razor; using Microsoft.CodeAnalysis.ExternalAccess.Razor.Cohost; using Microsoft.CodeAnalysis.ExternalAccess.Razor.Features; +using Microsoft.CodeAnalysis.Razor.Cohost; using Microsoft.CodeAnalysis.Razor.Logging; +using Microsoft.CodeAnalysis.Razor.Protocol.NestedFiles; using Microsoft.CodeAnalysis.Razor.Remote; namespace Microsoft.VisualStudio.Razor.LanguageClient.Cohost; #pragma warning disable RS0030 // Do not use banned APIs [Shared] -[RazorMethod(RazorLSPConstants.AddNestedFileName)] -[ExportRazorStatelessLspService(typeof(CohostAddNestedFileEndpoint))] +[CohostEndpoint(RazorLSPConstants.AddNestedFileName)] +[ExportCohostStatelessLspService(typeof(CohostAddNestedFileEndpoint))] [method: ImportingConstructor] #pragma warning restore RS0030 // Do not use banned APIs internal sealed class CohostAddNestedFileEndpoint( IRemoteServiceInvoker remoteServiceInvoker, + IIncompatibleProjectService incompatibleProjectService, ILoggerFactory loggerFactory) - : AbstractRazorCohostRequestHandler + : AbstractCohostDocumentEndpoint(incompatibleProjectService) { private readonly IRemoteServiceInvoker _remoteServiceInvoker = remoteServiceInvoker; private readonly ILogger _logger = loggerFactory.GetOrCreateLogger(); @@ -32,26 +35,23 @@ internal sealed class CohostAddNestedFileEndpoint( protected override bool RequiresLSPSolution => true; + protected override RazorTextDocumentIdentifier? GetRazorTextDocumentIdentifier(AddNestedFileParams request) + => request.TextDocument.ToRazorTextDocumentIdentifier(); + + protected override Task HandleRequestAsync( + AddNestedFileParams request, + TextDocument razorDocument, + CancellationToken cancellationToken) + => Assumed.Unreachable>(); + protected override async Task HandleRequestAsync( AddNestedFileParams request, RazorCohostRequestContext context, + TextDocument razorDocument, CancellationToken cancellationToken) { - var solution = context.Solution; - if (solution is null) - { - _logger.LogWarning($"No solution available for addNestedFile request."); - return new(); - } - - if (!solution.TryGetRazorDocument(request.RazorFileUri, out var razorDocument)) - { - _logger.LogWarning($"Could not find Razor document for URI: {request.RazorFileUri}"); - return new(); - } - var workspaceEdit = await _remoteServiceInvoker.TryInvokeAsync( - solution, + razorDocument.Project.Solution, (service, solutionInfo, ct) => service.GetNewNestedFileWorkspaceEditAsync( solutionInfo, razorDocument.Id, diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/NestedFiles/NestedFileCommandHandler.cs b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/NestedFiles/NestedFileCommandHandler.cs index 2d5729a826a..c36ad3eb848 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/NestedFiles/NestedFileCommandHandler.cs +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/NestedFiles/NestedFileCommandHandler.cs @@ -10,8 +10,8 @@ using Microsoft.AspNetCore.Mvc.Razor.Extensions; using Microsoft.AspNetCore.Razor.Language; using Microsoft.AspNetCore.Razor.Language.Components; +using Microsoft.CodeAnalysis.Razor.Protocol.NestedFiles; using Microsoft.VisualStudio.Razor.LanguageClient; -using Microsoft.VisualStudio.Razor.LanguageClient.Cohost; using Microsoft.VisualStudio.Razor.ProjectSystem; using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Shell.Interop; @@ -114,11 +114,7 @@ private async Task CreateAndOpenNestedFileAsync( await _requestInvoker.Value.ReinvokeRequestOnServerAsync( RazorLSPConstants.AddNestedFileName, RazorLSPConstants.RoslynLanguageServerName, - new AddNestedFileParams - { - RazorFileUri = new Uri(razorFilePath), - FileKind = _fileKind, - }, + AddNestedFileParams.Create(new Uri(razorFilePath), _fileKind), cancellationToken); if (File.Exists(nestedFilePath)) From 4a2283f1123a6112d18b7a807fc547901bf7408b Mon Sep 17 00:00:00 2001 From: Todd Grunke Date: Wed, 1 Apr 2026 22:26:47 -0700 Subject: [PATCH 11/13] Embrace AssertWorkspaceEditAsync --- .../Cohost/CohostAddNestedFileEndpoint.cs | 1 - .../RemoteAddNestedFileServiceTest.cs | 156 ++++++++---------- 2 files changed, 73 insertions(+), 84 deletions(-) diff --git a/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/LanguageClient/Cohost/CohostAddNestedFileEndpoint.cs b/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/LanguageClient/Cohost/CohostAddNestedFileEndpoint.cs index 1a652e9ae97..33e0d538c88 100644 --- a/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/LanguageClient/Cohost/CohostAddNestedFileEndpoint.cs +++ b/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/LanguageClient/Cohost/CohostAddNestedFileEndpoint.cs @@ -8,7 +8,6 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.ExternalAccess.Razor; using Microsoft.CodeAnalysis.ExternalAccess.Razor.Cohost; -using Microsoft.CodeAnalysis.ExternalAccess.Razor.Features; using Microsoft.CodeAnalysis.Razor.Cohost; using Microsoft.CodeAnalysis.Razor.Logging; using Microsoft.CodeAnalysis.Razor.Protocol.NestedFiles; diff --git a/src/Razor/test/Microsoft.CodeAnalysis.Razor.CohostingShared.Test/NestedFiles/RemoteAddNestedFileServiceTest.cs b/src/Razor/test/Microsoft.CodeAnalysis.Razor.CohostingShared.Test/NestedFiles/RemoteAddNestedFileServiceTest.cs index 308eb215ba4..62fe68444b4 100644 --- a/src/Razor/test/Microsoft.CodeAnalysis.Razor.CohostingShared.Test/NestedFiles/RemoteAddNestedFileServiceTest.cs +++ b/src/Razor/test/Microsoft.CodeAnalysis.Razor.CohostingShared.Test/NestedFiles/RemoteAddNestedFileServiceTest.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Threading.Tasks; +using Microsoft.AspNetCore.Razor.Test.Common; using Microsoft.CodeAnalysis.Razor.NestedFiles; using Microsoft.CodeAnalysis.Razor.Remote; using Xunit; @@ -21,17 +22,10 @@ public async Task CssNestedFile_CreatesEmptyFile() DisposalToken); Assert.NotNull(result); - var changes = GetDocumentChanges(result); - Assert.Equal(2, changes.Length); - - // First change: CreateFile - Assert.True(changes[0].TryGetSecond(out var createFile)); - Assert.Contains(".razor.css", createFile!.DocumentUri.ToString()); - - // Second change: TextDocumentEdit with CSS comment content - Assert.True(changes[1].TryGetFirst(out var textEdit)); - Assert.Single(textEdit!.Edits); - Assert.Contains("CSS for File1 component", ((TextEdit)textEdit.Edits[0]).NewText); + await result.AssertWorkspaceEditAsync( + document.Project.Solution, + [(FileUri("File1.razor.css"), "/* CSS for File1 component */\r\n")], + DisposalToken); } [Fact] @@ -44,18 +38,10 @@ public async Task JavaScriptNestedFile_CreatesFileWithTemplate() DisposalToken); Assert.NotNull(result); - var changes = GetDocumentChanges(result); - Assert.Equal(2, changes.Length); - - // First change: CreateFile - Assert.True(changes[0].TryGetSecond(out var createFile)); - Assert.Contains(".razor.js", createFile!.DocumentUri.ToString()); - - // Second change: TextDocumentEdit with JS comment content - Assert.True(changes[1].TryGetFirst(out var textEdit)); - Assert.Single(textEdit!.Edits); - var content = ((TextEdit)textEdit.Edits[0]).NewText; - Assert.Contains("JavaScript for File1 component", content); + await result.AssertWorkspaceEditAsync( + document.Project.Solution, + [(FileUri("File1.razor.js"), "// JavaScript for File1 component\r\n")], + DisposalToken); } [Fact] @@ -68,22 +54,18 @@ public async Task CSharpNestedFile_GeneratesCodeBehind() DisposalToken); Assert.NotNull(result); - var changes = GetDocumentChanges(result); - Assert.Equal(2, changes.Length); - - // First change: CreateFile with .razor.cs extension - Assert.True(changes[0].TryGetSecond(out var createFile)); - Assert.Contains(".razor.cs", createFile!.DocumentUri.ToString()); - - // Second change: TextDocumentEdit with C# content - Assert.True(changes[1].TryGetFirst(out var textEdit)); - Assert.Single(textEdit!.Edits); - var content = ((TextEdit)textEdit.Edits[0]).NewText; - - // Should contain namespace from test project (SomeProject) - Assert.Contains("namespace SomeProject", content); - // Should contain partial class matching the file name - Assert.Contains("partial class File1", content); + await result.AssertWorkspaceEditAsync( + document.Project.Solution, + [(FileUri("File1.razor.cs"), """ + namespace SomeProject + { + public partial class File1 + { + } + } + + """)], + DisposalToken); } [Fact] @@ -100,18 +82,25 @@ @namespace My.Custom.Namespace DisposalToken); Assert.NotNull(result); - var changes = GetDocumentChanges(result); - Assert.True(changes[1].TryGetFirst(out var textEdit)); - var content = ((TextEdit)textEdit!.Edits[0]).NewText; - - // Should use the @namespace directive value - Assert.Contains("namespace My.Custom.Namespace", content); - Assert.Contains("partial class File1", content); + await result.AssertWorkspaceEditAsync( + document.Project.Solution, + [(FileUri("File1.razor.cs"), """ + namespace My.Custom.Namespace + { + public partial class File1 + { + } + } + + """)], + DisposalToken); } [Fact] public async Task CSharpNestedFile_IncludesUsingDirectives() { + // Note: The @using directive is removed by Roslyn formatting since the code-behind + // class body is empty and has no references to types in the imported namespace. var document = CreateProjectAndRazorDocument( """ @using System.Diagnostics @@ -124,14 +113,18 @@ @using System.Diagnostics DisposalToken); Assert.NotNull(result); - var changes = GetDocumentChanges(result); - Assert.True(changes[1].TryGetFirst(out var textEdit)); - var content = ((TextEdit)textEdit!.Edits[0]).NewText; - - // The @using directive from the Razor file should appear in the generated code-behind. - // The test infrastructure may include default imports from _Imports.razor. - Assert.Contains("partial class File1", content); - Assert.Contains("namespace SomeProject", content); + await result.AssertWorkspaceEditAsync( + document.Project.Solution, + [(FileUri("File1.razor.cs"), """ + namespace SomeProject + { + public partial class File1 + { + } + } + + """)], + DisposalToken); } [Fact] @@ -147,13 +140,10 @@ public async Task CshtmlFile_CssNestedFile() DisposalToken); Assert.NotNull(result); - var changes = GetDocumentChanges(result); - Assert.True(changes[0].TryGetSecond(out var createFile)); - Assert.Contains(".cshtml.css", createFile!.DocumentUri.ToString()); - - // .cshtml files should say "view" not "component" - Assert.True(changes[1].TryGetFirst(out var textEdit)); - Assert.Contains("Page1 view", ((TextEdit)textEdit!.Edits[0]).NewText); + await result.AssertWorkspaceEditAsync( + document.Project.Solution, + [(FileUri("Page1.cshtml.css"), "/* CSS for Page1 view */\r\n")], + DisposalToken); } [Fact] @@ -167,14 +157,18 @@ public async Task CSharpNestedFile_DefaultsToBlockScopedNamespace() DisposalToken); Assert.NotNull(result); - var changes = GetDocumentChanges(result); - Assert.True(changes[1].TryGetFirst(out var textEdit)); - var content = ((TextEdit)textEdit!.Edits[0]).NewText; - - Assert.Contains("partial class File1", content); - // Without editorconfig, namespace should be block-scoped (not file-scoped) - Assert.DoesNotContain("namespace SomeProject;", content); - Assert.Contains("namespace SomeProject", content); + await result.AssertWorkspaceEditAsync( + document.Project.Solution, + [(FileUri("File1.razor.cs"), """ + namespace SomeProject + { + public partial class File1 + { + } + } + + """)], + DisposalToken); } [Fact] @@ -199,13 +193,17 @@ public async Task CSharpNestedFile_WithFileScopedNamespaceEditorConfig() DisposalToken); Assert.NotNull(result); - var changes = GetDocumentChanges(result); - Assert.True(changes[1].TryGetFirst(out var textEdit)); - var content = ((TextEdit)textEdit!.Edits[0]).NewText; + await result.AssertWorkspaceEditAsync( + document.Project.Solution, + [(FileUri("File1.razor.cs"), """ + namespace SomeProject; + + public partial class File1 + { + } - // With file-scoped namespace editorconfig, Roslyn formatting should produce "namespace X;" - Assert.Contains("namespace SomeProject;", content); - Assert.Contains("partial class File1", content); + """)], + DisposalToken); } [Fact] @@ -219,12 +217,4 @@ public async Task InvalidFileKind_ReturnsNull() Assert.Null(result); } - - private static SumType[] GetDocumentChanges(WorkspaceEdit edit) - { - Assert.NotNull(edit.DocumentChanges); - Assert.True(edit.DocumentChanges.Value.TryGetSecond(out var changes), - "Expected DocumentChanges to contain SumType[] (not TextDocumentEdit[])"); - return changes; - } } From 24e4422aba58fb80bb2d2678733086f11222f70a Mon Sep 17 00:00:00 2001 From: Todd Grunke Date: Wed, 1 Apr 2026 22:52:37 -0700 Subject: [PATCH 12/13] Change NestedFileKind to a struct Get rid of pretty useless derivations of NestedFileCommandHandler --- .../NestedFiles/NestedFileKind.cs | 8 ++++---- .../NestedFiles/AddNestedFileParams.cs | 6 +++--- .../Remote/IRemoteAddNestedFileService.cs | 3 ++- .../NestedFiles/RemoteAddNestedFileService.cs | 8 ++++---- .../CSharpNestedFileCommandHandler.cs | 18 ------------------ .../NestedFiles/CssNestedFileCommandHandler.cs | 18 ------------------ .../JavascriptNestedFileCommandHandler.cs | 18 ------------------ .../NestedFiles/NestedFileCommandHandler.cs | 9 +++++---- .../RazorPackage.cs | 7 ++++--- .../RemoteAddNestedFileServiceTest.cs | 2 +- 10 files changed, 23 insertions(+), 74 deletions(-) delete mode 100644 src/Razor/src/Microsoft.VisualStudio.RazorExtension/NestedFiles/CSharpNestedFileCommandHandler.cs delete mode 100644 src/Razor/src/Microsoft.VisualStudio.RazorExtension/NestedFiles/CssNestedFileCommandHandler.cs delete mode 100644 src/Razor/src/Microsoft.VisualStudio.RazorExtension/NestedFiles/JavascriptNestedFileCommandHandler.cs diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/NestedFiles/NestedFileKind.cs b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/NestedFiles/NestedFileKind.cs index dc8a655cd72..8c980e223d2 100644 --- a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/NestedFiles/NestedFileKind.cs +++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/NestedFiles/NestedFileKind.cs @@ -6,9 +6,9 @@ namespace Microsoft.CodeAnalysis.Razor.NestedFiles; /// /// Specifies which type of nested file to create for a Razor component. /// -internal static class NestedFileKind +internal enum NestedFileKind { - public const string Css = "css"; - public const string CSharp = "csharp"; - public const string JavaScript = "javascript"; + CSharp, + Css, + JavaScript, } diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Protocol/NestedFiles/AddNestedFileParams.cs b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Protocol/NestedFiles/AddNestedFileParams.cs index 69c535840b6..161c5ec3f78 100644 --- a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Protocol/NestedFiles/AddNestedFileParams.cs +++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Protocol/NestedFiles/AddNestedFileParams.cs @@ -19,12 +19,12 @@ internal sealed class AddNestedFileParams : ITextDocumentParams public required TextDocumentIdentifier TextDocument { get; set; } /// - /// The kind of nested file to create (one of constants). + /// The kind of nested file to create. /// [JsonPropertyName("fileKind")] - public required string FileKind { get; set; } + public required NestedFileKind FileKind { get; set; } - public static AddNestedFileParams Create(Uri razorFileUri, string fileKind) + public static AddNestedFileParams Create(Uri razorFileUri, NestedFileKind fileKind) { return new AddNestedFileParams { diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Remote/IRemoteAddNestedFileService.cs b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Remote/IRemoteAddNestedFileService.cs index cf91816615a..4d0aa3d5e0e 100644 --- a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Remote/IRemoteAddNestedFileService.cs +++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Remote/IRemoteAddNestedFileService.cs @@ -4,6 +4,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.ExternalAccess.Razor; +using Microsoft.CodeAnalysis.Razor.NestedFiles; namespace Microsoft.CodeAnalysis.Razor.Remote; @@ -17,6 +18,6 @@ internal interface IRemoteAddNestedFileService : IRemoteJsonService ValueTask GetNewNestedFileWorkspaceEditAsync( JsonSerializableRazorPinnedSolutionInfoWrapper solutionInfo, JsonSerializableDocumentId documentId, - string fileKind, + NestedFileKind fileKind, CancellationToken cancellationToken); } diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Remote.Razor/NestedFiles/RemoteAddNestedFileService.cs b/src/Razor/src/Microsoft.CodeAnalysis.Remote.Razor/NestedFiles/RemoteAddNestedFileService.cs index 67d2703acf0..8a7b59b0303 100644 --- a/src/Razor/src/Microsoft.CodeAnalysis.Remote.Razor/NestedFiles/RemoteAddNestedFileService.cs +++ b/src/Razor/src/Microsoft.CodeAnalysis.Remote.Razor/NestedFiles/RemoteAddNestedFileService.cs @@ -35,7 +35,7 @@ protected override IRemoteAddNestedFileService CreateService(in ServiceArgs args public ValueTask GetNewNestedFileWorkspaceEditAsync( JsonSerializableRazorPinnedSolutionInfoWrapper solutionInfo, JsonSerializableDocumentId documentId, - string fileKind, + NestedFileKind fileKind, CancellationToken cancellationToken) => RunServiceAsync( solutionInfo, @@ -45,7 +45,7 @@ protected override IRemoteAddNestedFileService CreateService(in ServiceArgs args private async ValueTask GetNewNestedFileWorkspaceEditAsync( RemoteDocumentContext context, - string fileKind, + NestedFileKind fileKind, CancellationToken cancellationToken) { var razorFilePath = context.FilePath; @@ -80,7 +80,7 @@ protected override IRemoteAddNestedFileService CreateService(in ServiceArgs args }; } - private static string? GetNestedFilePath(string razorFilePath, string fileKind) + private static string? GetNestedFilePath(string razorFilePath, NestedFileKind fileKind) { return fileKind switch { @@ -92,7 +92,7 @@ protected override IRemoteAddNestedFileService CreateService(in ServiceArgs args } private async Task GenerateContentAsync( - string fileKind, + NestedFileKind fileKind, RemoteDocumentContext documentContext, string razorFilePath, Uri nestedFileUri, diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/NestedFiles/CSharpNestedFileCommandHandler.cs b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/NestedFiles/CSharpNestedFileCommandHandler.cs deleted file mode 100644 index 56135f4a17d..00000000000 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/NestedFiles/CSharpNestedFileCommandHandler.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using Microsoft.CodeAnalysis.Razor.NestedFiles; -using Microsoft.VisualStudio.Razor.ProjectSystem; - -namespace Microsoft.VisualStudio.RazorExtension.NestedFiles; - -/// -/// Command handler for C# code-behind nested files (.razor.cs). -/// -internal sealed class CSharpNestedFileCommandHandler( - IServiceProvider serviceProvider, - Lazy requestInvoker) - : NestedFileCommandHandler(serviceProvider, ".cs", NestedFileKind.CSharp, requestInvoker) -{ -} diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/NestedFiles/CssNestedFileCommandHandler.cs b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/NestedFiles/CssNestedFileCommandHandler.cs deleted file mode 100644 index 0c8f4bc499a..00000000000 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/NestedFiles/CssNestedFileCommandHandler.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using Microsoft.CodeAnalysis.Razor.NestedFiles; -using Microsoft.VisualStudio.Razor.ProjectSystem; - -namespace Microsoft.VisualStudio.RazorExtension.NestedFiles; - -/// -/// Command handler for CSS nested files (.razor.css). -/// -internal sealed class CssNestedFileCommandHandler( - IServiceProvider serviceProvider, - Lazy requestInvoker) - : NestedFileCommandHandler(serviceProvider, ".css", NestedFileKind.Css, requestInvoker) -{ -} diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/NestedFiles/JavascriptNestedFileCommandHandler.cs b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/NestedFiles/JavascriptNestedFileCommandHandler.cs deleted file mode 100644 index dc5ba05f86d..00000000000 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/NestedFiles/JavascriptNestedFileCommandHandler.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using Microsoft.CodeAnalysis.Razor.NestedFiles; -using Microsoft.VisualStudio.Razor.ProjectSystem; - -namespace Microsoft.VisualStudio.RazorExtension.NestedFiles; - -/// -/// Command handler for JavaScript nested files (.razor.js). -/// -internal sealed class JavascriptNestedFileCommandHandler( - IServiceProvider serviceProvider, - Lazy requestInvoker) - : NestedFileCommandHandler(serviceProvider, ".js", NestedFileKind.JavaScript, requestInvoker) -{ -} diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/NestedFiles/NestedFileCommandHandler.cs b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/NestedFiles/NestedFileCommandHandler.cs index c36ad3eb848..81cd42e1f39 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/NestedFiles/NestedFileCommandHandler.cs +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/NestedFiles/NestedFileCommandHandler.cs @@ -10,6 +10,7 @@ using Microsoft.AspNetCore.Mvc.Razor.Extensions; using Microsoft.AspNetCore.Razor.Language; using Microsoft.AspNetCore.Razor.Language.Components; +using Microsoft.CodeAnalysis.Razor.NestedFiles; using Microsoft.CodeAnalysis.Razor.Protocol.NestedFiles; using Microsoft.VisualStudio.Razor.LanguageClient; using Microsoft.VisualStudio.Razor.ProjectSystem; @@ -23,15 +24,15 @@ namespace Microsoft.VisualStudio.RazorExtension.NestedFiles; /// When the file doesn't exist, sends an LSP request to the Razor language server /// to create it via workspace/applyEdit. When the file exists, just opens it. ///
-internal abstract class NestedFileCommandHandler( +internal sealed class NestedFileCommandHandler( IServiceProvider serviceProvider, string fileExtension, - string fileKind, + NestedFileKind fileKind, Lazy requestInvoker) { private readonly IServiceProvider _serviceProvider = serviceProvider; private readonly string _fileExtension = fileExtension; - private readonly string _fileKind = fileKind; + private readonly NestedFileKind _fileKind = fileKind; private readonly Lazy _requestInvoker = requestInvoker; /// @@ -131,7 +132,7 @@ private async Task CreateAndOpenNestedFileAsync( /// /// Gets the path to the nested file based on the Razor file path. /// - protected virtual string GetNestedFilePath(string razorFilePath) + private string GetNestedFilePath(string razorFilePath) { Debug.Assert(_fileExtension.StartsWith('.')); return razorFilePath + _fileExtension; diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/RazorPackage.cs b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/RazorPackage.cs index 9f2b7c0cd47..1f3c1d7e9fd 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/RazorPackage.cs +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/RazorPackage.cs @@ -7,6 +7,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Razor; +using Microsoft.CodeAnalysis.Razor.NestedFiles; using Microsoft.CodeAnalysis.Razor.Workspaces; using Microsoft.VisualStudio.ComponentModelHost; using Microsoft.VisualStudio.Editor; @@ -171,9 +172,9 @@ private void RegisterNestedFileCommands(OleMenuCommandService mcs) var requestInvoker = new Lazy(() => componentModel.GetService()); // Create command handlers - var csharpHandler = new CSharpNestedFileCommandHandler(this, requestInvoker); - var cssHandler = new CssNestedFileCommandHandler(this, requestInvoker); - var javascriptHandler = new JavascriptNestedFileCommandHandler(this, requestInvoker); + var csharpHandler = new NestedFileCommandHandler(this, ".cs", NestedFileKind.CSharp, requestInvoker); + var cssHandler = new NestedFileCommandHandler(this, ".css", NestedFileKind.Css, requestInvoker); + var javascriptHandler = new NestedFileCommandHandler(this, ".js", NestedFileKind.JavaScript, requestInvoker); // .cs Nested File Command var csharpCommandId = new CommandID(GuidRazorNestedFilesCmdSet, (int)CmdIdAddOrViewNestedCsFile); diff --git a/src/Razor/test/Microsoft.CodeAnalysis.Razor.CohostingShared.Test/NestedFiles/RemoteAddNestedFileServiceTest.cs b/src/Razor/test/Microsoft.CodeAnalysis.Razor.CohostingShared.Test/NestedFiles/RemoteAddNestedFileServiceTest.cs index 62fe68444b4..00899008a9e 100644 --- a/src/Razor/test/Microsoft.CodeAnalysis.Razor.CohostingShared.Test/NestedFiles/RemoteAddNestedFileServiceTest.cs +++ b/src/Razor/test/Microsoft.CodeAnalysis.Razor.CohostingShared.Test/NestedFiles/RemoteAddNestedFileServiceTest.cs @@ -212,7 +212,7 @@ public async Task InvalidFileKind_ReturnsNull() var document = CreateProjectAndRazorDocument("
"); var result = await RemoteServiceInvoker.TryInvokeAsync( document.Project.Solution, - (service, solutionInfo, ct) => service.GetNewNestedFileWorkspaceEditAsync(solutionInfo, document.Id, "invalid", ct), + (service, solutionInfo, ct) => service.GetNewNestedFileWorkspaceEditAsync(solutionInfo, document.Id, (NestedFileKind)999, ct), DisposalToken); Assert.Null(result); From db1eb221c968c77359530a1cd7eefdeeb078ba7f Mon Sep 17 00:00:00 2001 From: Todd Grunke Date: Wed, 1 Apr 2026 23:05:43 -0700 Subject: [PATCH 13/13] Add a test for TryGetNamespace failing (or returning an empty namespace) --- .../NestedFiles/RemoteAddNestedFileService.cs | 2 +- .../RemoteAddNestedFileServiceTest.cs | 26 ++++++++++++++++++- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Remote.Razor/NestedFiles/RemoteAddNestedFileService.cs b/src/Razor/src/Microsoft.CodeAnalysis.Remote.Razor/NestedFiles/RemoteAddNestedFileService.cs index 8a7b59b0303..024e6c1844a 100644 --- a/src/Razor/src/Microsoft.CodeAnalysis.Remote.Razor/NestedFiles/RemoteAddNestedFileService.cs +++ b/src/Razor/src/Microsoft.CodeAnalysis.Remote.Razor/NestedFiles/RemoteAddNestedFileService.cs @@ -133,7 +133,7 @@ private async Task GenerateCSharpContentAsync( // Use the Razor compiler's namespace resolution which handles @namespace directives, // _Imports.razor, and SDK-provided root namespace - if (!codeDocument.TryGetNamespace(fallbackToRootNamespace: true, out var ns)) + if (!codeDocument.TryGetNamespace(fallbackToRootNamespace: true, out var ns) || ns.IsNullOrEmpty()) { Logger.LogWarning($"Could not determine namespace for: {razorFilePath}"); ns = "Unknown"; diff --git a/src/Razor/test/Microsoft.CodeAnalysis.Razor.CohostingShared.Test/NestedFiles/RemoteAddNestedFileServiceTest.cs b/src/Razor/test/Microsoft.CodeAnalysis.Razor.CohostingShared.Test/NestedFiles/RemoteAddNestedFileServiceTest.cs index 00899008a9e..da7a5ca3ef9 100644 --- a/src/Razor/test/Microsoft.CodeAnalysis.Razor.CohostingShared.Test/NestedFiles/RemoteAddNestedFileServiceTest.cs +++ b/src/Razor/test/Microsoft.CodeAnalysis.Razor.CohostingShared.Test/NestedFiles/RemoteAddNestedFileServiceTest.cs @@ -1,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Threading.Tasks; @@ -206,6 +206,30 @@ public partial class File1 DisposalToken); } + [Fact] + public async Task CSharpNestedFile_GlobalNamespace_UsesUnkownNamespace() + { + var document = CreateProjectAndRazorDocument("
", inGlobalNamespace: true); + var result = await RemoteServiceInvoker.TryInvokeAsync( + document.Project.Solution, + (service, solutionInfo, ct) => service.GetNewNestedFileWorkspaceEditAsync(solutionInfo, document.Id, NestedFileKind.CSharp, ct), + DisposalToken); + + Assert.NotNull(result); + await result.AssertWorkspaceEditAsync( + document.Project.Solution, + [(FileUri("File1.razor.cs"), """ + namespace Unknown + { + public partial class File1 + { + } + } + + """)], + DisposalToken); + } + [Fact] public async Task InvalidFileKind_ReturnsNull() {