Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System.Collections.Generic;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Host;

namespace Microsoft.CodeAnalysis.Features.Workspaces;

internal interface IMiscellaneousProjectInfoService : ILanguageService
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you imagine this also being used in VS for Windows?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was hoping to, yes. Did I put something in the wrong place for that to happen?

{
string ProjectLanguageOverride { get; }
bool AddAsAdditionalDocument { get; }

IEnumerable<AnalyzerReference>? GetAnalyzerReferences(SolutionServices services);
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,18 @@ internal static ProjectInfo CreateMiscellaneousProjectInfoForDocument(
var fileExtension = PathUtilities.GetExtension(filePath);
var fileName = PathUtilities.GetFileName(filePath);

// For Razor files we need to override the language name to C# as thats what code is generated
var isRazor = languageInformation.LanguageName == "Razor";
var languageName = isRazor ? LanguageNames.CSharp : languageInformation.LanguageName;

var languageName = languageInformation.LanguageName;
var languageServices = services.GetLanguageServices(languageName);
var miscellaneousProjectInfoService = languageServices.GetService<IMiscellaneousProjectInfoService>();

if (miscellaneousProjectInfoService is not null)
{
// The MiscellaneousProjectInfoService can override the language name to use for the project, and therefore we have to re-get
// the right set of language services.
languageName = miscellaneousProjectInfoService.ProjectLanguageOverride;
languageServices = services.GetLanguageServices(languageName);
}

var compilationOptions = languageServices.GetService<ICompilationFactoryService>()?.GetDefaultCompilationOptions();

// Use latest language version which is more permissive, as we cannot find out language version of the project which the file belongs to
Expand Down Expand Up @@ -67,6 +74,7 @@ internal static ProjectInfo CreateMiscellaneousProjectInfoForDocument(
// a random GUID can be used.
var assemblyName = Guid.NewGuid().ToString("N");

var addAsAdditionalDocument = miscellaneousProjectInfoService?.AddAsAdditionalDocument ?? false;
var projectInfo = ProjectInfo.Create(
new ProjectInfo.ProjectAttributes(
id: projectId,
Expand All @@ -81,9 +89,10 @@ internal static ProjectInfo CreateMiscellaneousProjectInfoForDocument(
hasAllInformation: sourceCodeKind == SourceCodeKind.Script),
compilationOptions: compilationOptions,
parseOptions: parseOptions,
documents: isRazor ? null : [documentInfo],
additionalDocuments: isRazor ? [documentInfo] : null,
metadataReferences: metadataReferences);
documents: addAsAdditionalDocument ? null : [documentInfo],
additionalDocuments: addAsAdditionalDocument ? [documentInfo] : null,
metadataReferences: metadataReferences,
analyzerReferences: miscellaneousProjectInfoService?.GetAnalyzerReferences(services));

return projectInfo;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

namespace Microsoft.CodeAnalysis.LanguageServer.HostWorkspace;

[ExportWorkspaceService(typeof(IAnalyzerAssemblyLoaderProvider), [WorkspaceKind.Host]), Shared]
[ExportWorkspaceService(typeof(IAnalyzerAssemblyLoaderProvider), [WorkspaceKind.Host, WorkspaceKind.MiscellaneousFiles]), Shared]
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just bringing your attention to this change @dibarbet because it was added after your review. Hopefully it's okay because without it we're in a world of ALC pain and suffering :)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

seems... fine? mostly just the default loader, except also knows about the extension context.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, thats all it is. We need the source gen to come from the extension, because otherwise types are different in Razor when we try to actually get out generator bits, because of course all of the handlers come from the extension.

[method: ImportingConstructor]
[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
internal sealed class VSCodeAnalyzerLoaderProviderFactory(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,19 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System;
using System.Collections.Generic;
using System.Composition;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Features.Workspaces;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.Shared.TestHooks;
using Microsoft.CodeAnalysis.Test.Utilities;
using Microsoft.CodeAnalysis.Text;
using Roslyn.LanguageServer.Protocol;
using Roslyn.Test.Utilities;
using Xunit;
Expand Down Expand Up @@ -178,8 +183,10 @@ void M()
[Theory, CombinatorialData]
public async Task TestLooseFile_RazorFile(bool mutatingLspWorkspace)
{
var composition = Composition.AddParts(typeof(TestRazorMiscellaneousProjectInfoService));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this just be applied to all tests in this file?


// Create a server that supports LSP misc files and verify no misc files present.
await using var testLspServer = await CreateTestLspServerAsync(string.Empty, mutatingLspWorkspace, new InitializationOptions { ServerKind = WellKnownLspServerKinds.CSharpVisualBasicLspServer });
await using var testLspServer = await CreateTestLspServerAsync(string.Empty, mutatingLspWorkspace, new InitializationOptions { ServerKind = WellKnownLspServerKinds.CSharpVisualBasicLspServer }, composition);
Assert.Null(await GetMiscellaneousDocumentAsync(testLspServer));
Assert.Null(await GetMiscellaneousAdditionalDocumentAsync(testLspServer));

Expand Down Expand Up @@ -397,4 +404,17 @@ private async Task AssertFileInMainWorkspaceAsync(TestLspServer testLspServer, D
Contract.ThrowIfNull(result);
return result;
}

// This is a test version of the real service which lives in the Razor EA, which is not referenced here
[PartNotDiscoverable]
[ExportLanguageService(typeof(IMiscellaneousProjectInfoService), "Razor"), Shared]
[method: ImportingConstructor]
[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
private sealed class TestRazorMiscellaneousProjectInfoService() : IMiscellaneousProjectInfoService
{
public string ProjectLanguageOverride => LanguageNames.CSharp;
public bool AddAsAdditionalDocument => true;

public IEnumerable<AnalyzerReference>? GetAnalyzerReferences(SolutionServices services) => null;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

namespace Microsoft.CodeAnalysis.ExternalAccess.Razor.Features;

internal interface IRazorSourceGeneratorLocator
{
string GetGeneratorFilePath();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System;
using System.Collections.Generic;
using System.Composition;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Features.Workspaces;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.Host.Mef;

namespace Microsoft.CodeAnalysis.ExternalAccess.Razor.Features;

[Shared]
[ExportLanguageService(typeof(IMiscellaneousProjectInfoService), Constants.RazorLanguageName)]
[method: ImportingConstructor]
[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
internal sealed class RazorMiscellaneousProjectInfoService([Import(AllowDefault = true)] Lazy<IRazorSourceGeneratorLocator>? razorSourceGeneratorLocator) : IMiscellaneousProjectInfoService
{
public string ProjectLanguageOverride => LanguageNames.CSharp;

public bool AddAsAdditionalDocument => true;

public IEnumerable<AnalyzerReference>? GetAnalyzerReferences(Host.SolutionServices services)
{
if (razorSourceGeneratorLocator is null)
{
return null;
}

var filePath = razorSourceGeneratorLocator.Value.GetGeneratorFilePath();
var loaderProvider = services.GetRequiredService<IAnalyzerAssemblyLoaderProvider>();
var reference = new AnalyzerFileReference(filePath, loaderProvider.SharedShadowCopyLoader);

return [reference];
}
}
Loading