зеркало из https://github.com/dotnet/razor.git
Remove Omnisharp logic from main branch (#9027)
This commit is contained in:
Родитель
d59a761805
Коммит
b3c1f61021
33
Razor.sln
33
Razor.sln
|
@ -66,10 +66,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Razor.
|
|||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Razor.LanguageServer.Test.Common", "src\Razor\test\Microsoft.AspNetCore.Razor.LanguageServer.Test.Common\Microsoft.AspNetCore.Razor.LanguageServer.Test.Common.csproj", "{9D300F9A-1F78-45C9-B4BB-476EF12E40F8}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Razor.OmniSharpPlugin", "src\Razor\src\Microsoft.AspNetCore.Razor.OmniSharpPlugin\Microsoft.AspNetCore.Razor.OmniSharpPlugin.csproj", "{305354FD-5ED7-4E89-8B1D-58FCCA3E08AD}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Razor.OmniSharpPlugin.Test", "src\Razor\test\Microsoft.AspNetCore.Razor.OmniSharpPlugin.Test\Microsoft.AspNetCore.Razor.OmniSharpPlugin.Test.csproj", "{4ED6CC87-11C4-4ECD-B9A1-AFC5C2DACABE}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.VisualStudio.LanguageServerClient.Razor", "src\Razor\src\Microsoft.VisualStudio.LanguageServerClient.Razor\Microsoft.VisualStudio.LanguageServerClient.Razor.csproj", "{70E70B52-EB70-42D1-B785-8618BD0B950E}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.VisualStudio.LanguageServerClient.Razor.Test", "src\Razor\test\Microsoft.VisualStudio.LanguageServerClient.Razor.Test\Microsoft.VisualStudio.LanguageServerClient.Razor.Test.csproj", "{F6E8EEA2-BDD8-4AAF-A0CE-9E33A9A7CE8E}"
|
||||
|
@ -171,8 +167,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Compiler Tests", "Compiler
|
|||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Razor.Microbenchmarks.Generator", "src\Compiler\perf\Microsoft.AspNetCore.Razor.Microbenchmarks.Generator\Microsoft.AspNetCore.Razor.Microbenchmarks.Generator.csproj", "{7400A168-2552-49C7-93E3-D4DAA90C216F}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp", "src\Razor\src\Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp\Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.csproj", "{3E2B6DF5-524F-4909-8A66-7F8C6383620A}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Razor.ExternalAccess.RoslynWorkspace", "src\Razor\src\Microsoft.AspNetCore.Razor.ExternalAccess.RoslynWorkspace\Microsoft.AspNetCore.Razor.ExternalAccess.RoslynWorkspace.csproj", "{2223B8FD-D98A-47BE-94A9-6A3A6B8557B8}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Razor.ProjectEngineHost", "src\Razor\src\Microsoft.AspNetCore.Razor.ProjectEngineHost\Microsoft.AspNetCore.Razor.ProjectEngineHost.csproj", "{2FB4801C-A083-4F08-A4FB-C4910985DE31}"
|
||||
|
@ -409,22 +403,6 @@ Global
|
|||
{9D300F9A-1F78-45C9-B4BB-476EF12E40F8}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{9D300F9A-1F78-45C9-B4BB-476EF12E40F8}.ReleaseNoVSIX|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{9D300F9A-1F78-45C9-B4BB-476EF12E40F8}.ReleaseNoVSIX|Any CPU.Build.0 = Release|Any CPU
|
||||
{305354FD-5ED7-4E89-8B1D-58FCCA3E08AD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{305354FD-5ED7-4E89-8B1D-58FCCA3E08AD}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{305354FD-5ED7-4E89-8B1D-58FCCA3E08AD}.DebugNoVSIX|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{305354FD-5ED7-4E89-8B1D-58FCCA3E08AD}.DebugNoVSIX|Any CPU.Build.0 = Debug|Any CPU
|
||||
{305354FD-5ED7-4E89-8B1D-58FCCA3E08AD}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{305354FD-5ED7-4E89-8B1D-58FCCA3E08AD}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{305354FD-5ED7-4E89-8B1D-58FCCA3E08AD}.ReleaseNoVSIX|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{305354FD-5ED7-4E89-8B1D-58FCCA3E08AD}.ReleaseNoVSIX|Any CPU.Build.0 = Release|Any CPU
|
||||
{4ED6CC87-11C4-4ECD-B9A1-AFC5C2DACABE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{4ED6CC87-11C4-4ECD-B9A1-AFC5C2DACABE}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{4ED6CC87-11C4-4ECD-B9A1-AFC5C2DACABE}.DebugNoVSIX|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{4ED6CC87-11C4-4ECD-B9A1-AFC5C2DACABE}.DebugNoVSIX|Any CPU.Build.0 = Debug|Any CPU
|
||||
{4ED6CC87-11C4-4ECD-B9A1-AFC5C2DACABE}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{4ED6CC87-11C4-4ECD-B9A1-AFC5C2DACABE}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{4ED6CC87-11C4-4ECD-B9A1-AFC5C2DACABE}.ReleaseNoVSIX|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{4ED6CC87-11C4-4ECD-B9A1-AFC5C2DACABE}.ReleaseNoVSIX|Any CPU.Build.0 = Release|Any CPU
|
||||
{70E70B52-EB70-42D1-B785-8618BD0B950E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{70E70B52-EB70-42D1-B785-8618BD0B950E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{70E70B52-EB70-42D1-B785-8618BD0B950E}.DebugNoVSIX|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
|
@ -721,14 +699,6 @@ Global
|
|||
{7400A168-2552-49C7-93E3-D4DAA90C216F}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{7400A168-2552-49C7-93E3-D4DAA90C216F}.ReleaseNoVSIX|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{7400A168-2552-49C7-93E3-D4DAA90C216F}.ReleaseNoVSIX|Any CPU.Build.0 = Release|Any CPU
|
||||
{3E2B6DF5-524F-4909-8A66-7F8C6383620A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{3E2B6DF5-524F-4909-8A66-7F8C6383620A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{3E2B6DF5-524F-4909-8A66-7F8C6383620A}.DebugNoVSIX|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{3E2B6DF5-524F-4909-8A66-7F8C6383620A}.DebugNoVSIX|Any CPU.Build.0 = Debug|Any CPU
|
||||
{3E2B6DF5-524F-4909-8A66-7F8C6383620A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{3E2B6DF5-524F-4909-8A66-7F8C6383620A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{3E2B6DF5-524F-4909-8A66-7F8C6383620A}.ReleaseNoVSIX|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{3E2B6DF5-524F-4909-8A66-7F8C6383620A}.ReleaseNoVSIX|Any CPU.Build.0 = Release|Any CPU
|
||||
{2223B8FD-D98A-47BE-94A9-6A3A6B8557B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{2223B8FD-D98A-47BE-94A9-6A3A6B8557B8}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{2223B8FD-D98A-47BE-94A9-6A3A6B8557B8}.DebugNoVSIX|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
|
@ -809,8 +779,6 @@ Global
|
|||
{6C8A42B5-B41C-4334-959F-684E647A24E1} = {92463391-81BE-462B-AC3C-78C6C760741F}
|
||||
{FBAE9975-77BE-411B-A1A3-4790C8A367EF} = {92463391-81BE-462B-AC3C-78C6C760741F}
|
||||
{9D300F9A-1F78-45C9-B4BB-476EF12E40F8} = {92463391-81BE-462B-AC3C-78C6C760741F}
|
||||
{305354FD-5ED7-4E89-8B1D-58FCCA3E08AD} = {3C0D6505-79B3-49D0-B4C3-176F0F1836ED}
|
||||
{4ED6CC87-11C4-4ECD-B9A1-AFC5C2DACABE} = {92463391-81BE-462B-AC3C-78C6C760741F}
|
||||
{70E70B52-EB70-42D1-B785-8618BD0B950E} = {3C0D6505-79B3-49D0-B4C3-176F0F1836ED}
|
||||
{F6E8EEA2-BDD8-4AAF-A0CE-9E33A9A7CE8E} = {92463391-81BE-462B-AC3C-78C6C760741F}
|
||||
{35FEC0EA-09B5-45D2-832D-D6FEBA364871} = {3C0D6505-79B3-49D0-B4C3-176F0F1836ED}
|
||||
|
@ -855,7 +823,6 @@ Global
|
|||
{97DE8703-467C-49A7-BCE4-42FF1FEC8AC2} = {FB7C870E-A173-4F75-BE63-4EF39C79A759}
|
||||
{A9F9B5E5-C5C2-4860-BE56-038C70ADBAC9} = {FB7C870E-A173-4F75-BE63-4EF39C79A759}
|
||||
{7400A168-2552-49C7-93E3-D4DAA90C216F} = {C2C98051-0F39-47F2-80B6-E72B29159F2C}
|
||||
{3E2B6DF5-524F-4909-8A66-7F8C6383620A} = {3C0D6505-79B3-49D0-B4C3-176F0F1836ED}
|
||||
{2223B8FD-D98A-47BE-94A9-6A3A6B8557B8} = {3C0D6505-79B3-49D0-B4C3-176F0F1836ED}
|
||||
{2FB4801C-A083-4F08-A4FB-C4910985DE31} = {3C0D6505-79B3-49D0-B4C3-176F0F1836ED}
|
||||
{70C6EAF1-202B-481B-ADD4-D30DF1396BDE} = {92463391-81BE-462B-AC3C-78C6C760741F}
|
||||
|
|
|
@ -9,7 +9,6 @@ Created with https://www.sankeymatic.com/build/
|
|||
|
||||
Input:
|
||||
```
|
||||
OmniSharp [1] MS.AspNetCore.Razor.LanguageServer.Common
|
||||
RoslynWorkspace [1] ProjectEngineHost
|
||||
MS.AspNetCore.Razor.LanguageServer [1] MS.AspNetCore.Razor.LanguageServer.Common
|
||||
MS.AspNetCore.Razor.LanguageServer.Common [1] Compiler
|
||||
|
@ -114,13 +113,6 @@ target the broadest set of frameworks.
|
|||
- Microsoft.VisualStudio.Mac.LanguageServices.Razor
|
||||
- Microsoft.VisualStudio.Mac.RazorAddin
|
||||
|
||||
### Visual Studio Code (OmniSharp Plug-in)
|
||||
|
||||
- Target Framework: `net472`
|
||||
- Projects:
|
||||
- Microsoft.AspNetCore.Razor.OmniSharpPlugin
|
||||
- Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp
|
||||
|
||||
### Miscellaneous / Test hosting
|
||||
|
||||
- Target Framework: net7.0
|
||||
|
@ -170,7 +162,3 @@ target the broadest set of frameworks.
|
|||
### Visual Studio (Mac) Tests
|
||||
|
||||
- Microsoft.VisualStudio.Mac.LanguageServices.Razor.Test (`net472`)
|
||||
|
||||
### Visual Studio Code (OmniSharp Plug-in) Tests
|
||||
|
||||
- Microsoft.AspNetCore.Razor.OmniSharpPlugin.Test (`net472`)
|
||||
|
|
|
@ -82,27 +82,7 @@ In most cases, this is because the option _Use previews of the .NET Core SDK_ in
|
|||
|
||||
## Building with Visual Studio Code
|
||||
|
||||
Note, the [Visual Studio Code C# Extension](https://marketplace.visualstudio.com/items?itemName=ms-dotnettools.csharp) is required.
|
||||
|
||||
1. Run `Restore.cmd` on the command line.
|
||||
1. Launch the `razor` repo in VS Code.
|
||||
2. Open VS Code settings (`CTRL+,`) and navigate to the `Razor > Plugin: Path` setting:
|
||||
![image](https://user-images.githubusercontent.com/16968319/192892840-ae2b102c-a282-472f-b1f1-ef3dad671874.png)
|
||||
3. Set path to `C:\path_to_razor_repo\artifacts\bin\Microsoft.AspNetCore.Razor.OmniSharpPlugin\Debug\net472\Microsoft.AspNetCore.Razor.OmniSharpPlugin.dll`.
|
||||
4. Launch extension via `Run and Debug -> Run Extension`.
|
||||
5. Install missing assets if prompted.
|
||||
|
||||
### If you want to make changes within the Razor language server
|
||||
1. Make the changes, then run `Build.cmd -pack`.
|
||||
2. To debug through the language server code, open VS Code settings and check the box `Razor > Language Server: Debug`.
|
||||
![image](https://user-images.githubusercontent.com/16968319/192892444-1e4e514a-d41a-4aea-b739-cecee48d12d6.png)
|
||||
3. Attach your Visual Studio instance to `rzls.exe`.
|
||||
|
||||
### If you want to make changes within Razor VS Code
|
||||
(i.e. anywhere within the `Microsoft.AspNetCore.Razor.VSCode` folder)
|
||||
1. Make the changes, then delete the existing `node_modules` folder within `Microsoft.AspNetCore.Razor.VSCode.Extension` if one exists. (Deleting the `node_modules` folder is supposed to be unnecessary, but there is currently a bug preventing changes from being detected - tracked by [#6788](https://github.com/dotnet/razor-tooling/issues/6788)).
|
||||
2. Run `Restore.cmd`.
|
||||
3. When debugging, ensure breakpoints are set within the `*.js` equivalent of a given `*.ts` file. This file can generally be found in the `node_modules` folder within `Microsoft.AspNetCore.Razor.VSCode.Extension`.
|
||||
Outside of Razor's language server and C# workspace logic, the bulk of our VS Code logic now lives in the [dotnet/vscode-csharp](https://github.com/dotnet/vscode-csharp) repo.
|
||||
|
||||
## Building on command-line
|
||||
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
[*.cs]
|
||||
# RS0016: Add public types and members to the declared API
|
||||
dotnet_diagnostic.RS0016.severity = warning
|
|
@ -1,80 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT license. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.AspNetCore.Razor.LanguageServer;
|
||||
using Microsoft.AspNetCore.Razor.LanguageServer.Common;
|
||||
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Document;
|
||||
|
||||
public class OmniSharpBackgroundDocumentGenerator : IOmniSharpProjectSnapshotManagerChangeTrigger
|
||||
{
|
||||
private readonly BackgroundDocumentGenerator _backgroundDocumentGenerator;
|
||||
|
||||
public OmniSharpBackgroundDocumentGenerator(
|
||||
OmniSharpProjectSnapshotManagerDispatcher projectSnapshotManagerDispatcher,
|
||||
RemoteTextLoaderFactory remoteTextLoaderFactory,
|
||||
IEnumerable<OmniSharpDocumentProcessedListener> documentProcessedListeners)
|
||||
{
|
||||
if (projectSnapshotManagerDispatcher is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(projectSnapshotManagerDispatcher));
|
||||
}
|
||||
|
||||
if (remoteTextLoaderFactory is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(remoteTextLoaderFactory));
|
||||
}
|
||||
|
||||
if (documentProcessedListeners is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(documentProcessedListeners));
|
||||
}
|
||||
|
||||
var wrappedListeners = documentProcessedListeners.Select(listener => new WrappedDocumentProcessedListener(remoteTextLoaderFactory, listener));
|
||||
_backgroundDocumentGenerator = new BackgroundDocumentGenerator(projectSnapshotManagerDispatcher.InternalDispatcher, wrappedListeners);
|
||||
}
|
||||
|
||||
public void Initialize(OmniSharpProjectSnapshotManager projectManager)
|
||||
{
|
||||
_backgroundDocumentGenerator.Initialize(projectManager.InternalProjectSnapshotManager);
|
||||
}
|
||||
|
||||
private class WrappedDocumentProcessedListener : DocumentProcessedListener
|
||||
{
|
||||
private readonly RemoteTextLoaderFactory _remoteTextLoaderFactory;
|
||||
private readonly OmniSharpDocumentProcessedListener _innerDocumentProcessedListener;
|
||||
|
||||
internal WrappedDocumentProcessedListener(
|
||||
RemoteTextLoaderFactory remoteTextLoaderFactory,
|
||||
OmniSharpDocumentProcessedListener innerDocumentProcessedListener)
|
||||
{
|
||||
if (remoteTextLoaderFactory is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(remoteTextLoaderFactory));
|
||||
}
|
||||
|
||||
if (innerDocumentProcessedListener is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(innerDocumentProcessedListener));
|
||||
}
|
||||
|
||||
_remoteTextLoaderFactory = remoteTextLoaderFactory;
|
||||
_innerDocumentProcessedListener = innerDocumentProcessedListener;
|
||||
}
|
||||
|
||||
public override void DocumentProcessed(RazorCodeDocument codeDocument, IDocumentSnapshot document)
|
||||
{
|
||||
var omniSharpDocument = new OmniSharpDocumentSnapshot(document);
|
||||
_innerDocumentProcessedListener.DocumentProcessed(codeDocument, omniSharpDocument);
|
||||
}
|
||||
|
||||
public override void Initialize(ProjectSnapshotManager projectManager)
|
||||
{
|
||||
var omniSharpProjectManager = new OmniSharpProjectSnapshotManager((ProjectSnapshotManagerBase)projectManager, _remoteTextLoaderFactory);
|
||||
_innerDocumentProcessedListener.Initialize(omniSharpProjectManager);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT license. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Document;
|
||||
|
||||
public abstract class OmniSharpDocumentProcessedListener
|
||||
{
|
||||
public abstract void Initialize(OmniSharpProjectSnapshotManager projectManager);
|
||||
|
||||
public abstract void DocumentProcessed(RazorCodeDocument codeDocument, OmniSharpDocumentSnapshot document);
|
||||
}
|
|
@ -1,58 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT license. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project;
|
||||
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Document;
|
||||
|
||||
public sealed class OmniSharpDocumentSnapshot
|
||||
{
|
||||
private readonly IDocumentSnapshot _documentSnapshot;
|
||||
private readonly object _projectLock;
|
||||
private OmniSharpHostDocument? _hostDocument;
|
||||
private OmniSharpProjectSnapshot? _project;
|
||||
|
||||
internal OmniSharpDocumentSnapshot(IDocumentSnapshot documentSnapshot)
|
||||
{
|
||||
if (documentSnapshot is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(documentSnapshot));
|
||||
}
|
||||
|
||||
_documentSnapshot = documentSnapshot;
|
||||
_projectLock = new object();
|
||||
}
|
||||
|
||||
public OmniSharpHostDocument HostDocument
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_hostDocument is null)
|
||||
{
|
||||
var defaultDocumentSnapshot = (DocumentSnapshot)_documentSnapshot;
|
||||
var hostDocument = defaultDocumentSnapshot.State.HostDocument;
|
||||
_hostDocument = new OmniSharpHostDocument(hostDocument.FilePath, hostDocument.TargetPath, hostDocument.FileKind);
|
||||
}
|
||||
|
||||
return _hostDocument;
|
||||
}
|
||||
}
|
||||
|
||||
public string? FileKind => _documentSnapshot.FileKind;
|
||||
|
||||
public string? FilePath => _documentSnapshot.FilePath;
|
||||
|
||||
public OmniSharpProjectSnapshot Project
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (_projectLock)
|
||||
{
|
||||
_project ??= new OmniSharpProjectSnapshot(_documentSnapshot.Project);
|
||||
}
|
||||
|
||||
return _project;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT license. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Document;
|
||||
|
||||
public sealed class OmniSharpHostDocument
|
||||
{
|
||||
public OmniSharpHostDocument(string filePath, string targetPath, string kind)
|
||||
{
|
||||
InternalHostDocument = new HostDocument(filePath, targetPath, kind);
|
||||
|
||||
if (targetPath.Contains("/"))
|
||||
{
|
||||
throw new FormatException("TargetPath's must use '\\' instead of '/'");
|
||||
}
|
||||
|
||||
if (targetPath.StartsWith("\\", StringComparison.Ordinal))
|
||||
{
|
||||
throw new FormatException("TargetPath's can't start with '\\'");
|
||||
}
|
||||
}
|
||||
|
||||
public string FilePath => InternalHostDocument.FilePath;
|
||||
|
||||
internal string TargetPath => InternalHostDocument.TargetPath;
|
||||
|
||||
internal string FileKind => InternalHostDocument.FileKind;
|
||||
|
||||
internal HostDocument InternalHostDocument { get; }
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT license. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.AspNetCore.Razor.LanguageServer.Common;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Document;
|
||||
|
||||
public sealed class OmniSharpHostDocumentComparer : IEqualityComparer<OmniSharpHostDocument>
|
||||
{
|
||||
public static readonly OmniSharpHostDocumentComparer Instance = new();
|
||||
|
||||
private OmniSharpHostDocumentComparer()
|
||||
{
|
||||
}
|
||||
|
||||
public bool Equals(OmniSharpHostDocument? x, OmniSharpHostDocument? y) =>
|
||||
HostDocumentComparer.Instance.Equals(x?.InternalHostDocument, y?.InternalHostDocument);
|
||||
|
||||
public int GetHashCode(OmniSharpHostDocument hostDocument) =>
|
||||
HostDocumentComparer.Instance.GetHashCode(hostDocument.InternalHostDocument);
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT license. See License.txt in the project root for license information.
|
||||
|
||||
using System.Composition;
|
||||
using Microsoft.AspNetCore.Razor.Telemetry;
|
||||
using Microsoft.CodeAnalysis.Host;
|
||||
using Microsoft.CodeAnalysis.Host.Mef;
|
||||
using Microsoft.CodeAnalysis.Razor;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp;
|
||||
|
||||
[Shared]
|
||||
[ExportWorkspaceServiceFactory(typeof(TagHelperResolver), ServiceLayer.Default)]
|
||||
internal class ExportedTagHelperResolverFactory : IWorkspaceServiceFactory
|
||||
{
|
||||
private readonly ITelemetryReporter _telemetryReporter;
|
||||
|
||||
[ImportingConstructor]
|
||||
public ExportedTagHelperResolverFactory()
|
||||
{
|
||||
_telemetryReporter = new OmniSharpTelemetryReporter();
|
||||
}
|
||||
|
||||
public IWorkspaceService CreateService(HostWorkspaceServices workspaceServices)
|
||||
{
|
||||
return new DefaultTagHelperResolver(_telemetryReporter);
|
||||
}
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT license. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.CodeAnalysis.Razor.Workspaces.Extensions;
|
||||
using Microsoft.CodeAnalysis.Text;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Extensions;
|
||||
|
||||
public static class OmniSharpRazorCodeDocumentExtensions
|
||||
{
|
||||
public static SourceText GetInternalCSharpSourceText(this RazorCodeDocument codeDocument)
|
||||
{
|
||||
return codeDocument.GetCSharpSourceText();
|
||||
}
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT license. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp;
|
||||
|
||||
public class FallbackRazorConfiguration
|
||||
{
|
||||
public static RazorConfiguration SelectConfiguration(Version version) => CodeAnalysis.Razor.ProjectSystem.FallbackRazorConfiguration.SelectConfiguration(version);
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>$(DefaultNetCoreTargetFrameworks);$(DefaultNetFxTargetFramework)</TargetFrameworks>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="../Microsoft.AspNetCore.Razor.LanguageServer.Common/Microsoft.AspNetCore.Razor.LanguageServer.Common.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -1,12 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT license. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.AspNetCore.Razor.LanguageServer;
|
||||
using Microsoft.CodeAnalysis.Razor.Workspaces;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp;
|
||||
|
||||
public class OmniSharpLanguageServerFeatureOptions
|
||||
{
|
||||
internal LanguageServerFeatureOptions InternalLanguageServerFeatureOptions { get; } = new DefaultLanguageServerFeatureOptions();
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT license. See License.txt in the project root for license information.
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project;
|
||||
|
||||
public interface IOmniSharpProjectSnapshotManagerChangeTrigger
|
||||
{
|
||||
void Initialize(OmniSharpProjectSnapshotManager projectManager);
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT license. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project;
|
||||
|
||||
public sealed class OmniSharpHostProject
|
||||
{
|
||||
public OmniSharpHostProject(string projectFilePath, string intermediateOutputPath, RazorConfiguration razorConfiguration, string rootNamespace)
|
||||
{
|
||||
if (projectFilePath is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(projectFilePath));
|
||||
}
|
||||
|
||||
if (intermediateOutputPath is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(intermediateOutputPath));
|
||||
}
|
||||
|
||||
if (razorConfiguration is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(razorConfiguration));
|
||||
}
|
||||
|
||||
InternalHostProject = new HostProject(projectFilePath, intermediateOutputPath, razorConfiguration, rootNamespace);
|
||||
}
|
||||
|
||||
public OmniSharpProjectKey Key => new OmniSharpProjectKey(InternalHostProject.Key);
|
||||
|
||||
internal HostProject InternalHostProject { get; }
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT license. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project;
|
||||
|
||||
public class OmniSharpProjectChangeEventArgs : EventArgs
|
||||
{
|
||||
internal OmniSharpProjectChangeEventArgs(ProjectChangeEventArgs args) : this(
|
||||
OmniSharpProjectSnapshot.Convert(args.Older),
|
||||
OmniSharpProjectSnapshot.Convert(args.Newer),
|
||||
args.DocumentFilePath,
|
||||
(OmniSharpProjectChangeKind)args.Kind)
|
||||
{
|
||||
}
|
||||
|
||||
public OmniSharpProjectChangeEventArgs(OmniSharpProjectSnapshot? older, OmniSharpProjectSnapshot? newer, string? documentFilePath, OmniSharpProjectChangeKind kind)
|
||||
{
|
||||
if (older is null && newer is null)
|
||||
{
|
||||
throw new ArgumentException("Both projects cannot be null.");
|
||||
}
|
||||
|
||||
Older = older;
|
||||
Newer = newer;
|
||||
DocumentFilePath = documentFilePath;
|
||||
Kind = kind;
|
||||
}
|
||||
|
||||
public OmniSharpProjectSnapshot? Older { get; }
|
||||
|
||||
public OmniSharpProjectSnapshot? Newer { get; }
|
||||
|
||||
public string? DocumentFilePath { get; }
|
||||
|
||||
public OmniSharpProjectChangeKind Kind { get; }
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT license. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project;
|
||||
|
||||
public enum OmniSharpProjectChangeKind
|
||||
{
|
||||
ProjectAdded = ProjectChangeKind.ProjectAdded,
|
||||
ProjectRemoved = ProjectChangeKind.ProjectRemoved,
|
||||
ProjectChanged = ProjectChangeKind.ProjectChanged,
|
||||
DocumentAdded = ProjectChangeKind.DocumentAdded,
|
||||
DocumentRemoved = ProjectChangeKind.DocumentRemoved,
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT license. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project;
|
||||
|
||||
public sealed class OmniSharpProjectKey
|
||||
{
|
||||
public static OmniSharpProjectKey? From(CodeAnalysis.Project workspaceProject)
|
||||
{
|
||||
var key = ProjectKey.From(workspaceProject);
|
||||
return key is null ? null : new(key.Value);
|
||||
}
|
||||
|
||||
internal ProjectKey Key { get; }
|
||||
|
||||
internal OmniSharpProjectKey(ProjectKey key)
|
||||
{
|
||||
this.Key = key;
|
||||
}
|
||||
}
|
|
@ -1,56 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT license. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Document;
|
||||
using Microsoft.AspNetCore.Razor.ProjectSystem;
|
||||
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
||||
using Microsoft.CodeAnalysis.Razor.Workspaces.ProjectSystem;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project;
|
||||
|
||||
public sealed class OmniSharpProjectSnapshot
|
||||
{
|
||||
internal readonly IProjectSnapshot InternalProjectSnapshot;
|
||||
|
||||
internal OmniSharpProjectSnapshot(IProjectSnapshot projectSnapshot)
|
||||
{
|
||||
InternalProjectSnapshot = projectSnapshot;
|
||||
}
|
||||
|
||||
public OmniSharpProjectKey Key => new OmniSharpProjectKey(InternalProjectSnapshot.Key);
|
||||
|
||||
public string FilePath => InternalProjectSnapshot.FilePath;
|
||||
|
||||
public IEnumerable<string> DocumentFilePaths => InternalProjectSnapshot.DocumentFilePaths;
|
||||
|
||||
public ProjectWorkspaceState? ProjectWorkspaceState => InternalProjectSnapshot.ProjectWorkspaceState;
|
||||
|
||||
public OmniSharpDocumentSnapshot? GetDocument(string filePath)
|
||||
{
|
||||
var documentSnapshot = InternalProjectSnapshot.GetDocument(filePath);
|
||||
if (documentSnapshot is null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var internalDocumentSnapshot = new OmniSharpDocumentSnapshot(documentSnapshot);
|
||||
return internalDocumentSnapshot;
|
||||
}
|
||||
|
||||
public void Serialize(string publishFilePath, JsonSerializer serializer, StreamWriter writer)
|
||||
{
|
||||
var projectRazorJson = InternalProjectSnapshot.ToProjectRazorJson(publishFilePath);
|
||||
serializer.Serialize(writer, projectRazorJson);
|
||||
}
|
||||
|
||||
internal static OmniSharpProjectSnapshot? Convert(IProjectSnapshot? projectSnapshot)
|
||||
{
|
||||
if (projectSnapshot is null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return new OmniSharpProjectSnapshot(projectSnapshot);
|
||||
}
|
||||
}
|
|
@ -1,91 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT license. See License.txt in the project root for license information.
|
||||
|
||||
using System.Collections.Immutable;
|
||||
using Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Document;
|
||||
using Microsoft.AspNetCore.Razor.LanguageServer.Common;
|
||||
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project;
|
||||
|
||||
public class OmniSharpProjectSnapshotManager
|
||||
{
|
||||
private readonly RemoteTextLoaderFactory _remoteTextLoaderFactory;
|
||||
|
||||
internal OmniSharpProjectSnapshotManager(
|
||||
ProjectSnapshotManagerBase projectSnapshotManager,
|
||||
RemoteTextLoaderFactory remoteTextLoaderFactory)
|
||||
{
|
||||
if (projectSnapshotManager is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(projectSnapshotManager));
|
||||
}
|
||||
|
||||
if (remoteTextLoaderFactory is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(remoteTextLoaderFactory));
|
||||
}
|
||||
|
||||
InternalProjectSnapshotManager = projectSnapshotManager;
|
||||
_remoteTextLoaderFactory = remoteTextLoaderFactory;
|
||||
InternalProjectSnapshotManager.Changed += ProjectSnapshotManager_Changed;
|
||||
}
|
||||
|
||||
internal ProjectSnapshotManagerBase InternalProjectSnapshotManager { get; }
|
||||
|
||||
public IReadOnlyList<OmniSharpProjectSnapshot> Projects => InternalProjectSnapshotManager.GetProjects().Select(project => OmniSharpProjectSnapshot.Convert(project)!).ToList();
|
||||
|
||||
public event EventHandler<OmniSharpProjectChangeEventArgs>? Changed;
|
||||
|
||||
public OmniSharpProjectSnapshot GetLoadedProject(OmniSharpProjectKey projectKey)
|
||||
{
|
||||
var projectSnapshot = InternalProjectSnapshotManager.GetLoadedProject(projectKey.Key);
|
||||
// Forgiving null because we should only return null when projectSnapshot is null
|
||||
var converted = OmniSharpProjectSnapshot.Convert(projectSnapshot)!;
|
||||
|
||||
return converted;
|
||||
}
|
||||
|
||||
public ImmutableArray<OmniSharpProjectKey> GetAllProjectKeys(string projectFilePath)
|
||||
{
|
||||
var keys = InternalProjectSnapshotManager.GetAllProjectKeys(projectFilePath);
|
||||
|
||||
return ImmutableArray<OmniSharpProjectKey>.Empty.AddRange(keys.Select(k => new OmniSharpProjectKey(k)));
|
||||
}
|
||||
|
||||
public void ProjectAdded(OmniSharpHostProject hostProject)
|
||||
{
|
||||
InternalProjectSnapshotManager.ProjectAdded(hostProject.InternalHostProject);
|
||||
}
|
||||
|
||||
public void ProjectConfigurationChanged(OmniSharpHostProject hostProject)
|
||||
{
|
||||
InternalProjectSnapshotManager.ProjectConfigurationChanged(hostProject.InternalHostProject);
|
||||
}
|
||||
|
||||
public void DocumentAdded(OmniSharpHostProject hostProject, OmniSharpHostDocument hostDocument)
|
||||
{
|
||||
var textLoader = _remoteTextLoaderFactory.Create(hostDocument.FilePath);
|
||||
InternalProjectSnapshotManager.DocumentAdded(hostProject.InternalHostProject.Key, hostDocument.InternalHostDocument, textLoader);
|
||||
}
|
||||
|
||||
public void DocumentChanged(string projectFilePath, string documentFilePath)
|
||||
{
|
||||
var textLoader = _remoteTextLoaderFactory.Create(documentFilePath);
|
||||
foreach (var projectKey in InternalProjectSnapshotManager.GetAllProjectKeys(projectFilePath))
|
||||
{
|
||||
InternalProjectSnapshotManager.DocumentChanged(projectKey, documentFilePath, textLoader);
|
||||
}
|
||||
}
|
||||
|
||||
public void DocumentRemoved(OmniSharpHostProject hostProject, OmniSharpHostDocument hostDocument)
|
||||
{
|
||||
InternalProjectSnapshotManager.DocumentRemoved(hostProject.InternalHostProject.Key, hostDocument.InternalHostDocument);
|
||||
}
|
||||
|
||||
private void ProjectSnapshotManager_Changed(object? sender, ProjectChangeEventArgs args)
|
||||
{
|
||||
var convertedArgs = new OmniSharpProjectChangeEventArgs(args);
|
||||
Changed?.Invoke(this, convertedArgs);
|
||||
}
|
||||
}
|
|
@ -1,73 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT license. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.AspNetCore.Razor.LanguageServer.Common;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.Razor;
|
||||
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project;
|
||||
|
||||
public class OmniSharpProjectSnapshotManagerAccessor
|
||||
{
|
||||
private readonly RemoteTextLoaderFactory _remoteTextLoaderFactory;
|
||||
private readonly IEnumerable<IOmniSharpProjectSnapshotManagerChangeTrigger> _projectChangeTriggers;
|
||||
private readonly OmniSharpProjectSnapshotManagerDispatcher _projectSnapshotManagerDispatcher;
|
||||
private readonly Workspace _workspace;
|
||||
private OmniSharpProjectSnapshotManager? _instance;
|
||||
|
||||
public OmniSharpProjectSnapshotManagerAccessor(
|
||||
RemoteTextLoaderFactory remoteTextLoaderFactory,
|
||||
IEnumerable<IOmniSharpProjectSnapshotManagerChangeTrigger> projectChangeTriggers,
|
||||
OmniSharpProjectSnapshotManagerDispatcher projectSnapshotManagerDispatcher,
|
||||
Workspace workspace)
|
||||
{
|
||||
if (remoteTextLoaderFactory is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(remoteTextLoaderFactory));
|
||||
}
|
||||
|
||||
if (projectChangeTriggers is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(projectChangeTriggers));
|
||||
}
|
||||
|
||||
if (projectSnapshotManagerDispatcher is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(projectSnapshotManagerDispatcher));
|
||||
}
|
||||
|
||||
if (workspace is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(workspace));
|
||||
}
|
||||
|
||||
_remoteTextLoaderFactory = remoteTextLoaderFactory;
|
||||
_projectChangeTriggers = projectChangeTriggers;
|
||||
_projectSnapshotManagerDispatcher = projectSnapshotManagerDispatcher;
|
||||
_workspace = workspace;
|
||||
}
|
||||
|
||||
public OmniSharpProjectSnapshotManager Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_instance is null)
|
||||
{
|
||||
var projectSnapshotManager = new DefaultProjectSnapshotManager(
|
||||
ErrorReporter.Instance,
|
||||
Array.Empty<ProjectSnapshotChangeTrigger>(),
|
||||
_workspace);
|
||||
|
||||
var instance = new OmniSharpProjectSnapshotManager(projectSnapshotManager, _remoteTextLoaderFactory);
|
||||
_instance = instance;
|
||||
foreach (var changeTrigger in _projectChangeTriggers)
|
||||
{
|
||||
changeTrigger.Initialize(instance);
|
||||
}
|
||||
}
|
||||
|
||||
return _instance;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,39 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT license. See License.txt in the project root for license information.
|
||||
|
||||
using System.Runtime.CompilerServices;
|
||||
using Microsoft.CodeAnalysis.Razor;
|
||||
using Microsoft.CodeAnalysis.Razor.Workspaces;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project;
|
||||
|
||||
public class OmniSharpProjectSnapshotManagerDispatcher
|
||||
{
|
||||
public OmniSharpProjectSnapshotManagerDispatcher()
|
||||
{
|
||||
InternalDispatcher = new InternalOmniSharpProjectSnapshotManagerDispatcher();
|
||||
}
|
||||
|
||||
internal ProjectSnapshotManagerDispatcher InternalDispatcher { get; private protected set; }
|
||||
|
||||
public Task RunOnDispatcherThreadAsync(Action action, CancellationToken cancellationToken)
|
||||
=> InternalDispatcher.RunOnDispatcherThreadAsync(action, cancellationToken);
|
||||
|
||||
public TaskScheduler DispatcherScheduler => InternalDispatcher.DispatcherScheduler;
|
||||
|
||||
public void AssertDispatcherThread([CallerMemberName] string? caller = null) => InternalDispatcher.AssertDispatcherThread(caller);
|
||||
|
||||
private class InternalOmniSharpProjectSnapshotManagerDispatcher : ProjectSnapshotManagerDispatcherBase
|
||||
{
|
||||
private const string ThreadName = "Razor." + nameof(OmniSharpProjectSnapshotManagerDispatcher);
|
||||
|
||||
internal InternalOmniSharpProjectSnapshotManagerDispatcher() : base(ThreadName)
|
||||
{
|
||||
}
|
||||
|
||||
public override void LogException(Exception ex)
|
||||
{
|
||||
// We don't currently have logging mechanisms in place for O#.
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT license. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.CodeAnalysis.Razor;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project;
|
||||
|
||||
public class OmniSharpProjectWorkspaceStateGenerator : IOmniSharpProjectSnapshotManagerChangeTrigger
|
||||
{
|
||||
// Internal for testing
|
||||
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
|
||||
internal OmniSharpProjectWorkspaceStateGenerator()
|
||||
#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
|
||||
{
|
||||
}
|
||||
|
||||
public OmniSharpProjectWorkspaceStateGenerator(OmniSharpProjectSnapshotManagerDispatcher projectSnapshotManagerDispatcher)
|
||||
{
|
||||
if (projectSnapshotManagerDispatcher is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(projectSnapshotManagerDispatcher));
|
||||
}
|
||||
|
||||
InternalWorkspaceStateGenerator = new DefaultProjectWorkspaceStateGenerator(projectSnapshotManagerDispatcher.InternalDispatcher);
|
||||
}
|
||||
|
||||
internal DefaultProjectWorkspaceStateGenerator InternalWorkspaceStateGenerator { get; }
|
||||
|
||||
public void Initialize(OmniSharpProjectSnapshotManager projectManager) => InternalWorkspaceStateGenerator.Initialize(projectManager.InternalProjectSnapshotManager);
|
||||
|
||||
public virtual void Update(CodeAnalysis.Project workspaceProject, OmniSharpProjectSnapshot projectSnapshot) => InternalWorkspaceStateGenerator.Update(workspaceProject, projectSnapshot.InternalProjectSnapshot, CancellationToken.None);
|
||||
}
|
|
@ -1,44 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT license. See License.txt in the project root for license information.
|
||||
|
||||
using System.Collections.Immutable;
|
||||
using Microsoft.AspNetCore.Razor.Telemetry;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp;
|
||||
|
||||
internal class OmniSharpTelemetryReporter : ITelemetryReporter
|
||||
{
|
||||
public void ReportEvent(string name, Severity severity)
|
||||
{
|
||||
}
|
||||
|
||||
public void ReportEvent(string name, Severity severity, ImmutableDictionary<string, object?> values)
|
||||
{
|
||||
}
|
||||
|
||||
public void ReportFault(Exception exception, string? message, params object?[] @params)
|
||||
{
|
||||
}
|
||||
|
||||
public IDisposable BeginBlock(string name, Severity severity)
|
||||
{
|
||||
return NullScope.Instance;
|
||||
}
|
||||
|
||||
public IDisposable BeginBlock(string name, Severity severity, ImmutableDictionary<string, object?> values)
|
||||
{
|
||||
return NullScope.Instance;
|
||||
}
|
||||
|
||||
public IDisposable TrackLspRequest(string lspMethodName, string lspServerName, Guid correlationId)
|
||||
{
|
||||
return NullScope.Instance;
|
||||
}
|
||||
|
||||
private class NullScope : IDisposable
|
||||
{
|
||||
public static NullScope Instance { get; } = new NullScope();
|
||||
private NullScope() { }
|
||||
public void Dispose() { }
|
||||
}
|
||||
}
|
|
@ -1,113 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT license. See License.txt in the project root for license information.
|
||||
|
||||
using System.Diagnostics;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.Razor;
|
||||
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
||||
using Microsoft.CodeAnalysis.Razor.Workspaces;
|
||||
using Microsoft.VisualStudio.Threading;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project;
|
||||
|
||||
public class OmniSharpWorkspaceProjectStateChangeDetector : IOmniSharpProjectSnapshotManagerChangeTrigger
|
||||
{
|
||||
public OmniSharpWorkspaceProjectStateChangeDetector(
|
||||
OmniSharpProjectSnapshotManagerDispatcher projectSnapshotManagerDispatcher,
|
||||
OmniSharpProjectWorkspaceStateGenerator workspaceStateGenerator,
|
||||
OmniSharpLanguageServerFeatureOptions languageServerFeatureOptions)
|
||||
{
|
||||
if (projectSnapshotManagerDispatcher is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(projectSnapshotManagerDispatcher));
|
||||
}
|
||||
|
||||
if (workspaceStateGenerator is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(workspaceStateGenerator));
|
||||
}
|
||||
|
||||
InternalWorkspaceProjectStateChangeDetector = new ProjectSnapshotManagerWorkspaceProjectStateChangeDetector(
|
||||
projectSnapshotManagerDispatcher.InternalDispatcher,
|
||||
workspaceStateGenerator.InternalWorkspaceStateGenerator,
|
||||
languageServerFeatureOptions.InternalLanguageServerFeatureOptions);
|
||||
}
|
||||
|
||||
private WorkspaceProjectStateChangeDetector InternalWorkspaceProjectStateChangeDetector { get; }
|
||||
|
||||
public void Initialize(OmniSharpProjectSnapshotManager projectManager)
|
||||
{
|
||||
InternalWorkspaceProjectStateChangeDetector.Initialize(projectManager.InternalProjectSnapshotManager);
|
||||
}
|
||||
|
||||
private class ProjectSnapshotManagerWorkspaceProjectStateChangeDetector : WorkspaceProjectStateChangeDetector
|
||||
{
|
||||
private readonly ProjectSnapshotManagerDispatcher _projectSnapshotManagerDispatcher;
|
||||
|
||||
internal ProjectSnapshotManagerWorkspaceProjectStateChangeDetector(
|
||||
ProjectSnapshotManagerDispatcher projectSnapshotManagerDispatcher,
|
||||
ProjectWorkspaceStateGenerator workspaceStateGenerator,
|
||||
LanguageServerFeatureOptions languageServerFeatureOptions)
|
||||
: base(workspaceStateGenerator, projectSnapshotManagerDispatcher, languageServerFeatureOptions)
|
||||
{
|
||||
if (projectSnapshotManagerDispatcher is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(projectSnapshotManagerDispatcher));
|
||||
}
|
||||
|
||||
_projectSnapshotManagerDispatcher = projectSnapshotManagerDispatcher;
|
||||
}
|
||||
|
||||
// We override the InitializeSolution in order to enforce calls to this to be on the project snapshot manager's
|
||||
// thread. OmniSharp currently has an issue where they update the Solution on multiple different threads resulting
|
||||
// in change events dispatching through the Workspace on multiple different threads. This normalizes
|
||||
// that abnormality.
|
||||
protected override void InitializeSolution(Solution solution)
|
||||
{
|
||||
_ = InitializeSolutionAsync(solution, CancellationToken.None);
|
||||
}
|
||||
|
||||
private async Task InitializeSolutionAsync(Solution solution, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
await _projectSnapshotManagerDispatcher.RunOnDispatcherThreadAsync(
|
||||
() =>
|
||||
{
|
||||
base.InitializeSolution(solution);
|
||||
},
|
||||
cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.Fail("Unexpected error when initializing solution: " + ex);
|
||||
}
|
||||
}
|
||||
|
||||
// We override Workspace_WorkspaceChanged in order to enforce calls to this to be on the project snapshot manager's
|
||||
// thread. OmniSharp currently has an issue where they update the Solution on multiple different threads resulting
|
||||
// in change events dispatching through the Workspace on multiple different threads. This normalizes
|
||||
// that abnormality.
|
||||
internal override void Workspace_WorkspaceChanged(object? sender, WorkspaceChangeEventArgs args)
|
||||
{
|
||||
Workspace_WorkspaceChangedAsync(sender, args).Forget();
|
||||
}
|
||||
|
||||
private async Task Workspace_WorkspaceChangedAsync(object? sender, WorkspaceChangeEventArgs args)
|
||||
{
|
||||
try
|
||||
{
|
||||
await _projectSnapshotManagerDispatcher.RunOnDispatcherThreadAsync(
|
||||
() =>
|
||||
{
|
||||
base.Workspace_WorkspaceChanged(sender, args);
|
||||
},
|
||||
CancellationToken.None).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.Fail("Unexpected error when handling a workspace changed event: " + ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT license. See License.txt in the project root for license information.
|
||||
|
||||
using System.Composition;
|
||||
using Microsoft.AspNetCore.Razor.LanguageServer.Common;
|
||||
using Microsoft.CodeAnalysis.Host;
|
||||
using Microsoft.CodeAnalysis.Host.Mef;
|
||||
using Microsoft.CodeAnalysis.Razor;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project;
|
||||
|
||||
[Shared]
|
||||
[ExportWorkspaceServiceFactory(typeof(ProjectSnapshotProjectEngineFactory))]
|
||||
internal class ProjectSnapshotProjectEngineFactoryFactory : IWorkspaceServiceFactory
|
||||
{
|
||||
public IWorkspaceService CreateService(HostWorkspaceServices workspaceServices)
|
||||
{
|
||||
if (workspaceServices is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(workspaceServices));
|
||||
}
|
||||
|
||||
return new DefaultProjectSnapshotProjectEngineFactory(new FallbackProjectEngineFactory(), MefProjectEngineFactories.Factories);
|
||||
}
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT license. See License.txt in the project root for license information.
|
||||
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")]
|
|
@ -1 +0,0 @@
|
|||
#nullable enable
|
|
@ -1,80 +0,0 @@
|
|||
#nullable enable
|
||||
abstract Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Document.OmniSharpDocumentProcessedListener.DocumentProcessed(Microsoft.AspNetCore.Razor.Language.RazorCodeDocument! codeDocument, Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Document.OmniSharpDocumentSnapshot! document) -> void
|
||||
abstract Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Document.OmniSharpDocumentProcessedListener.Initialize(Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project.OmniSharpProjectSnapshotManager! projectManager) -> void
|
||||
Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Document.OmniSharpBackgroundDocumentGenerator
|
||||
Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Document.OmniSharpBackgroundDocumentGenerator.Initialize(Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project.OmniSharpProjectSnapshotManager! projectManager) -> void
|
||||
Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Document.OmniSharpBackgroundDocumentGenerator.OmniSharpBackgroundDocumentGenerator(Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project.OmniSharpProjectSnapshotManagerDispatcher! projectSnapshotManagerDispatcher, Microsoft.AspNetCore.Razor.LanguageServer.Common.RemoteTextLoaderFactory! remoteTextLoaderFactory, System.Collections.Generic.IEnumerable<Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Document.OmniSharpDocumentProcessedListener!>! documentProcessedListeners) -> void
|
||||
Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Document.OmniSharpDocumentProcessedListener
|
||||
Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Document.OmniSharpDocumentProcessedListener.OmniSharpDocumentProcessedListener() -> void
|
||||
Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Document.OmniSharpDocumentSnapshot
|
||||
Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Document.OmniSharpDocumentSnapshot.FileKind.get -> string?
|
||||
Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Document.OmniSharpDocumentSnapshot.FilePath.get -> string?
|
||||
Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Document.OmniSharpDocumentSnapshot.HostDocument.get -> Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Document.OmniSharpHostDocument!
|
||||
Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Document.OmniSharpDocumentSnapshot.Project.get -> Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project.OmniSharpProjectSnapshot!
|
||||
Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Document.OmniSharpHostDocument
|
||||
Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Document.OmniSharpHostDocument.FilePath.get -> string!
|
||||
Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Document.OmniSharpHostDocument.OmniSharpHostDocument(string! filePath, string! targetPath, string! kind) -> void
|
||||
Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Document.OmniSharpHostDocumentComparer
|
||||
Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Document.OmniSharpHostDocumentComparer.Equals(Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Document.OmniSharpHostDocument? x, Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Document.OmniSharpHostDocument? y) -> bool
|
||||
Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Document.OmniSharpHostDocumentComparer.GetHashCode(Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Document.OmniSharpHostDocument! hostDocument) -> int
|
||||
Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Extensions.OmniSharpRazorCodeDocumentExtensions
|
||||
Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.FallbackRazorConfiguration
|
||||
Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.FallbackRazorConfiguration.FallbackRazorConfiguration() -> void
|
||||
Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.OmniSharpLanguageServerFeatureOptions
|
||||
Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.OmniSharpLanguageServerFeatureOptions.OmniSharpLanguageServerFeatureOptions() -> void
|
||||
Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project.IOmniSharpProjectSnapshotManagerChangeTrigger
|
||||
Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project.IOmniSharpProjectSnapshotManagerChangeTrigger.Initialize(Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project.OmniSharpProjectSnapshotManager! projectManager) -> void
|
||||
Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project.OmniSharpHostProject
|
||||
Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project.OmniSharpHostProject.Key.get -> Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project.OmniSharpProjectKey!
|
||||
Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project.OmniSharpHostProject.OmniSharpHostProject(string! projectFilePath, string! intermediateOutputPath, Microsoft.AspNetCore.Razor.Language.RazorConfiguration! razorConfiguration, string! rootNamespace) -> void
|
||||
Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project.OmniSharpProjectChangeEventArgs
|
||||
Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project.OmniSharpProjectChangeEventArgs.DocumentFilePath.get -> string?
|
||||
Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project.OmniSharpProjectChangeEventArgs.Kind.get -> Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project.OmniSharpProjectChangeKind
|
||||
Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project.OmniSharpProjectChangeEventArgs.Newer.get -> Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project.OmniSharpProjectSnapshot?
|
||||
Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project.OmniSharpProjectChangeEventArgs.Older.get -> Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project.OmniSharpProjectSnapshot?
|
||||
Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project.OmniSharpProjectChangeEventArgs.OmniSharpProjectChangeEventArgs(Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project.OmniSharpProjectSnapshot? older, Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project.OmniSharpProjectSnapshot? newer, string? documentFilePath, Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project.OmniSharpProjectChangeKind kind) -> void
|
||||
Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project.OmniSharpProjectChangeKind
|
||||
Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project.OmniSharpProjectChangeKind.DocumentAdded = 3 -> Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project.OmniSharpProjectChangeKind
|
||||
Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project.OmniSharpProjectChangeKind.DocumentRemoved = 4 -> Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project.OmniSharpProjectChangeKind
|
||||
Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project.OmniSharpProjectChangeKind.ProjectAdded = 0 -> Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project.OmniSharpProjectChangeKind
|
||||
Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project.OmniSharpProjectChangeKind.ProjectChanged = 2 -> Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project.OmniSharpProjectChangeKind
|
||||
Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project.OmniSharpProjectChangeKind.ProjectRemoved = 1 -> Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project.OmniSharpProjectChangeKind
|
||||
Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project.OmniSharpProjectKey
|
||||
Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project.OmniSharpProjectSnapshot
|
||||
Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project.OmniSharpProjectSnapshot.DocumentFilePaths.get -> System.Collections.Generic.IEnumerable<string!>!
|
||||
Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project.OmniSharpProjectSnapshot.FilePath.get -> string!
|
||||
Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project.OmniSharpProjectSnapshot.GetDocument(string! filePath) -> Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Document.OmniSharpDocumentSnapshot?
|
||||
Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project.OmniSharpProjectSnapshot.Key.get -> Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project.OmniSharpProjectKey!
|
||||
Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project.OmniSharpProjectSnapshot.ProjectWorkspaceState.get -> Microsoft.AspNetCore.Razor.ProjectSystem.ProjectWorkspaceState?
|
||||
Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project.OmniSharpProjectSnapshot.Serialize(string! publishFilePath, Newtonsoft.Json.JsonSerializer! serializer, System.IO.StreamWriter! writer) -> void
|
||||
Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project.OmniSharpProjectSnapshotManager
|
||||
Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project.OmniSharpProjectSnapshotManager.Changed -> System.EventHandler<Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project.OmniSharpProjectChangeEventArgs!>?
|
||||
Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project.OmniSharpProjectSnapshotManager.DocumentAdded(Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project.OmniSharpHostProject! hostProject, Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Document.OmniSharpHostDocument! hostDocument) -> void
|
||||
Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project.OmniSharpProjectSnapshotManager.DocumentChanged(string! projectFilePath, string! documentFilePath) -> void
|
||||
Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project.OmniSharpProjectSnapshotManager.DocumentRemoved(Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project.OmniSharpHostProject! hostProject, Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Document.OmniSharpHostDocument! hostDocument) -> void
|
||||
Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project.OmniSharpProjectSnapshotManager.GetAllProjectKeys(string! projectFilePath) -> System.Collections.Immutable.ImmutableArray<Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project.OmniSharpProjectKey!>
|
||||
Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project.OmniSharpProjectSnapshotManager.GetLoadedProject(Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project.OmniSharpProjectKey! projectKey) -> Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project.OmniSharpProjectSnapshot!
|
||||
Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project.OmniSharpProjectSnapshotManager.ProjectAdded(Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project.OmniSharpHostProject! hostProject) -> void
|
||||
Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project.OmniSharpProjectSnapshotManager.ProjectConfigurationChanged(Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project.OmniSharpHostProject! hostProject) -> void
|
||||
Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project.OmniSharpProjectSnapshotManager.Projects.get -> System.Collections.Generic.IReadOnlyList<Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project.OmniSharpProjectSnapshot!>!
|
||||
Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project.OmniSharpProjectSnapshotManagerAccessor
|
||||
Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project.OmniSharpProjectSnapshotManagerAccessor.Instance.get -> Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project.OmniSharpProjectSnapshotManager!
|
||||
Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project.OmniSharpProjectSnapshotManagerAccessor.OmniSharpProjectSnapshotManagerAccessor(Microsoft.AspNetCore.Razor.LanguageServer.Common.RemoteTextLoaderFactory! remoteTextLoaderFactory, System.Collections.Generic.IEnumerable<Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project.IOmniSharpProjectSnapshotManagerChangeTrigger!>! projectChangeTriggers, Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project.OmniSharpProjectSnapshotManagerDispatcher! projectSnapshotManagerDispatcher, Microsoft.CodeAnalysis.Workspace! workspace) -> void
|
||||
Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project.OmniSharpProjectSnapshotManagerDispatcher
|
||||
Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project.OmniSharpProjectSnapshotManagerDispatcher.AssertDispatcherThread(string? caller = null) -> void
|
||||
Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project.OmniSharpProjectSnapshotManagerDispatcher.DispatcherScheduler.get -> System.Threading.Tasks.TaskScheduler!
|
||||
Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project.OmniSharpProjectSnapshotManagerDispatcher.OmniSharpProjectSnapshotManagerDispatcher() -> void
|
||||
Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project.OmniSharpProjectSnapshotManagerDispatcher.RunOnDispatcherThreadAsync(System.Action! action, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task!
|
||||
Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project.OmniSharpProjectWorkspaceStateGenerator
|
||||
Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project.OmniSharpProjectWorkspaceStateGenerator.Initialize(Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project.OmniSharpProjectSnapshotManager! projectManager) -> void
|
||||
Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project.OmniSharpProjectWorkspaceStateGenerator.OmniSharpProjectWorkspaceStateGenerator(Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project.OmniSharpProjectSnapshotManagerDispatcher! projectSnapshotManagerDispatcher) -> void
|
||||
Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project.OmniSharpWorkspaceProjectStateChangeDetector
|
||||
Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project.OmniSharpWorkspaceProjectStateChangeDetector.Initialize(Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project.OmniSharpProjectSnapshotManager! projectManager) -> void
|
||||
Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project.OmniSharpWorkspaceProjectStateChangeDetector.OmniSharpWorkspaceProjectStateChangeDetector(Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project.OmniSharpProjectSnapshotManagerDispatcher! projectSnapshotManagerDispatcher, Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project.OmniSharpProjectWorkspaceStateGenerator! workspaceStateGenerator, Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.OmniSharpLanguageServerFeatureOptions! languageServerFeatureOptions) -> void
|
||||
Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Serialization.JsonConverterCollectionExtensions
|
||||
static Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Extensions.OmniSharpRazorCodeDocumentExtensions.GetInternalCSharpSourceText(this Microsoft.AspNetCore.Razor.Language.RazorCodeDocument! codeDocument) -> Microsoft.CodeAnalysis.Text.SourceText!
|
||||
static Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.FallbackRazorConfiguration.SelectConfiguration(System.Version! version) -> Microsoft.AspNetCore.Razor.Language.RazorConfiguration!
|
||||
static Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project.OmniSharpProjectKey.From(Microsoft.CodeAnalysis.Project! workspaceProject) -> Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project.OmniSharpProjectKey?
|
||||
static Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Serialization.JsonConverterCollectionExtensions.RegisterOmniSharpRazorConverters(this Newtonsoft.Json.JsonConverterCollection! collection) -> void
|
||||
static readonly Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Document.OmniSharpHostDocumentComparer.Instance -> Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Document.OmniSharpHostDocumentComparer!
|
||||
virtual Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project.OmniSharpProjectWorkspaceStateGenerator.Update(Microsoft.CodeAnalysis.Project! workspaceProject, Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project.OmniSharpProjectSnapshot! projectSnapshot) -> void
|
|
@ -1,39 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT license. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project;
|
||||
using Microsoft.AspNetCore.Razor.Serialization.Converters;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Serialization;
|
||||
|
||||
public static class JsonConverterCollectionExtensions
|
||||
{
|
||||
public static void RegisterOmniSharpRazorConverters(this JsonConverterCollection collection)
|
||||
{
|
||||
collection.RegisterRazorConverters();
|
||||
collection.Add(OmniSharpProjectSnapshotHandleJsonConverter.Instance);
|
||||
}
|
||||
|
||||
private class OmniSharpProjectSnapshotHandleJsonConverter : JsonConverter
|
||||
{
|
||||
internal static readonly OmniSharpProjectSnapshotHandleJsonConverter Instance = new();
|
||||
|
||||
public override bool CanConvert(Type objectType)
|
||||
{
|
||||
return typeof(OmniSharpProjectSnapshot).IsAssignableFrom(objectType);
|
||||
}
|
||||
|
||||
public override object ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer)
|
||||
{
|
||||
var snapshot = (OmniSharpProjectSnapshot)value!;
|
||||
|
||||
serializer.Serialize(writer, snapshot.InternalProjectSnapshot);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,299 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT license. See License.txt in the project root for license information.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Composition;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Document;
|
||||
using Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Extensions;
|
||||
using Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using OmniSharp;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.OmniSharpPlugin;
|
||||
|
||||
// This class is responsible for listening to document processed events and then synchronizing their existence in the C# workspace.
|
||||
// Key scenarios are:
|
||||
// 1. A Razor document is not open - Publish Razor documents C# content to workspace under a background convention file path (i.e. Index.razor__bg__virtual.cs)
|
||||
// 2. A Razor document is already open - Active C# content for the open doc already exists in the workspace (i.e. Index.razor__virtual.cs), noop.
|
||||
// 3. A Razor document gets opened - Remove background generated C# content from workspace so active C# content gets prioritized
|
||||
// 4. A Razor document gets closed - Need to transition active C# content to background C# content in the workspace.
|
||||
//
|
||||
// Since we don't have LSP access to fully understand if a document opens/closes we utilize the convention of our active and background generated C#
|
||||
// file paths to understand if a Razor document is open or closed.
|
||||
|
||||
[Shared]
|
||||
[Export(typeof(OmniSharpDocumentProcessedListener))]
|
||||
internal class BackgroundDocumentProcessedPublisher : OmniSharpDocumentProcessedListener
|
||||
{
|
||||
// File paths need to align with the file path that's used to create virutal document buffers in the RazorDocumentFactory.ts.
|
||||
// The purpose of the alignment is to ensure that when a Razor virtual C# buffer opens we can properly detect its existence.
|
||||
internal const string ActiveVirtualDocumentSuffix = "__virtual.cs";
|
||||
internal const string BackgroundVirtualDocumentSuffix = "__bg" + ActiveVirtualDocumentSuffix;
|
||||
|
||||
private readonly OmniSharpProjectSnapshotManagerDispatcher _projectSnapshotManagerDispatcher;
|
||||
private readonly OmniSharpWorkspace _workspace;
|
||||
private readonly ILogger _logger;
|
||||
private OmniSharpProjectSnapshotManager _projectManager;
|
||||
private readonly object _workspaceChangedLock;
|
||||
|
||||
[ImportingConstructor]
|
||||
public BackgroundDocumentProcessedPublisher(
|
||||
OmniSharpProjectSnapshotManagerDispatcher projectSnapshotManagerDispatcher,
|
||||
OmniSharpWorkspace workspace,
|
||||
ILoggerFactory loggerFactory)
|
||||
{
|
||||
if (projectSnapshotManagerDispatcher is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(projectSnapshotManagerDispatcher));
|
||||
}
|
||||
|
||||
if (workspace is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(workspace));
|
||||
}
|
||||
|
||||
if (loggerFactory is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(loggerFactory));
|
||||
}
|
||||
|
||||
_projectSnapshotManagerDispatcher = projectSnapshotManagerDispatcher;
|
||||
_workspace = workspace;
|
||||
_logger = loggerFactory.CreateLogger<BackgroundDocumentProcessedPublisher>();
|
||||
_workspaceChangedLock = new object();
|
||||
|
||||
_workspace.WorkspaceChanged += Workspace_WorkspaceChanged;
|
||||
}
|
||||
|
||||
// A Razor file has been processed, this portion is responsible for the decision of whether we need to create or update
|
||||
// the Razor documents background C# representation.
|
||||
public override void DocumentProcessed(RazorCodeDocument codeDocument, OmniSharpDocumentSnapshot document)
|
||||
{
|
||||
if (document is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(document));
|
||||
}
|
||||
|
||||
_projectSnapshotManagerDispatcher.AssertDispatcherThread();
|
||||
|
||||
lock (_workspaceChangedLock)
|
||||
{
|
||||
if (FileKinds.IsComponentImport(document.FileKind))
|
||||
{
|
||||
// Razor component imports don't have any C# to generate anyways, don't do the work. This doesn't capture _ViewImports.cshtml because we never
|
||||
// associated a FileKind with those files.
|
||||
return;
|
||||
}
|
||||
|
||||
var openVirtualFilePath = document.FilePath + ActiveVirtualDocumentSuffix;
|
||||
var openDocument = _workspace.GetDocument(openVirtualFilePath);
|
||||
if (openDocument != null)
|
||||
{
|
||||
// This document is open in the editor, no reason for us to populate anything in the workspace the editor will do that.
|
||||
return;
|
||||
}
|
||||
|
||||
var backgroundVirtualFilePath = document.FilePath + BackgroundVirtualDocumentSuffix;
|
||||
var currentDocument = _workspace.GetDocument(backgroundVirtualFilePath);
|
||||
if (currentDocument is null)
|
||||
{
|
||||
// Background document doesn't exist, we need to create it
|
||||
|
||||
var roslynProject = GetRoslynProject(document.Project);
|
||||
if (roslynProject is null)
|
||||
{
|
||||
// There's no Roslyn project associated with the Razor document.
|
||||
_logger.LogTrace("Could not find a Roslyn project for Razor virtual document '{backgroundVirtualFilePath}'.", backgroundVirtualFilePath);
|
||||
return;
|
||||
}
|
||||
|
||||
var documentId = DocumentId.CreateNewId(roslynProject.Id);
|
||||
var name = Path.GetFileName(backgroundVirtualFilePath);
|
||||
var emptyTextLoader = new EmptyTextLoader(backgroundVirtualFilePath);
|
||||
var documentInfo = DocumentInfo.Create(documentId, name, filePath: backgroundVirtualFilePath, loader: emptyTextLoader);
|
||||
_workspace.AddDocument(documentInfo);
|
||||
currentDocument = _workspace.GetDocument(backgroundVirtualFilePath);
|
||||
|
||||
Debug.Assert(currentDocument != null, "We just added the document, it should definitely be there.");
|
||||
}
|
||||
|
||||
// Update document content
|
||||
|
||||
var sourceText = codeDocument.GetInternalCSharpSourceText();
|
||||
_workspace.OnDocumentChanged(currentDocument.Id, sourceText);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Initialize(OmniSharpProjectSnapshotManager projectManager)
|
||||
{
|
||||
if (projectManager is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(projectManager));
|
||||
}
|
||||
|
||||
_projectManager = projectManager;
|
||||
_projectManager.Changed += ProjectManager_Changed;
|
||||
}
|
||||
|
||||
// Here we're specifically listening for cases when a user opens a Razor document and an active C# content gets created.
|
||||
internal void Workspace_WorkspaceChanged(object sender, WorkspaceChangeEventArgs args)
|
||||
{
|
||||
lock (_workspaceChangedLock)
|
||||
{
|
||||
switch (args.Kind)
|
||||
{
|
||||
case WorkspaceChangeKind.DocumentAdded:
|
||||
{
|
||||
// We could technically listen for DocumentAdded here but just because a document gets added doesn't mean it has content included.
|
||||
// Therefore we need to wait for content to populated for Razor files so we don't preemptively remove the corresponding background
|
||||
// C# before the active C# content has been populated.
|
||||
|
||||
var project = args.NewSolution.GetProject(args.ProjectId);
|
||||
var document = project.GetDocument(args.DocumentId);
|
||||
|
||||
if (document.FilePath is null)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (document.FilePath.EndsWith(ActiveVirtualDocumentSuffix, StringComparison.Ordinal) && !document.FilePath.EndsWith(BackgroundVirtualDocumentSuffix, StringComparison.Ordinal))
|
||||
{
|
||||
// Document from editor got opened, clear out any background documents of the same type
|
||||
|
||||
var razorDocumentFilePath = GetRazorDocumentFilePath(document);
|
||||
var backgroundDocumentFilePath = GetBackgroundVirtualDocumentFilePath(razorDocumentFilePath);
|
||||
var backgroundDocument = GetRoslynDocument(project, backgroundDocumentFilePath);
|
||||
if (backgroundDocument != null)
|
||||
{
|
||||
_workspace.RemoveDocument(backgroundDocument.Id);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case WorkspaceChangeKind.DocumentRemoved:
|
||||
{
|
||||
var project = args.OldSolution.GetProject(args.ProjectId);
|
||||
var document = project.GetDocument(args.DocumentId);
|
||||
|
||||
if (document.FilePath is null)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (document.FilePath.EndsWith(ActiveVirtualDocumentSuffix, StringComparison.Ordinal) && !document.FilePath.EndsWith(BackgroundVirtualDocumentSuffix, StringComparison.Ordinal))
|
||||
{
|
||||
var razorDocumentFilePath = GetRazorDocumentFilePath(document);
|
||||
|
||||
if (File.Exists(razorDocumentFilePath))
|
||||
{
|
||||
// Razor document closed because the backing C# virtual document went away
|
||||
var backgroundDocumentFilePath = GetBackgroundVirtualDocumentFilePath(razorDocumentFilePath);
|
||||
var newName = Path.GetFileName(backgroundDocumentFilePath);
|
||||
var delegatedTextLoader = new DelegatedTextLoader(document);
|
||||
var movedDocumentInfo = DocumentInfo.Create(args.DocumentId, newName, loader: delegatedTextLoader, filePath: backgroundDocumentFilePath);
|
||||
_workspace.AddDocument(movedDocumentInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// When the Razor project manager forgets about a document we need remove its background C# representation
|
||||
// so that content doesn't get stale.
|
||||
private void ProjectManager_Changed(object sender, OmniSharpProjectChangeEventArgs args)
|
||||
{
|
||||
switch (args.Kind)
|
||||
{
|
||||
case OmniSharpProjectChangeKind.DocumentRemoved:
|
||||
var roslynProject = GetRoslynProject(args.Older);
|
||||
if (roslynProject is null)
|
||||
{
|
||||
// Project no longer exists
|
||||
return;
|
||||
}
|
||||
|
||||
var backgroundVirtualFilePath = GetBackgroundVirtualDocumentFilePath(args.DocumentFilePath);
|
||||
var backgroundDocument = GetRoslynDocument(roslynProject, backgroundVirtualFilePath);
|
||||
if (backgroundDocument is null)
|
||||
{
|
||||
// No background document associated
|
||||
return;
|
||||
}
|
||||
|
||||
// There's still a background document associated with the removed Razor document.
|
||||
_workspace.RemoveDocument(backgroundDocument.Id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private Project GetRoslynProject(OmniSharpProjectSnapshot project)
|
||||
{
|
||||
var roslynProject = _workspace.CurrentSolution.Projects.FirstOrDefault(roslynProject => string.Equals(roslynProject.FilePath, project.FilePath, FilePathComparison.Instance));
|
||||
return roslynProject;
|
||||
}
|
||||
|
||||
private static Document GetRoslynDocument(Project project, string backgroundDocumentFilePath)
|
||||
{
|
||||
var roslynDocument = project.Documents.FirstOrDefault(document => string.Equals(document.FilePath, backgroundDocumentFilePath, FilePathComparison.Instance));
|
||||
return roslynDocument;
|
||||
}
|
||||
|
||||
private static string GetRazorDocumentFilePath(Document document)
|
||||
{
|
||||
if (document.FilePath.EndsWith(BackgroundVirtualDocumentSuffix, StringComparison.Ordinal))
|
||||
{
|
||||
var razorDocumentFilePath = document.FilePath.Substring(0, document.FilePath.Length - BackgroundVirtualDocumentSuffix.Length);
|
||||
return razorDocumentFilePath;
|
||||
}
|
||||
else if (document.FilePath.EndsWith(ActiveVirtualDocumentSuffix, StringComparison.Ordinal))
|
||||
{
|
||||
var razorDocumentFilePath = document.FilePath.Substring(0, document.FilePath.Length - ActiveVirtualDocumentSuffix.Length);
|
||||
return razorDocumentFilePath;
|
||||
}
|
||||
|
||||
Debug.Fail($"The caller should have ensured that '{document.FilePath}' is associated with a Razor file path.");
|
||||
return null;
|
||||
}
|
||||
|
||||
private static string GetBackgroundVirtualDocumentFilePath(string razorDocumentFilePath)
|
||||
{
|
||||
var backgroundDocumentFilePath = razorDocumentFilePath + BackgroundVirtualDocumentSuffix;
|
||||
return backgroundDocumentFilePath;
|
||||
}
|
||||
|
||||
private class DelegatedTextLoader : TextLoader
|
||||
{
|
||||
private readonly Document _document;
|
||||
|
||||
public DelegatedTextLoader(Document document)
|
||||
{
|
||||
if (document is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(document));
|
||||
}
|
||||
|
||||
_document = document;
|
||||
}
|
||||
|
||||
public async override Task<TextAndVersion> LoadTextAndVersionAsync(LoadTextOptions options, CancellationToken cancellationToken)
|
||||
{
|
||||
var sourceText = await _document.GetTextAsync(cancellationToken);
|
||||
var textVersion = await _document.GetTextVersionAsync(cancellationToken);
|
||||
var textAndVersion = TextAndVersion.Create(sourceText, textVersion);
|
||||
return textAndVersion;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT license. See License.txt in the project root for license information.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.OmniSharpPlugin;
|
||||
|
||||
public abstract class CoreProjectConfigurationProvider : ProjectConfigurationProvider
|
||||
{
|
||||
// Internal for testing
|
||||
internal const string DotNetCoreRazorCapability = "DotNetCoreRazor";
|
||||
internal const string DotNetCoreWebCapability = "DotNetCoreWeb";
|
||||
internal const string DotNetCoreRazorConfigurationCapability = "DotNetCoreRazorConfiguration";
|
||||
|
||||
protected bool HasRazorCoreCapability(ProjectConfigurationProviderContext context) =>
|
||||
context.ProjectCapabilities.Contains(DotNetCoreRazorCapability) ||
|
||||
context.ProjectCapabilities.Contains(DotNetCoreWebCapability);
|
||||
|
||||
protected bool HasRazorCoreConfigurationCapability(ProjectConfigurationProviderContext context) =>
|
||||
context.ProjectCapabilities.Contains(DotNetCoreRazorConfigurationCapability);
|
||||
}
|
|
@ -1,213 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT license. See License.txt in the project root for license information.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Composition;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project;
|
||||
using Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Serialization;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.OmniSharpPlugin;
|
||||
|
||||
[Shared]
|
||||
[Export(typeof(ProjectChangePublisher))]
|
||||
[Export(typeof(IOmniSharpProjectSnapshotManagerChangeTrigger))]
|
||||
internal class DefaultProjectChangePublisher : ProjectChangePublisher, IOmniSharpProjectSnapshotManagerChangeTrigger
|
||||
{
|
||||
private const string TempFileExt = ".temp";
|
||||
|
||||
// Internal for testing
|
||||
internal readonly Dictionary<string, Task> DeferredPublishTasks;
|
||||
private readonly ILogger<DefaultProjectChangePublisher> _logger;
|
||||
private readonly JsonSerializer _serializer;
|
||||
private readonly Dictionary<string, string> _publishFilePathMappings;
|
||||
private readonly Dictionary<string, OmniSharpProjectSnapshot> _pendingProjectPublishes;
|
||||
private readonly object _publishLock;
|
||||
private OmniSharpProjectSnapshotManager _projectManager;
|
||||
|
||||
[ImportingConstructor]
|
||||
public DefaultProjectChangePublisher(ILoggerFactory loggerFactory)
|
||||
{
|
||||
if (loggerFactory is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(loggerFactory));
|
||||
}
|
||||
|
||||
_logger = loggerFactory.CreateLogger<DefaultProjectChangePublisher>();
|
||||
|
||||
_serializer = new JsonSerializer()
|
||||
{
|
||||
Formatting = Formatting.Indented,
|
||||
};
|
||||
|
||||
_serializer.Converters.RegisterOmniSharpRazorConverters();
|
||||
_publishFilePathMappings = new Dictionary<string, string>(FilePathComparer.Instance);
|
||||
DeferredPublishTasks = new Dictionary<string, Task>(FilePathComparer.Instance);
|
||||
_pendingProjectPublishes = new Dictionary<string, OmniSharpProjectSnapshot>(FilePathComparer.Instance);
|
||||
_publishLock = new object();
|
||||
}
|
||||
|
||||
// Internal settable for testing
|
||||
// 250ms between publishes to prevent bursts of changes yet still be responsive to changes.
|
||||
internal int EnqueueDelay { get; set; } = 250;
|
||||
|
||||
public void Initialize(OmniSharpProjectSnapshotManager projectManager)
|
||||
{
|
||||
if (projectManager is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(projectManager));
|
||||
}
|
||||
|
||||
_projectManager = projectManager;
|
||||
_projectManager.Changed += ProjectManager_Changed;
|
||||
}
|
||||
|
||||
public override void SetPublishFilePath(string projectFilePath, string publishFilePath)
|
||||
{
|
||||
lock (_publishLock)
|
||||
{
|
||||
_publishFilePathMappings[projectFilePath] = publishFilePath;
|
||||
}
|
||||
}
|
||||
|
||||
// Virtual for testing
|
||||
protected virtual void SerializeToFile(OmniSharpProjectSnapshot projectSnapshot, string publishFilePath)
|
||||
{
|
||||
// We need to avoid having an incomplete file at any point, but our
|
||||
// project configuration is large enough that it will be written as multiple operations.
|
||||
var tempFilePath = string.Concat(publishFilePath, TempFileExt);
|
||||
var tempFileInfo = new FileInfo(tempFilePath);
|
||||
|
||||
if (tempFileInfo.Exists)
|
||||
{
|
||||
// This could be caused by failures during serialization or early process termination.
|
||||
tempFileInfo.Delete();
|
||||
}
|
||||
|
||||
// This needs to be in explicit brackets because the operation needs to be completed
|
||||
// by the time we move the tempfile into its place
|
||||
using (var writer = tempFileInfo.CreateText())
|
||||
{
|
||||
projectSnapshot.Serialize(publishFilePath, _serializer, writer);
|
||||
}
|
||||
|
||||
var fileInfo = new FileInfo(publishFilePath);
|
||||
if (fileInfo.Exists)
|
||||
{
|
||||
fileInfo.Delete();
|
||||
}
|
||||
|
||||
File.Move(tempFileInfo.FullName, publishFilePath);
|
||||
}
|
||||
|
||||
// Internal for testing
|
||||
internal void Publish(OmniSharpProjectSnapshot projectSnapshot)
|
||||
{
|
||||
if (projectSnapshot is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(projectSnapshot));
|
||||
}
|
||||
|
||||
lock (_publishLock)
|
||||
{
|
||||
string publishFilePath = null;
|
||||
try
|
||||
{
|
||||
if (!_publishFilePathMappings.TryGetValue(projectSnapshot.FilePath, out publishFilePath))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SerializeToFile(projectSnapshot, publishFilePath);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogWarning(@"Could not update Razor project configuration file '{publishFilePath}':
|
||||
{ex}", publishFilePath, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Internal for testing
|
||||
internal void EnqueuePublish(OmniSharpProjectSnapshot projectSnapshot)
|
||||
{
|
||||
lock (_publishLock)
|
||||
{
|
||||
_pendingProjectPublishes[projectSnapshot.FilePath] = projectSnapshot;
|
||||
|
||||
if (!DeferredPublishTasks.TryGetValue(projectSnapshot.FilePath, out var update) || update.IsCompleted)
|
||||
{
|
||||
DeferredPublishTasks[projectSnapshot.FilePath] = PublishAfterDelayAsync(projectSnapshot.FilePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Internal for testing
|
||||
internal void ProjectManager_Changed(object sender, OmniSharpProjectChangeEventArgs args)
|
||||
{
|
||||
switch (args.Kind)
|
||||
{
|
||||
case OmniSharpProjectChangeKind.DocumentRemoved:
|
||||
case OmniSharpProjectChangeKind.DocumentAdded:
|
||||
case OmniSharpProjectChangeKind.ProjectChanged:
|
||||
// These changes can come in bursts so we don't want to overload the publishing system. Therefore,
|
||||
// we enqueue publishes and then publish the latest project after a delay.
|
||||
|
||||
if (args.Newer.ProjectWorkspaceState != null)
|
||||
{
|
||||
EnqueuePublish(args.Newer);
|
||||
}
|
||||
|
||||
break;
|
||||
case OmniSharpProjectChangeKind.ProjectRemoved:
|
||||
RemovePublishingData(args.Older);
|
||||
break;
|
||||
|
||||
// We don't care about ProjectAdded scenarios because a newly added project does not have a workspace state associated with it meaning
|
||||
// it isn't interesting for us to serialize quite yet.
|
||||
}
|
||||
}
|
||||
|
||||
internal void RemovePublishingData(OmniSharpProjectSnapshot projectSnapshot)
|
||||
{
|
||||
lock (_publishLock)
|
||||
{
|
||||
var oldProjectFilePath = projectSnapshot.FilePath;
|
||||
if (!_publishFilePathMappings.TryGetValue(oldProjectFilePath, out var configurationFilePath))
|
||||
{
|
||||
// If we don't track the value in PublishFilePathMappings that means it's already been removed, do nothing.
|
||||
return;
|
||||
}
|
||||
|
||||
if (_pendingProjectPublishes.TryGetValue(oldProjectFilePath, out _))
|
||||
{
|
||||
// Project was removed while a delayed publish was in flight. Clear the in-flight publish so it noops.
|
||||
_pendingProjectPublishes.Remove(oldProjectFilePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task PublishAfterDelayAsync(string projectFilePath)
|
||||
{
|
||||
await Task.Delay(EnqueueDelay);
|
||||
|
||||
lock (_publishLock)
|
||||
{
|
||||
if (!_pendingProjectPublishes.TryGetValue(projectFilePath, out var projectSnapshot))
|
||||
{
|
||||
// Project was removed while waiting for the publish delay.
|
||||
return;
|
||||
}
|
||||
|
||||
_pendingProjectPublishes.Remove(projectFilePath);
|
||||
|
||||
Publish(projectSnapshot);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,108 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT license. See License.txt in the project root for license information.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Composition;
|
||||
using System.Linq;
|
||||
using Microsoft.Build.Evaluation;
|
||||
using Microsoft.Build.Execution;
|
||||
using Microsoft.Build.Framework;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.OmniSharpPlugin;
|
||||
|
||||
// This class enables us to re-evaluate MSBuild project instances. Doing such a thing isn't directly
|
||||
// supported by the ProjectInstance type because they're meant to be a snapshot of an MSBuild project
|
||||
// at a certain point in time. Therefore, we re-create the MSBuild project environment by lifting all
|
||||
// configuration values from the project instance and re-creating our own MSBuild project collection
|
||||
// that can duplicate the work that OmniSharp does.
|
||||
//
|
||||
// Project re-evaluation is required for two reasons:
|
||||
// 1. Razor file additions/deletions. OmniSharp tells us when a change happens to impact the csproj
|
||||
// configuration but not the opaque items of a project such as content/initial compile items.
|
||||
// 2. When OmniSharp initially evaluates the users project they don't do a true design time build.
|
||||
// Because of this several bits of Razor information get left out in the initial project instance,
|
||||
// we then need to force the execution of the appropriate targets to populate that information on
|
||||
// the project instance.
|
||||
[Shared]
|
||||
[Export(typeof(ProjectInstanceEvaluator))]
|
||||
public class DefaultProjectInstanceEvaluator : ProjectInstanceEvaluator
|
||||
{
|
||||
internal const string TargetFrameworkPropertyName = "TargetFramework";
|
||||
internal const string TargetFrameworksPropertyName = "TargetFrameworks";
|
||||
private const string CompileTargetName = "Compile";
|
||||
private const string CoreCompileTargetName = "CoreCompile";
|
||||
private const string RazorGenerateDesignTimeTargetName = "RazorGenerateDesignTime";
|
||||
private const string RazorGenerateComponentDesignTimeTargetName = "RazorGenerateComponentDesignTime";
|
||||
private static readonly IEnumerable<ILogger> s_emptyMSBuildLoggers = Enumerable.Empty<ILogger>();
|
||||
private readonly object _evaluationLock = new object();
|
||||
|
||||
[ImportingConstructor]
|
||||
public DefaultProjectInstanceEvaluator()
|
||||
{
|
||||
}
|
||||
|
||||
public override ProjectInstance Evaluate(ProjectInstance projectInstance)
|
||||
{
|
||||
if (projectInstance is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(projectInstance));
|
||||
}
|
||||
|
||||
lock (_evaluationLock)
|
||||
{
|
||||
var refreshTargets = new List<string>()
|
||||
{
|
||||
// These are the default targets for the project instance that OmniSharp runs.
|
||||
CompileTargetName,
|
||||
CoreCompileTargetName
|
||||
};
|
||||
|
||||
if (projectInstance.Targets.ContainsKey(RazorGenerateDesignTimeTargetName))
|
||||
{
|
||||
refreshTargets.Add(RazorGenerateDesignTimeTargetName);
|
||||
}
|
||||
|
||||
if (projectInstance.Targets.ContainsKey(RazorGenerateComponentDesignTimeTargetName))
|
||||
{
|
||||
refreshTargets.Add(RazorGenerateComponentDesignTimeTargetName);
|
||||
}
|
||||
|
||||
if (refreshTargets.Count > 2)
|
||||
{
|
||||
var projectCollection = new ProjectCollection(projectInstance.GlobalProperties);
|
||||
var project = projectCollection.LoadProject(projectInstance.ProjectFileLocation.File, projectInstance.ToolsVersion);
|
||||
SetTargetFrameworkIfNeeded(project);
|
||||
|
||||
var refreshedProjectInstance = project.CreateProjectInstance();
|
||||
|
||||
// Force a Razor information refresh
|
||||
refreshedProjectInstance.Build(refreshTargets.ToArray(), s_emptyMSBuildLoggers);
|
||||
|
||||
return refreshedProjectInstance;
|
||||
}
|
||||
|
||||
return projectInstance;
|
||||
}
|
||||
}
|
||||
|
||||
private static void SetTargetFrameworkIfNeeded(Project evaluatedProject)
|
||||
{
|
||||
var targetFramework = evaluatedProject.GetPropertyValue(TargetFrameworkPropertyName);
|
||||
var targetFrameworksRaw = evaluatedProject.GetPropertyValue(TargetFrameworksPropertyName);
|
||||
var targetFrameworks = targetFrameworksRaw
|
||||
.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries)
|
||||
.Select(framework => framework.Trim())
|
||||
.ToArray();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(targetFramework) && targetFrameworks.Length > 0)
|
||||
{
|
||||
// Pick first target framework to replicate what OmniSharp does.
|
||||
targetFramework = targetFrameworks[0];
|
||||
evaluatedProject.SetProperty(TargetFrameworkPropertyName, targetFramework);
|
||||
evaluatedProject.ReevaluateIfNecessary();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,62 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT license. See License.txt in the project root for license information.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Composition;
|
||||
using System.Threading;
|
||||
using Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project;
|
||||
using Microsoft.AspNetCore.Razor.OmniSharpPlugin;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.LanguageServer.Common;
|
||||
|
||||
[Shared]
|
||||
[Export(typeof(IRazorDocumentChangeListener))]
|
||||
[Export(typeof(IOmniSharpProjectSnapshotManagerChangeTrigger))]
|
||||
internal class DocumentChangedSynchronizationService : IOmniSharpProjectSnapshotManagerChangeTrigger, IRazorDocumentChangeListener
|
||||
{
|
||||
private readonly OmniSharpProjectSnapshotManagerDispatcher _projectSnapshotManagerDispatcher;
|
||||
private OmniSharpProjectSnapshotManager _projectManager;
|
||||
|
||||
[ImportingConstructor]
|
||||
public DocumentChangedSynchronizationService(OmniSharpProjectSnapshotManagerDispatcher projectSnapshotManagerDispatcher)
|
||||
{
|
||||
if (projectSnapshotManagerDispatcher is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(projectSnapshotManagerDispatcher));
|
||||
}
|
||||
|
||||
_projectSnapshotManagerDispatcher = projectSnapshotManagerDispatcher;
|
||||
}
|
||||
|
||||
public void Initialize(OmniSharpProjectSnapshotManager projectManager)
|
||||
{
|
||||
if (projectManager is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(projectManager));
|
||||
}
|
||||
|
||||
_projectManager = projectManager;
|
||||
}
|
||||
|
||||
public void RazorDocumentChanged(RazorFileChangeEventArgs args)
|
||||
{
|
||||
if (args is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(args));
|
||||
}
|
||||
|
||||
if (args.Kind != RazorFileChangeKind.Changed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var projectFilePath = args.UnevaluatedProjectInstance.ProjectFileLocation.File;
|
||||
var documentFilePath = args.FilePath;
|
||||
|
||||
_ = _projectSnapshotManagerDispatcher.RunOnDispatcherThreadAsync(
|
||||
() => _projectManager.DocumentChanged(projectFilePath, documentFilePath),
|
||||
CancellationToken.None).ConfigureAwait(false);
|
||||
}
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT license. See License.txt in the project root for license information.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.Text;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.OmniSharpPlugin;
|
||||
|
||||
internal class EmptyTextLoader : TextLoader
|
||||
{
|
||||
private readonly string _filePath;
|
||||
private readonly VersionStamp _version;
|
||||
|
||||
public EmptyTextLoader(string filePath)
|
||||
{
|
||||
_filePath = filePath;
|
||||
_version = VersionStamp.Create(); // Version will never change so this can be reused.
|
||||
}
|
||||
|
||||
public override Task<TextAndVersion> LoadTextAndVersionAsync(LoadTextOptions options, CancellationToken cancellationToken)
|
||||
{
|
||||
// Providing an encoding here is important for debuggability. Without this edit-and-continue
|
||||
// won't work for projects with Razor files.
|
||||
return Task.FromResult(TextAndVersion.Create(SourceText.From(string.Empty, Encoding.UTF8), _version, _filePath));
|
||||
}
|
||||
}
|
|
@ -1,85 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT license. See License.txt in the project root for license information.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Reflection.Metadata;
|
||||
using System.Reflection.PortableExecutable;
|
||||
using Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp;
|
||||
using Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Document;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.OmniSharpPlugin;
|
||||
|
||||
internal class FallbackConfigurationProvider : CoreProjectConfigurationProvider
|
||||
{
|
||||
public static FallbackConfigurationProvider Instance = new FallbackConfigurationProvider();
|
||||
|
||||
// Internal for testing
|
||||
internal const string ReferencePathWithRefAssembliesItemType = "ReferencePathWithRefAssemblies";
|
||||
internal const string MvcAssemblyFileName = "Microsoft.AspNetCore.Mvc.Razor.dll";
|
||||
|
||||
public override bool TryResolveConfiguration(ProjectConfigurationProviderContext context, out ProjectConfiguration configuration)
|
||||
{
|
||||
if (!HasRazorCoreCapability(context))
|
||||
{
|
||||
configuration = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
var compilationReferences = context.ProjectInstance.GetItems(ReferencePathWithRefAssembliesItemType);
|
||||
string mvcReferenceFullPath = null;
|
||||
foreach (var compilationReference in compilationReferences)
|
||||
{
|
||||
var assemblyFullPath = compilationReference.EvaluatedInclude;
|
||||
if (assemblyFullPath.EndsWith(MvcAssemblyFileName, FilePathComparison.Instance))
|
||||
{
|
||||
var potentialPathSeparator = assemblyFullPath[assemblyFullPath.Length - MvcAssemblyFileName.Length - 1];
|
||||
if (potentialPathSeparator == '/' || potentialPathSeparator == '\\')
|
||||
{
|
||||
mvcReferenceFullPath = assemblyFullPath;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mvcReferenceFullPath is null)
|
||||
{
|
||||
configuration = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
var version = GetAssemblyVersion(mvcReferenceFullPath);
|
||||
if (version is null)
|
||||
{
|
||||
configuration = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
var razorConfiguration = FallbackRazorConfiguration.SelectConfiguration(version);
|
||||
configuration = new ProjectConfiguration(razorConfiguration, Array.Empty<OmniSharpHostDocument>(), rootNamespace: null);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Protected virtual for testing
|
||||
protected virtual Version GetAssemblyVersion(string filePath)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite | FileShare.Delete))
|
||||
using (var reader = new PEReader(stream))
|
||||
{
|
||||
var metadataReader = reader.GetMetadataReader();
|
||||
|
||||
var assemblyDefinition = metadataReader.GetAssemblyDefinition();
|
||||
return assemblyDefinition.Version;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// We're purposely silencing any kinds of I/O exceptions here, just in case something wacky is going on.
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT license. See License.txt in the project root for license information.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.OmniSharpPlugin;
|
||||
|
||||
internal static class FilePathComparer
|
||||
{
|
||||
private static StringComparer s_instance;
|
||||
|
||||
public static StringComparer Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (s_instance is null && RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
|
||||
{
|
||||
s_instance = StringComparer.Ordinal;
|
||||
}
|
||||
else if (s_instance is null)
|
||||
{
|
||||
s_instance = StringComparer.OrdinalIgnoreCase;
|
||||
}
|
||||
|
||||
return s_instance;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT license. See License.txt in the project root for license information.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.OmniSharpPlugin;
|
||||
|
||||
internal static class FilePathComparison
|
||||
{
|
||||
private static StringComparison? s_instance;
|
||||
|
||||
public static StringComparison Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (s_instance is null && RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
|
||||
{
|
||||
s_instance = StringComparison.Ordinal;
|
||||
}
|
||||
else if (s_instance is null)
|
||||
{
|
||||
s_instance = StringComparison.OrdinalIgnoreCase;
|
||||
}
|
||||
|
||||
return s_instance.Value;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT license. See License.txt in the project root for license information.
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.OmniSharpPlugin;
|
||||
|
||||
internal interface IRazorDocumentChangeListener
|
||||
{
|
||||
void RazorDocumentChanged(RazorFileChangeEventArgs args);
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT license. See License.txt in the project root for license information.
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.OmniSharpPlugin;
|
||||
|
||||
internal interface IRazorDocumentOutputChangeListener
|
||||
{
|
||||
void RazorDocumentOutputChanged(RazorFileChangeEventArgs args);
|
||||
}
|
|
@ -1,290 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT license. See License.txt in the project root for license information.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Composition;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Document;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.Build.Execution;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.OmniSharpPlugin;
|
||||
|
||||
[Shared]
|
||||
[Export(typeof(ProjectConfigurationProvider))]
|
||||
internal class LatestProjectConfigurationProvider : CoreProjectConfigurationProvider
|
||||
{
|
||||
// Internal for testing
|
||||
internal const string RazorGenerateWithTargetPathItemType = "RazorGenerateWithTargetPath";
|
||||
internal const string RazorComponentWithTargetPathItemType = "RazorComponentWithTargetPath";
|
||||
internal const string RazorTargetPathMetadataName = "TargetPath";
|
||||
internal const string RootNamespaceProperty = "RootNamespace";
|
||||
|
||||
private const string RazorLangVersionProperty = "RazorLangVersion";
|
||||
private const string RazorDefaultConfigurationProperty = "RazorDefaultConfiguration";
|
||||
private const string RazorExtensionItemType = "RazorExtension";
|
||||
private const string RazorConfigurationItemType = "RazorConfiguration";
|
||||
private const string RazorConfigurationItemTypeExtensionsProperty = "Extensions";
|
||||
|
||||
public override bool TryResolveConfiguration(ProjectConfigurationProviderContext context, out ProjectConfiguration configuration)
|
||||
{
|
||||
if (!HasRazorCoreCapability(context))
|
||||
{
|
||||
configuration = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!HasRazorCoreConfigurationCapability(context))
|
||||
{
|
||||
// Razor project is < 2.1, we don't handle that.
|
||||
configuration = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
var projectInstance = context.ProjectInstance;
|
||||
if (!TryGetConfiguration(projectInstance, out configuration))
|
||||
{
|
||||
configuration = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Internal for testing
|
||||
internal static bool TryGetConfiguration(
|
||||
ProjectInstance projectInstance,
|
||||
out ProjectConfiguration configuration)
|
||||
{
|
||||
if (!TryGetDefaultConfiguration(projectInstance, out var defaultConfiguration))
|
||||
{
|
||||
configuration = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!TryGetLanguageVersion(projectInstance, out var languageVersion))
|
||||
{
|
||||
configuration = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!TryGetConfigurationItem(defaultConfiguration, projectInstance.Items, out var configurationItem))
|
||||
{
|
||||
configuration = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
var configuredExtensionNames = GetConfiguredExtensionNames(configurationItem);
|
||||
var rootNamespace = GetRootNamespace(projectInstance);
|
||||
var extensions = GetExtensions(configuredExtensionNames, projectInstance.Items);
|
||||
var razorConfiguration = new ProjectSystemRazorConfiguration(languageVersion, configurationItem.EvaluatedInclude, extensions);
|
||||
var hostDocuments = GetHostDocuments(projectInstance.Items);
|
||||
|
||||
configuration = new ProjectConfiguration(razorConfiguration, hostDocuments, rootNamespace);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Internal for testing
|
||||
internal static string GetRootNamespace(ProjectInstance projectInstance)
|
||||
{
|
||||
var rootNamespace = projectInstance.GetPropertyValue(RootNamespaceProperty);
|
||||
if (string.IsNullOrEmpty(rootNamespace))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return rootNamespace;
|
||||
}
|
||||
|
||||
// Internal for testing
|
||||
internal static IReadOnlyList<OmniSharpHostDocument> GetHostDocuments(ICollection<ProjectItemInstance> projectItems)
|
||||
{
|
||||
var hostDocuments = new HashSet<OmniSharpHostDocument>();
|
||||
|
||||
foreach (var item in projectItems)
|
||||
{
|
||||
if (item.ItemType == RazorGenerateWithTargetPathItemType)
|
||||
{
|
||||
var filePath = Path.Combine(item.Project.Directory, item.EvaluatedInclude);
|
||||
var originalTargetPath = item.GetMetadataValue(RazorTargetPathMetadataName);
|
||||
var normalizedTargetPath = NormalizeTargetPath(originalTargetPath);
|
||||
var hostDocument = new OmniSharpHostDocument(filePath, normalizedTargetPath, FileKinds.Legacy);
|
||||
hostDocuments.Add(hostDocument);
|
||||
}
|
||||
else if (item.ItemType == RazorComponentWithTargetPathItemType)
|
||||
{
|
||||
var filePath = Path.Combine(item.Project.Directory, item.EvaluatedInclude);
|
||||
var originalTargetPath = item.GetMetadataValue(RazorTargetPathMetadataName);
|
||||
var normalizedTargetPath = NormalizeTargetPath(originalTargetPath);
|
||||
var fileKind = FileKinds.GetComponentFileKindFromFilePath(filePath);
|
||||
var hostDocument = new OmniSharpHostDocument(filePath, normalizedTargetPath, fileKind);
|
||||
hostDocuments.Add(hostDocument);
|
||||
}
|
||||
}
|
||||
|
||||
return hostDocuments.ToList();
|
||||
}
|
||||
|
||||
// Internal for testing
|
||||
internal static bool TryGetDefaultConfiguration(ProjectInstance projectInstance, out string defaultConfiguration)
|
||||
{
|
||||
defaultConfiguration = projectInstance.GetPropertyValue(RazorDefaultConfigurationProperty);
|
||||
if (string.IsNullOrEmpty(defaultConfiguration))
|
||||
{
|
||||
defaultConfiguration = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Internal for testing
|
||||
internal static bool TryGetLanguageVersion(ProjectInstance projectInstance, out RazorLanguageVersion languageVersion)
|
||||
{
|
||||
var languageVersionValue = projectInstance.GetPropertyValue(RazorLangVersionProperty);
|
||||
if (string.IsNullOrEmpty(languageVersionValue))
|
||||
{
|
||||
languageVersion = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!RazorLanguageVersion.TryParse(languageVersionValue, out languageVersion))
|
||||
{
|
||||
languageVersion = RazorLanguageVersion.Latest;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Internal for testing
|
||||
internal static bool TryGetConfigurationItem(
|
||||
string configuration,
|
||||
IEnumerable<ProjectItemInstance> projectItems,
|
||||
out ProjectItemInstance configurationItem)
|
||||
{
|
||||
foreach (var item in projectItems)
|
||||
{
|
||||
if (item.ItemType == RazorConfigurationItemType && item.EvaluatedInclude == configuration)
|
||||
{
|
||||
configurationItem = item;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
configurationItem = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Internal for testing
|
||||
internal static string[] GetConfiguredExtensionNames(ProjectItemInstance configurationItem)
|
||||
{
|
||||
var extensionNamesValue = configurationItem.GetMetadataValue(RazorConfigurationItemTypeExtensionsProperty);
|
||||
|
||||
if (string.IsNullOrEmpty(extensionNamesValue))
|
||||
{
|
||||
return Array.Empty<string>();
|
||||
}
|
||||
|
||||
var configuredExtensionNames = extensionNamesValue.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
return configuredExtensionNames;
|
||||
}
|
||||
|
||||
// Internal for testing
|
||||
internal static ProjectSystemRazorExtension[] GetExtensions(
|
||||
string[] configuredExtensionNames,
|
||||
IEnumerable<ProjectItemInstance> projectItems)
|
||||
{
|
||||
var extensions = new List<ProjectSystemRazorExtension>();
|
||||
|
||||
foreach (var item in projectItems)
|
||||
{
|
||||
if (item.ItemType != RazorExtensionItemType)
|
||||
{
|
||||
// Not a RazorExtension
|
||||
continue;
|
||||
}
|
||||
|
||||
var extensionName = item.EvaluatedInclude;
|
||||
if (configuredExtensionNames.Contains(extensionName))
|
||||
{
|
||||
extensions.Add(new ProjectSystemRazorExtension(extensionName));
|
||||
}
|
||||
}
|
||||
|
||||
return extensions.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// TargetPath is defined as using '\' but some Tasks used to set that parameter don't respect that, so we normalize.
|
||||
/// </summary>
|
||||
/// <param name="targetPath">The TargetPath to be normalized.</param>
|
||||
/// <returns>A normalized TargetPath</returns>
|
||||
internal static string NormalizeTargetPath(string targetPath)
|
||||
{
|
||||
if (targetPath is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(targetPath));
|
||||
}
|
||||
|
||||
var normalizedTargetPath = targetPath.Replace('/', '\\');
|
||||
normalizedTargetPath = normalizedTargetPath.TrimStart('\\');
|
||||
return normalizedTargetPath;
|
||||
}
|
||||
|
||||
private class ProjectSystemRazorConfiguration : RazorConfiguration
|
||||
{
|
||||
public ProjectSystemRazorConfiguration(
|
||||
RazorLanguageVersion languageVersion,
|
||||
string configurationName,
|
||||
RazorExtension[] extensions,
|
||||
bool useConsolidatedMvcViews = false)
|
||||
{
|
||||
if (languageVersion is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(languageVersion));
|
||||
}
|
||||
|
||||
if (configurationName is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(configurationName));
|
||||
}
|
||||
|
||||
if (extensions is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(extensions));
|
||||
}
|
||||
|
||||
LanguageVersion = languageVersion;
|
||||
ConfigurationName = configurationName;
|
||||
Extensions = extensions;
|
||||
UseConsolidatedMvcViews = useConsolidatedMvcViews;
|
||||
}
|
||||
|
||||
public override string ConfigurationName { get; }
|
||||
|
||||
public override IReadOnlyList<RazorExtension> Extensions { get; }
|
||||
|
||||
public override RazorLanguageVersion LanguageVersion { get; }
|
||||
|
||||
public override bool UseConsolidatedMvcViews { get; }
|
||||
}
|
||||
|
||||
internal class ProjectSystemRazorExtension : RazorExtension
|
||||
{
|
||||
public ProjectSystemRazorExtension(string extensionName)
|
||||
{
|
||||
if (extensionName is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(extensionName));
|
||||
}
|
||||
|
||||
ExtensionName = extensionName;
|
||||
}
|
||||
|
||||
public override string ExtensionName { get; }
|
||||
}
|
||||
}
|
|
@ -1,162 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT license. See License.txt in the project root for license information.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Composition;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNetCore.Razor.LanguageServer.Common;
|
||||
using Microsoft.Build.Execution;
|
||||
using OmniSharp.MSBuild.Notification;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.OmniSharpPlugin;
|
||||
|
||||
[Shared]
|
||||
[Export(typeof(IMSBuildEventSink))]
|
||||
internal class MSBuildProjectDocumentChangeDetector : IMSBuildEventSink
|
||||
{
|
||||
private const string MSBuildProjectFullPathPropertyName = "MSBuildProjectFullPath";
|
||||
private const string MSBuildProjectDirectoryPropertyName = "MSBuildProjectDirectory";
|
||||
private static readonly IReadOnlyList<string> s_razorFileExtensions = new[] { ".razor", ".cshtml" };
|
||||
|
||||
private readonly Dictionary<string, IReadOnlyList<FileSystemWatcher>> _watcherMap;
|
||||
private readonly IReadOnlyList<IRazorDocumentChangeListener> _documentChangeListeners;
|
||||
private readonly List<IRazorDocumentOutputChangeListener> _documentOutputChangeListeners;
|
||||
|
||||
[ImportingConstructor]
|
||||
public MSBuildProjectDocumentChangeDetector(
|
||||
[ImportMany] IEnumerable<IRazorDocumentChangeListener> documentChangeListeners,
|
||||
[ImportMany] IEnumerable<IRazorDocumentOutputChangeListener> documentOutputChangeListeners)
|
||||
{
|
||||
if (documentChangeListeners is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(documentChangeListeners));
|
||||
}
|
||||
|
||||
if (documentOutputChangeListeners is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(documentOutputChangeListeners));
|
||||
}
|
||||
|
||||
_watcherMap = new Dictionary<string, IReadOnlyList<FileSystemWatcher>>(FilePathComparer.Instance);
|
||||
_documentChangeListeners = documentChangeListeners.ToList();
|
||||
_documentOutputChangeListeners = documentOutputChangeListeners.ToList();
|
||||
}
|
||||
|
||||
public void ProjectLoaded(ProjectLoadedEventArgs loadedArgs)
|
||||
{
|
||||
if (loadedArgs is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(loadedArgs));
|
||||
}
|
||||
|
||||
var projectInstance = loadedArgs.ProjectInstance;
|
||||
var projectFilePath = projectInstance.GetPropertyValue(MSBuildProjectFullPathPropertyName);
|
||||
if (string.IsNullOrEmpty(projectFilePath))
|
||||
{
|
||||
// This should never be true but we're being extra careful.
|
||||
return;
|
||||
}
|
||||
|
||||
var projectDirectory = projectInstance.GetPropertyValue(MSBuildProjectDirectoryPropertyName);
|
||||
if (string.IsNullOrEmpty(projectDirectory))
|
||||
{
|
||||
// This should never be true but we're beign extra careful.
|
||||
return;
|
||||
}
|
||||
|
||||
if (_watcherMap.TryGetValue(projectDirectory, out var existingWatchers))
|
||||
{
|
||||
for (var i = 0; i < existingWatchers.Count; i++)
|
||||
{
|
||||
existingWatchers[i].Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
var watchers = new List<FileSystemWatcher>(s_razorFileExtensions.Count);
|
||||
for (var i = 0; i < s_razorFileExtensions.Count; i++)
|
||||
{
|
||||
var documentWatcher = new FileSystemWatcher(projectDirectory, "*" + s_razorFileExtensions[i])
|
||||
{
|
||||
NotifyFilter = NotifyFilters.FileName | NotifyFilters.LastWrite | NotifyFilters.CreationTime,
|
||||
IncludeSubdirectories = true,
|
||||
};
|
||||
|
||||
documentWatcher.Created += (sender, args) => FileSystemWatcher_RazorDocumentEvent(args.FullPath, projectInstance, RazorFileChangeKind.Added);
|
||||
documentWatcher.Deleted += (sender, args) => FileSystemWatcher_RazorDocumentEvent(args.FullPath, projectInstance, RazorFileChangeKind.Removed);
|
||||
documentWatcher.Changed += (sender, args) => FileSystemWatcher_RazorDocumentEvent(args.FullPath, projectInstance, RazorFileChangeKind.Changed);
|
||||
documentWatcher.Renamed += (sender, args) =>
|
||||
{
|
||||
// Translate file renames into remove / add
|
||||
|
||||
if (s_razorFileExtensions.Any(extension => args.OldFullPath.EndsWith(extension, StringComparison.Ordinal)))
|
||||
{
|
||||
// Renaming from Razor file to something else.
|
||||
FileSystemWatcher_RazorDocumentEvent(args.OldFullPath, projectInstance, RazorFileChangeKind.Removed);
|
||||
}
|
||||
|
||||
if (s_razorFileExtensions.Any(extension => args.FullPath.EndsWith(extension, StringComparison.Ordinal)))
|
||||
{
|
||||
// Renaming into a Razor file. This typically occurs when users go from .cshtml => .razor
|
||||
FileSystemWatcher_RazorDocumentEvent(args.FullPath, projectInstance, RazorFileChangeKind.Added);
|
||||
}
|
||||
};
|
||||
watchers.Add(documentWatcher);
|
||||
|
||||
var documentOutputWatcher = new FileSystemWatcher(projectDirectory, "*" + s_razorFileExtensions[i] + ".g.cs")
|
||||
{
|
||||
NotifyFilter = NotifyFilters.FileName | NotifyFilters.LastWrite,
|
||||
IncludeSubdirectories = true,
|
||||
};
|
||||
|
||||
documentOutputWatcher.Created += (sender, args) => FileSystemWatcher_RazorDocumentOutputEvent(args.FullPath, projectInstance, RazorFileChangeKind.Added);
|
||||
documentOutputWatcher.Deleted += (sender, args) => FileSystemWatcher_RazorDocumentOutputEvent(args.FullPath, projectInstance, RazorFileChangeKind.Removed);
|
||||
documentOutputWatcher.Changed += (sender, args) => FileSystemWatcher_RazorDocumentOutputEvent(args.FullPath, projectInstance, RazorFileChangeKind.Changed);
|
||||
documentOutputWatcher.Renamed += (sender, args) =>
|
||||
{
|
||||
// Translate file renames into remove / add
|
||||
|
||||
if (s_razorFileExtensions.Any(extension => args.OldFullPath.EndsWith(extension + ".g.cs", StringComparison.Ordinal)))
|
||||
{
|
||||
// Renaming from Razor background file to something else.
|
||||
FileSystemWatcher_RazorDocumentOutputEvent(args.OldFullPath, projectInstance, RazorFileChangeKind.Removed);
|
||||
}
|
||||
|
||||
if (s_razorFileExtensions.Any(extension => args.FullPath.EndsWith(extension + ".g.cs", StringComparison.Ordinal)))
|
||||
{
|
||||
// Renaming into a Razor generated file.
|
||||
FileSystemWatcher_RazorDocumentOutputEvent(args.FullPath, projectInstance, RazorFileChangeKind.Added);
|
||||
}
|
||||
};
|
||||
watchers.Add(documentOutputWatcher);
|
||||
|
||||
documentWatcher.EnableRaisingEvents = true;
|
||||
documentOutputWatcher.EnableRaisingEvents = true;
|
||||
}
|
||||
|
||||
_watcherMap[projectDirectory] = watchers;
|
||||
}
|
||||
|
||||
// Internal for testing
|
||||
internal void FileSystemWatcher_RazorDocumentEvent(string filePath, ProjectInstance projectInstance, RazorFileChangeKind changeKind)
|
||||
{
|
||||
var args = new RazorFileChangeEventArgs(filePath, projectInstance, changeKind);
|
||||
for (var i = 0; i < _documentChangeListeners.Count; i++)
|
||||
{
|
||||
_documentChangeListeners[i].RazorDocumentChanged(args);
|
||||
}
|
||||
}
|
||||
|
||||
// Internal for testing
|
||||
internal void FileSystemWatcher_RazorDocumentOutputEvent(string filePath, ProjectInstance projectInstance, RazorFileChangeKind changeKind)
|
||||
{
|
||||
var args = new RazorFileChangeEventArgs(filePath, projectInstance, changeKind);
|
||||
for (var i = 0; i < _documentOutputChangeListeners.Count; i++)
|
||||
{
|
||||
_documentOutputChangeListeners[i].RazorDocumentOutputChanged(args);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,327 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT license. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Composition;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Document;
|
||||
using Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project;
|
||||
using Microsoft.AspNetCore.Razor.LanguageServer.Common;
|
||||
using Microsoft.AspNetCore.Razor.OmniSharpPlugin;
|
||||
using Microsoft.Build.Execution;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using OmniSharp.MSBuild.Notification;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.OmnisharpPlugin;
|
||||
|
||||
[Shared]
|
||||
[Export(typeof(IMSBuildEventSink))]
|
||||
[Export(typeof(IRazorDocumentChangeListener))]
|
||||
[Export(typeof(IOmniSharpProjectSnapshotManagerChangeTrigger))]
|
||||
internal class MSBuildProjectManager : IOmniSharpProjectSnapshotManagerChangeTrigger, IMSBuildEventSink, IRazorDocumentChangeListener
|
||||
{
|
||||
// Internal for testing
|
||||
internal const string IntermediateOutputPathPropertyName = "IntermediateOutputPath";
|
||||
internal const string MSBuildProjectDirectoryPropertyName = "MSBuildProjectDirectory";
|
||||
internal const string ProjectCapabilityItemType = "ProjectCapability";
|
||||
|
||||
private const string MSBuildProjectFullPathPropertyName = "MSBuildProjectFullPath";
|
||||
private const string DebugRazorOmnisharpPluginPropertyName = "_DebugRazorOmnisharpPlugin_";
|
||||
private readonly ILogger _logger;
|
||||
private readonly IEnumerable<ProjectConfigurationProvider> _projectConfigurationProviders;
|
||||
private readonly ProjectInstanceEvaluator _projectInstanceEvaluator;
|
||||
private readonly ProjectChangePublisher _projectConfigurationPublisher;
|
||||
private readonly OmniSharpProjectSnapshotManagerDispatcher _projectSnapshotManagerDispatcher;
|
||||
private OmniSharpProjectSnapshotManager? _projectManager;
|
||||
|
||||
[ImportingConstructor]
|
||||
public MSBuildProjectManager(
|
||||
[ImportMany] IEnumerable<ProjectConfigurationProvider> projectConfigurationProviders,
|
||||
ProjectInstanceEvaluator projectInstanceEvaluator,
|
||||
ProjectChangePublisher projectConfigurationPublisher,
|
||||
OmniSharpProjectSnapshotManagerDispatcher projectSnapshotManagerDispatcher,
|
||||
ILoggerFactory loggerFactory)
|
||||
{
|
||||
if (projectConfigurationProviders is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(projectConfigurationProviders));
|
||||
}
|
||||
|
||||
if (projectInstanceEvaluator is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(projectInstanceEvaluator));
|
||||
}
|
||||
|
||||
if (projectConfigurationPublisher is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(projectConfigurationPublisher));
|
||||
}
|
||||
|
||||
if (projectSnapshotManagerDispatcher is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(projectSnapshotManagerDispatcher));
|
||||
}
|
||||
|
||||
if (loggerFactory is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(loggerFactory));
|
||||
}
|
||||
|
||||
_logger = loggerFactory.CreateLogger<MSBuildProjectManager>();
|
||||
_projectConfigurationProviders = projectConfigurationProviders;
|
||||
_projectInstanceEvaluator = projectInstanceEvaluator;
|
||||
_projectConfigurationPublisher = projectConfigurationPublisher;
|
||||
_projectSnapshotManagerDispatcher = projectSnapshotManagerDispatcher;
|
||||
}
|
||||
|
||||
public OmniSharpProjectSnapshotManager ProjectManager => _projectManager ?? throw new InvalidOperationException($"{nameof(ProjectManager)} was unexpectedly 'null'. Has {nameof(Initialize)} been called?");
|
||||
|
||||
public void Initialize(OmniSharpProjectSnapshotManager projectManager)
|
||||
{
|
||||
if (projectManager is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(projectManager));
|
||||
}
|
||||
|
||||
_projectManager = projectManager;
|
||||
}
|
||||
|
||||
public void ProjectLoaded(ProjectLoadedEventArgs args)
|
||||
{
|
||||
_ = ProjectLoadedAsync(args, CancellationToken.None);
|
||||
}
|
||||
|
||||
public void RazorDocumentChanged(RazorFileChangeEventArgs args)
|
||||
{
|
||||
if (args.Kind == RazorFileChangeKind.Added ||
|
||||
args.Kind == RazorFileChangeKind.Removed)
|
||||
{
|
||||
// When documents get added or removed we need to refresh project state to properly reflect the host documents in the project.
|
||||
|
||||
var evaluatedProjectInstance = _projectInstanceEvaluator.Evaluate(args.UnevaluatedProjectInstance);
|
||||
_ = _projectSnapshotManagerDispatcher.RunOnDispatcherThreadAsync(
|
||||
() => UpdateProjectState(evaluatedProjectInstance), CancellationToken.None).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
// Internal for testing
|
||||
internal async Task ProjectLoadedAsync(ProjectLoadedEventArgs args, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
var projectInstance = args.ProjectInstance;
|
||||
HandleDebug(projectInstance);
|
||||
|
||||
if (!TryResolveConfigurationOutputPath(projectInstance, out var configPath))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var projectFilePath = projectInstance.GetPropertyValue(MSBuildProjectFullPathPropertyName);
|
||||
if (string.IsNullOrEmpty(projectFilePath))
|
||||
{
|
||||
// This should never be true but we're being extra careful.
|
||||
return;
|
||||
}
|
||||
|
||||
_projectConfigurationPublisher.SetPublishFilePath(projectFilePath, configPath);
|
||||
|
||||
// Force project instance evaluation to ensure that all Razor specific targets have run.
|
||||
projectInstance = _projectInstanceEvaluator.Evaluate(projectInstance);
|
||||
|
||||
await _projectSnapshotManagerDispatcher.RunOnDispatcherThreadAsync(
|
||||
() => UpdateProjectState(projectInstance), cancellationToken).ConfigureAwait(false);
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError("Unexpected exception got thrown from the Razor plugin: {exception}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateProjectState(ProjectInstance projectInstance)
|
||||
{
|
||||
_projectSnapshotManagerDispatcher.AssertDispatcherThread();
|
||||
|
||||
var projectFilePath = projectInstance.GetPropertyValue(MSBuildProjectFullPathPropertyName);
|
||||
if (string.IsNullOrEmpty(projectFilePath))
|
||||
{
|
||||
// This should never be true but we're being extra careful.
|
||||
return;
|
||||
}
|
||||
|
||||
var projectConfiguration = GetProjectConfiguration(projectInstance, _projectConfigurationProviders);
|
||||
if (projectConfiguration is null)
|
||||
{
|
||||
// Not a Razor project
|
||||
return;
|
||||
}
|
||||
|
||||
if (!TryResolveConfigurationOutputPath(projectInstance, out var configPath) || configPath is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var hostProject = new OmniSharpHostProject(projectFilePath, configPath, projectConfiguration.Configuration, projectConfiguration.RootNamespace);
|
||||
var projectSnapshot = ProjectManager.GetLoadedProject(hostProject.Key);
|
||||
if (projectSnapshot is null)
|
||||
{
|
||||
// Project doesn't exist yet, create it and set it up with all of its host documents.
|
||||
|
||||
ProjectManager.ProjectAdded(hostProject);
|
||||
|
||||
foreach (var hostDocument in projectConfiguration.Documents)
|
||||
{
|
||||
ProjectManager.DocumentAdded(hostProject, hostDocument);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Project already exists (project change). Reconfigure the project and add or remove host documents to synchronize it with the configured host documents.
|
||||
|
||||
ProjectManager.ProjectConfigurationChanged(hostProject);
|
||||
|
||||
SynchronizeDocuments(projectConfiguration.Documents, projectSnapshot, hostProject);
|
||||
}
|
||||
}
|
||||
|
||||
// Internal for testing
|
||||
internal void SynchronizeDocuments(
|
||||
IReadOnlyList<OmniSharpHostDocument> configuredHostDocuments,
|
||||
OmniSharpProjectSnapshot projectSnapshot,
|
||||
OmniSharpHostProject hostProject)
|
||||
{
|
||||
// Remove any documents that need to be removed
|
||||
foreach (var documentFilePath in projectSnapshot.DocumentFilePaths)
|
||||
{
|
||||
OmniSharpHostDocument? associatedHostDocument = null;
|
||||
|
||||
var documentSnapshot = projectSnapshot.GetDocument(documentFilePath);
|
||||
if (documentSnapshot is null)
|
||||
{
|
||||
_logger.LogWarning("Missing DocumentSnapshot for {documentFilePath}.", documentFilePath);
|
||||
continue;
|
||||
}
|
||||
|
||||
var currentHostDocument = documentSnapshot.HostDocument;
|
||||
|
||||
for (var i = 0; i < configuredHostDocuments.Count; i++)
|
||||
{
|
||||
var configuredHostDocument = configuredHostDocuments[i];
|
||||
if (OmniSharpHostDocumentComparer.Instance.Equals(configuredHostDocument, currentHostDocument))
|
||||
{
|
||||
associatedHostDocument = configuredHostDocument;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (associatedHostDocument is null)
|
||||
{
|
||||
// Document was removed
|
||||
ProjectManager.DocumentRemoved(hostProject, currentHostDocument);
|
||||
}
|
||||
}
|
||||
|
||||
// Refresh the project snapshot to reflect any removed documents.
|
||||
projectSnapshot = ProjectManager.GetLoadedProject(projectSnapshot.Key);
|
||||
|
||||
// Add any documents that need to be added
|
||||
for (var i = 0; i < configuredHostDocuments.Count; i++)
|
||||
{
|
||||
var hostDocument = configuredHostDocuments[i];
|
||||
if (!projectSnapshot.DocumentFilePaths.Contains(hostDocument.FilePath, FilePathComparer.Instance))
|
||||
{
|
||||
// Document was added.
|
||||
ProjectManager.DocumentAdded(hostProject, hostDocument);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Internal for testing
|
||||
internal static ProjectConfiguration? GetProjectConfiguration(
|
||||
ProjectInstance projectInstance,
|
||||
IEnumerable<ProjectConfigurationProvider> projectConfigurationProviders)
|
||||
{
|
||||
if (projectInstance is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(projectInstance));
|
||||
}
|
||||
|
||||
if (projectConfigurationProviders is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(projectConfigurationProviders));
|
||||
}
|
||||
|
||||
var projectCapabilities = projectInstance
|
||||
.GetItems(ProjectCapabilityItemType)
|
||||
.Select(capability => capability.EvaluatedInclude)
|
||||
.ToList();
|
||||
|
||||
var context = new ProjectConfigurationProviderContext(projectCapabilities, projectInstance);
|
||||
foreach (var projectConfigurationProvider in projectConfigurationProviders)
|
||||
{
|
||||
if (projectConfigurationProvider.TryResolveConfiguration(context, out var configuration))
|
||||
{
|
||||
return configuration;
|
||||
}
|
||||
}
|
||||
|
||||
if (FallbackConfigurationProvider.Instance.TryResolveConfiguration(context, out var fallbackConfiguration))
|
||||
{
|
||||
return fallbackConfiguration;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void HandleDebug(ProjectInstance projectInstance)
|
||||
{
|
||||
var debugPlugin = projectInstance.GetPropertyValue(DebugRazorOmnisharpPluginPropertyName);
|
||||
if (!string.IsNullOrEmpty(debugPlugin) && string.Equals(debugPlugin, "true", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
Console.WriteLine($"Waiting for a debugger to attach to the Razor Plugin. Process id: {Process.GetCurrentProcess().Id}");
|
||||
while (!Debugger.IsAttached)
|
||||
{
|
||||
Thread.Sleep(1000);
|
||||
}
|
||||
|
||||
Debugger.Break();
|
||||
}
|
||||
}
|
||||
|
||||
// Internal for testing
|
||||
internal static bool TryResolveConfigurationOutputPath(ProjectInstance projectInstance, out string? path)
|
||||
{
|
||||
var intermediateOutputPath = projectInstance.GetPropertyValue(IntermediateOutputPathPropertyName);
|
||||
if (string.IsNullOrEmpty(intermediateOutputPath))
|
||||
{
|
||||
path = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Path.IsPathRooted(intermediateOutputPath))
|
||||
{
|
||||
// Relative path, need to convert to absolute.
|
||||
var projectDirectory = projectInstance.GetPropertyValue(MSBuildProjectDirectoryPropertyName);
|
||||
if (string.IsNullOrEmpty(projectDirectory))
|
||||
{
|
||||
// This should never be true but we're being extra careful.
|
||||
path = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
intermediateOutputPath = Path.Combine(projectDirectory, intermediateOutputPath);
|
||||
}
|
||||
|
||||
intermediateOutputPath = intermediateOutputPath
|
||||
.Replace('\\', Path.DirectorySeparatorChar)
|
||||
.Replace('/', Path.DirectorySeparatorChar);
|
||||
path = Path.Combine(intermediateOutputPath, LanguageServerConstants.DefaultProjectConfigurationFile);
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>$(DefaultNetFxTargetFramework)</TargetFramework>
|
||||
|
||||
<Description>Razor is a markup syntax for adding logic to pages. This package contains the Omnisharp Razor plugin that extracts Razor configuration information from projects.</Description>
|
||||
<EnableApiCheck>false</EnableApiCheck>
|
||||
<IsShippingPackage>false</IsShippingPackage>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp\Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="OmniSharp.MSBuild" Version="$(OmniSharpMSBuildPackageVersion)" />
|
||||
<PackageReference Include="Microsoft.Build" Version="$(MicrosoftBuildPackageVersion)" ExcludeAssets="Runtime" PrivateAssets="All" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Features" Version="$(MicrosoftCodeAnalysisCSharpFeaturesPackageVersion)" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.ExternalAccess.OmniSharp.CSharp" Version="$(MicrosoftCodeAnalysisExternalAccessOmniSharpCSharpPackageVersion)" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -1,33 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT license. See License.txt in the project root for license information.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System.Collections.Immutable;
|
||||
using System.Composition;
|
||||
using System.Reflection;
|
||||
using OmniSharp.Services;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.OmniSharpPlugin;
|
||||
|
||||
// This service provider is here to enable the OmniSharp process to indirectly utilize internal types that are exposed via the strong named
|
||||
// Razor assemblies by re-exporting them via the Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp assembly. For example, we re-export the
|
||||
// DefaultTagHelperResolver's factory in the strong named assembly because it's internal to Razor and can only be accessed in a strong named
|
||||
// assembly.
|
||||
//
|
||||
// We're also unable to directly load and discover Roslyn exports in the Microsoft.CodeAnalysis.Razor.Workspaces.dll due to mismatches in
|
||||
// MSBuild metadata dependencies. The expectations of the MSBuild that is loaded with OmniSharp doesn't understand the version that Razor
|
||||
// is compiled against. If we could there'd be no need for this class.
|
||||
|
||||
[Shared]
|
||||
[Export(typeof(IHostServicesProvider))]
|
||||
public class OmniSharpPluginStrongNamedRoslynServiceProvider : IHostServicesProvider
|
||||
{
|
||||
public OmniSharpPluginStrongNamedRoslynServiceProvider()
|
||||
{
|
||||
var strongNamedAssembly = Assembly.Load("Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp");
|
||||
Assemblies = ImmutableArray.Create(strongNamedAssembly);
|
||||
}
|
||||
|
||||
public ImmutableArray<Assembly> Assemblies { get; }
|
||||
}
|
|
@ -1,93 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT license. See License.txt in the project root for license information.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Composition;
|
||||
using Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Document;
|
||||
using Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project;
|
||||
using Microsoft.AspNetCore.Razor.LanguageServer.Common;
|
||||
using Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp;
|
||||
using OmniSharp;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.OmniSharpPlugin;
|
||||
|
||||
// We need to re-export MEF based services from the OmniSharp plugin strong named assembly in order
|
||||
// to make those services available via MEF. This isn't an issue for Roslyn based services because
|
||||
// we're able to hook into OmniSharp's Roslyn service aggregator to allow it to inspect the strong
|
||||
// named plugin assembly.
|
||||
|
||||
[Shared]
|
||||
[Export(typeof(OmniSharpProjectSnapshotManagerDispatcher))]
|
||||
internal class ExportOmniSharpProjectSnapshotManagerDispatcher : OmniSharpProjectSnapshotManagerDispatcher
|
||||
{
|
||||
}
|
||||
|
||||
[Shared]
|
||||
[Export(typeof(RemoteTextLoaderFactory))]
|
||||
internal class ExportRemoteTextLoaderFactory : DefaultRemoteTextLoaderFactory
|
||||
{
|
||||
}
|
||||
|
||||
[Shared]
|
||||
[Export(typeof(OmniSharpProjectSnapshotManagerAccessor))]
|
||||
internal class ExportDefaultOmniSharpProjectSnapshotManagerAccessor : OmniSharpProjectSnapshotManagerAccessor
|
||||
{
|
||||
[ImportingConstructor]
|
||||
public ExportDefaultOmniSharpProjectSnapshotManagerAccessor(
|
||||
RemoteTextLoaderFactory remoteTextLoaderFactory,
|
||||
[ImportMany] IEnumerable<IOmniSharpProjectSnapshotManagerChangeTrigger> projectChangeTriggers,
|
||||
OmniSharpProjectSnapshotManagerDispatcher projectSnapshotManagerDispatcher,
|
||||
OmniSharpWorkspace workspace) : base(remoteTextLoaderFactory, projectChangeTriggers, projectSnapshotManagerDispatcher, workspace)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
[Shared]
|
||||
[Export(typeof(IOmniSharpProjectSnapshotManagerChangeTrigger))]
|
||||
internal class ExportOmniSharpWorkspaceProjectStateChangeDetector : OmniSharpWorkspaceProjectStateChangeDetector
|
||||
{
|
||||
[ImportingConstructor]
|
||||
public ExportOmniSharpWorkspaceProjectStateChangeDetector(
|
||||
OmniSharpProjectSnapshotManagerDispatcher projectSnapshotManagerDispatcher,
|
||||
OmniSharpProjectWorkspaceStateGenerator workspaceStateGenerator,
|
||||
OmniSharpLanguageServerFeatureOptions languageServerFeatureOptions)
|
||||
: base(projectSnapshotManagerDispatcher, workspaceStateGenerator, languageServerFeatureOptions)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
[Shared]
|
||||
[Export(typeof(IOmniSharpProjectSnapshotManagerChangeTrigger))]
|
||||
[Export(typeof(OmniSharpProjectWorkspaceStateGenerator))]
|
||||
internal class ExportOmniSharpProjectWorkspaceStateGenerator : OmniSharpProjectWorkspaceStateGenerator
|
||||
{
|
||||
[ImportingConstructor]
|
||||
public ExportOmniSharpProjectWorkspaceStateGenerator(OmniSharpProjectSnapshotManagerDispatcher projectSnapshotManagerDispatcher) : base(projectSnapshotManagerDispatcher)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
[Shared]
|
||||
[Export(typeof(IOmniSharpProjectSnapshotManagerChangeTrigger))]
|
||||
internal class ExportOmniSharpBackgroundDocumentGenerator : OmniSharpBackgroundDocumentGenerator
|
||||
{
|
||||
[ImportingConstructor]
|
||||
public ExportOmniSharpBackgroundDocumentGenerator(
|
||||
OmniSharpProjectSnapshotManagerDispatcher projectSnapshotManagerDispatcher,
|
||||
RemoteTextLoaderFactory remoteTextLoaderFactory,
|
||||
[ImportMany] IEnumerable<OmniSharpDocumentProcessedListener> documentProcessedListeners) : base(projectSnapshotManagerDispatcher, remoteTextLoaderFactory, documentProcessedListeners)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
[Shared]
|
||||
[Export(typeof(OmniSharpLanguageServerFeatureOptions))]
|
||||
internal class ExportOmniSharpLanguageServerFeatureOptions : OmniSharpLanguageServerFeatureOptions
|
||||
{
|
||||
[ImportingConstructor]
|
||||
public ExportOmniSharpLanguageServerFeatureOptions() : base()
|
||||
{
|
||||
}
|
||||
}
|
|
@ -1,82 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT license. See License.txt in the project root for license information.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Composition;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using OmniSharp;
|
||||
using OmniSharp.MSBuild.Notification;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.OmniSharpPlugin;
|
||||
|
||||
// This entire class is a temporary work around for https://github.com/OmniSharp/omnisharp-roslyn/issues/1443.
|
||||
// We hack together a heuristic to detect when Razor documents that shouldn't be added to the workspace are and to then
|
||||
// remove them from the workspace. In the primary case we're watching for pre-compiled Razor files that are generated
|
||||
// from calling dotnet build and removing them from the workspace once they're added.
|
||||
|
||||
[Shared]
|
||||
[Export(typeof(IMSBuildEventSink))]
|
||||
public class PrecompiledRazorPageSuppressor : IMSBuildEventSink
|
||||
{
|
||||
private readonly OmniSharpWorkspace _workspace;
|
||||
|
||||
[ImportingConstructor]
|
||||
public PrecompiledRazorPageSuppressor(OmniSharpWorkspace workspace)
|
||||
{
|
||||
if (workspace is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(workspace));
|
||||
}
|
||||
|
||||
_workspace = workspace;
|
||||
|
||||
_workspace.WorkspaceChanged += Workspace_WorkspaceChanged;
|
||||
}
|
||||
|
||||
// Internal for testing
|
||||
internal void Workspace_WorkspaceChanged(object sender, WorkspaceChangeEventArgs args)
|
||||
{
|
||||
switch (args.Kind)
|
||||
{
|
||||
case WorkspaceChangeKind.DocumentAdded:
|
||||
case WorkspaceChangeKind.DocumentChanged:
|
||||
var project = args.NewSolution.GetProject(args.ProjectId);
|
||||
var document = project.GetDocument(args.DocumentId);
|
||||
|
||||
if (document.FilePath is null)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (document.FilePath.EndsWith(".RazorTargetAssemblyInfo.cs", StringComparison.Ordinal) ||
|
||||
document.FilePath.EndsWith(".RazorAssemblyInfo.cs", StringComparison.Ordinal))
|
||||
{
|
||||
// Razor assembly info. This doesn't catch cases when users have customized their assembly info but captures all of the
|
||||
// default cases for now. Once the omnisharp-roslyn bug has been fixed this entire class can go awy so we're hacking for now.
|
||||
_workspace.RemoveDocument(document.Id);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!document.FilePath.EndsWith(".cshtml.g.cs", StringComparison.Ordinal) &&
|
||||
!document.FilePath.EndsWith(".razor.g.cs", StringComparison.Ordinal) &&
|
||||
|
||||
// 2.2 only extension for generated Razor files
|
||||
!document.FilePath.EndsWith(".g.cshtml.cs", StringComparison.Ordinal))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// Razor output file
|
||||
_workspace.RemoveDocument(document.Id);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void ProjectLoaded(ProjectLoadedEventArgs e)
|
||||
{
|
||||
// We don't do anything on project load we're just using the IMSBuildEventSink to ensure we're instantiated.
|
||||
}
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT license. See License.txt in the project root for license information.
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.OmniSharpPlugin;
|
||||
|
||||
internal abstract class ProjectChangePublisher
|
||||
{
|
||||
public abstract void SetPublishFilePath(string projectFilePath, string publishFilePath);
|
||||
}
|
|
@ -1,37 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT license. See License.txt in the project root for license information.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Document;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.OmniSharpPlugin;
|
||||
|
||||
public sealed class ProjectConfiguration
|
||||
{
|
||||
internal ProjectConfiguration(RazorConfiguration configuration, IReadOnlyList<OmniSharpHostDocument> documents, string rootNamespace)
|
||||
{
|
||||
if (configuration is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(configuration));
|
||||
}
|
||||
|
||||
if (documents is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(documents));
|
||||
}
|
||||
|
||||
Configuration = configuration;
|
||||
Documents = documents;
|
||||
RootNamespace = rootNamespace;
|
||||
}
|
||||
|
||||
public RazorConfiguration Configuration { get; }
|
||||
|
||||
internal IReadOnlyList<OmniSharpHostDocument> Documents { get; }
|
||||
|
||||
public string RootNamespace { get; }
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT license. See License.txt in the project root for license information.
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.OmniSharpPlugin;
|
||||
|
||||
public abstract class ProjectConfigurationProvider
|
||||
{
|
||||
public abstract bool TryResolveConfiguration(ProjectConfigurationProviderContext context, out ProjectConfiguration configuration);
|
||||
}
|
|
@ -1,35 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT license. See License.txt in the project root for license information.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.Build.Execution;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.OmniSharpPlugin;
|
||||
|
||||
public sealed class ProjectConfigurationProviderContext
|
||||
{
|
||||
public ProjectConfigurationProviderContext(
|
||||
IReadOnlyList<string> projectCapabilities,
|
||||
ProjectInstance projectInstance)
|
||||
{
|
||||
if (projectCapabilities is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(projectCapabilities));
|
||||
}
|
||||
|
||||
if (projectInstance is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(projectInstance));
|
||||
}
|
||||
|
||||
ProjectCapabilities = projectCapabilities;
|
||||
ProjectInstance = projectInstance;
|
||||
}
|
||||
|
||||
public IReadOnlyList<string> ProjectCapabilities { get; }
|
||||
|
||||
public ProjectInstance ProjectInstance { get; }
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT license. See License.txt in the project root for license information.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using Microsoft.Build.Execution;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.OmniSharpPlugin;
|
||||
|
||||
public abstract class ProjectInstanceEvaluator
|
||||
{
|
||||
public abstract ProjectInstance Evaluate(ProjectInstance projectInstance);
|
||||
}
|
|
@ -1,33 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT license. See License.txt in the project root for license information.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System.Composition;
|
||||
using Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project;
|
||||
using OmniSharp.MSBuild.Notification;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.OmniSharpPlugin;
|
||||
|
||||
[Shared]
|
||||
[Export(typeof(IMSBuildEventSink))]
|
||||
internal class ProjectSnapshotManagerInstantiator : IMSBuildEventSink
|
||||
{
|
||||
// The entire purpose of this class is to ensure the project manager is instantiated.
|
||||
// Without this class all exporters of AbstractOmniSharpProjectSnapshotManagerChangeTrigger
|
||||
// would never be called (the class wouldn't have been created). So instead we rely
|
||||
// on OmniSharp to instantiate the snapshot manager and therefore configure the
|
||||
// dependent change triggers.
|
||||
|
||||
private readonly OmniSharpProjectSnapshotManager _projectManager;
|
||||
|
||||
[ImportingConstructor]
|
||||
public ProjectSnapshotManagerInstantiator(OmniSharpProjectSnapshotManagerAccessor accessor)
|
||||
{
|
||||
_projectManager = accessor.Instance;
|
||||
}
|
||||
|
||||
public void ProjectLoaded(ProjectLoadedEventArgs _)
|
||||
{
|
||||
}
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT license. See License.txt in the project root for license information.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")]
|
||||
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Razor.OmniSharpPlugin.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
|
@ -1,39 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT license. See License.txt in the project root for license information.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using Microsoft.AspNetCore.Razor.LanguageServer.Common;
|
||||
using Microsoft.Build.Execution;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.OmniSharpPlugin;
|
||||
|
||||
internal class RazorFileChangeEventArgs : EventArgs
|
||||
{
|
||||
public RazorFileChangeEventArgs(
|
||||
string filePath,
|
||||
ProjectInstance projectInstance,
|
||||
RazorFileChangeKind kind)
|
||||
{
|
||||
if (filePath is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(filePath));
|
||||
}
|
||||
|
||||
if (projectInstance is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(projectInstance));
|
||||
}
|
||||
|
||||
FilePath = filePath;
|
||||
UnevaluatedProjectInstance = projectInstance;
|
||||
Kind = kind;
|
||||
}
|
||||
|
||||
public string FilePath { get; }
|
||||
|
||||
public ProjectInstance UnevaluatedProjectInstance { get; }
|
||||
|
||||
public RazorFileChangeKind Kind { get; }
|
||||
}
|
|
@ -1,47 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT license. See License.txt in the project root for license information.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Composition;
|
||||
using Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Document;
|
||||
using Microsoft.AspNetCore.Razor.ProjectEngineHost;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.OmniSharpPlugin;
|
||||
|
||||
[Shared]
|
||||
[Export(typeof(ProjectConfigurationProvider))]
|
||||
internal class SystemWebConfigurationProvider : CoreProjectConfigurationProvider
|
||||
{
|
||||
// Internal for testing
|
||||
internal const string ReferencePathWithRefAssembliesItemType = "ReferencePathWithRefAssemblies";
|
||||
internal const string SystemWebRazorAssemblyFileName = "System.Web.Razor.dll";
|
||||
|
||||
public override bool TryResolveConfiguration(ProjectConfigurationProviderContext context, out ProjectConfiguration configuration)
|
||||
{
|
||||
if (HasRazorCoreCapability(context))
|
||||
{
|
||||
configuration = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
var compilationReferences = context.ProjectInstance.GetItems(ReferencePathWithRefAssembliesItemType);
|
||||
foreach (var compilationReference in compilationReferences)
|
||||
{
|
||||
var assemblyFullPath = compilationReference.EvaluatedInclude;
|
||||
if (assemblyFullPath.EndsWith(SystemWebRazorAssemblyFileName, FilePathComparison.Instance))
|
||||
{
|
||||
var potentialPathSeparator = assemblyFullPath[assemblyFullPath.Length - SystemWebRazorAssemblyFileName.Length - 1];
|
||||
if (potentialPathSeparator == '/' || potentialPathSeparator == '\\')
|
||||
{
|
||||
configuration = new ProjectConfiguration(UnsupportedRazorConfiguration.Instance, Array.Empty<OmniSharpHostDocument>(), rootNamespace: null);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
configuration = null;
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -1,202 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT license. See License.txt in the project root for license information.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Composition;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Razor.ExternalAccess.OmniSharp.Project;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using OmniSharp;
|
||||
using OmniSharp.MSBuild.Notification;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.OmniSharpPlugin;
|
||||
|
||||
[Shared]
|
||||
[Export(typeof(IMSBuildEventSink))]
|
||||
[Export(typeof(IRazorDocumentChangeListener))]
|
||||
[Export(typeof(IRazorDocumentOutputChangeListener))]
|
||||
[Export(typeof(IOmniSharpProjectSnapshotManagerChangeTrigger))]
|
||||
internal class TagHelperRefreshTrigger : IOmniSharpProjectSnapshotManagerChangeTrigger, IMSBuildEventSink, IRazorDocumentOutputChangeListener, IRazorDocumentChangeListener
|
||||
{
|
||||
private readonly OmniSharpProjectSnapshotManagerDispatcher _projectSnapshotManagerDispatcher;
|
||||
private readonly Workspace _omniSharpWorkspace;
|
||||
private readonly OmniSharpProjectWorkspaceStateGenerator _workspaceStateGenerator;
|
||||
private readonly Dictionary<string, Task> _deferredUpdates;
|
||||
private OmniSharpProjectSnapshotManager _projectManager;
|
||||
|
||||
[ImportingConstructor]
|
||||
public TagHelperRefreshTrigger(
|
||||
OmniSharpProjectSnapshotManagerDispatcher projectSnapshotManagerDispatcher,
|
||||
OmniSharpWorkspace omniSharpWorkspace,
|
||||
OmniSharpProjectWorkspaceStateGenerator workspaceStateGenerator)
|
||||
: this(projectSnapshotManagerDispatcher, (Workspace)omniSharpWorkspace, workspaceStateGenerator)
|
||||
{
|
||||
}
|
||||
|
||||
// Internal for testing
|
||||
internal TagHelperRefreshTrigger(
|
||||
OmniSharpProjectSnapshotManagerDispatcher projectSnapshotManagerDispatcher,
|
||||
Workspace omniSharpWorkspace,
|
||||
OmniSharpProjectWorkspaceStateGenerator workspaceStateGenerator)
|
||||
{
|
||||
if (projectSnapshotManagerDispatcher is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(projectSnapshotManagerDispatcher));
|
||||
}
|
||||
|
||||
if (omniSharpWorkspace is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(omniSharpWorkspace));
|
||||
}
|
||||
|
||||
if (workspaceStateGenerator is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(workspaceStateGenerator));
|
||||
}
|
||||
|
||||
_projectSnapshotManagerDispatcher = projectSnapshotManagerDispatcher;
|
||||
_omniSharpWorkspace = omniSharpWorkspace;
|
||||
_workspaceStateGenerator = workspaceStateGenerator;
|
||||
_deferredUpdates = new Dictionary<string, Task>();
|
||||
}
|
||||
|
||||
public int EnqueueDelay { get; set; } = 3 * 1000;
|
||||
|
||||
public void Initialize(OmniSharpProjectSnapshotManager projectManager)
|
||||
{
|
||||
if (projectManager is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(projectManager));
|
||||
}
|
||||
|
||||
_projectManager = projectManager;
|
||||
}
|
||||
|
||||
public void ProjectLoaded(ProjectLoadedEventArgs args)
|
||||
{
|
||||
if (args is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(args));
|
||||
}
|
||||
|
||||
// Project file was modified or impacted in a significant way.
|
||||
|
||||
_ = _projectSnapshotManagerDispatcher.RunOnDispatcherThreadAsync(
|
||||
() => EnqueueUpdate(args.ProjectInstance.ProjectFileLocation.File),
|
||||
CancellationToken.None).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public void RazorDocumentChanged(RazorFileChangeEventArgs args)
|
||||
{
|
||||
if (args is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(args));
|
||||
}
|
||||
|
||||
// Razor document changed
|
||||
|
||||
_ = Task.Factory.StartNew(
|
||||
() =>
|
||||
{
|
||||
if (IsComponentFile(args.FilePath, args.UnevaluatedProjectInstance.ProjectFileLocation.File))
|
||||
{
|
||||
// Razor component file changed.
|
||||
|
||||
EnqueueUpdate(args.UnevaluatedProjectInstance.ProjectFileLocation.File);
|
||||
}
|
||||
},
|
||||
CancellationToken.None,
|
||||
TaskCreationOptions.None,
|
||||
_projectSnapshotManagerDispatcher.DispatcherScheduler).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public void RazorDocumentOutputChanged(RazorFileChangeEventArgs args)
|
||||
{
|
||||
if (args is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(args));
|
||||
}
|
||||
|
||||
// Razor build occurred
|
||||
|
||||
_ = Task.Factory.StartNew(
|
||||
() => EnqueueUpdate(args.UnevaluatedProjectInstance.ProjectFileLocation.File),
|
||||
CancellationToken.None,
|
||||
TaskCreationOptions.None,
|
||||
_projectSnapshotManagerDispatcher.DispatcherScheduler).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
// Internal for testing
|
||||
internal async Task UpdateAfterDelayAsync(string projectFilePath)
|
||||
{
|
||||
if (string.IsNullOrEmpty(projectFilePath))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await Task.Delay(EnqueueDelay);
|
||||
|
||||
var solution = _omniSharpWorkspace.CurrentSolution;
|
||||
var workspaceProject = solution.Projects.FirstOrDefault(project => FilePathComparer.Instance.Equals(project.FilePath, projectFilePath));
|
||||
if (workspaceProject != null && TryGetProjectSnapshot(OmniSharpProjectKey.From(workspaceProject), out var projectSnapshot))
|
||||
{
|
||||
_workspaceStateGenerator.Update(workspaceProject, projectSnapshot);
|
||||
}
|
||||
}
|
||||
|
||||
private void EnqueueUpdate(string projectFilePath)
|
||||
{
|
||||
_projectSnapshotManagerDispatcher.AssertDispatcherThread();
|
||||
|
||||
// A race is not possible here because we use the main thread to synchronize the updates
|
||||
// by capturing the sync context.
|
||||
if (!_deferredUpdates.TryGetValue(projectFilePath, out var update) || update.IsCompleted)
|
||||
{
|
||||
_deferredUpdates[projectFilePath] = UpdateAfterDelayAsync(projectFilePath);
|
||||
}
|
||||
}
|
||||
|
||||
private bool TryGetProjectSnapshot(OmniSharpProjectKey projectKey, out OmniSharpProjectSnapshot projectSnapshot)
|
||||
{
|
||||
if (projectKey is null)
|
||||
{
|
||||
projectSnapshot = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
projectSnapshot = _projectManager.GetLoadedProject(projectKey);
|
||||
return projectSnapshot != null;
|
||||
}
|
||||
|
||||
// Internal for testing
|
||||
internal bool IsComponentFile(string relativeDocumentFilePath, string projectFilePath)
|
||||
{
|
||||
_projectSnapshotManagerDispatcher.AssertDispatcherThread();
|
||||
|
||||
foreach (var key in _projectManager.GetAllProjectKeys(projectFilePath))
|
||||
{
|
||||
var projectSnapshot = _projectManager.GetLoadedProject(key);
|
||||
if (projectSnapshot is null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var documentSnapshot = projectSnapshot.GetDocument(relativeDocumentFilePath);
|
||||
if (documentSnapshot is null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var isComponentKind = FileKinds.IsComponent(documentSnapshot.FileKind);
|
||||
return isComponentKind;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -7,19 +7,6 @@
|
|||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference
|
||||
Include="..\Microsoft.AspNetCore.Razor.VSCode\Microsoft.AspNetCore.Razor.VSCode.npmproj"
|
||||
ReferenceOutputAssemblies="false"
|
||||
SkipGetTargetFrameworkProperties="true"
|
||||
UndefineProperties="TargetFramework"
|
||||
Private="false" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<BuildOutputFiles Include="dist\extension.js" />
|
||||
</ItemGroup>
|
||||
|
||||
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), Directory.Build.targets))\Directory.Build.targets" />
|
||||
|
||||
</Project>
|
||||
|
|
|
@ -1,19 +0,0 @@
|
|||
# Info
|
||||
|
||||
In practice the Razor VSCode extension operates as a library that [OmniSharp](https://github.com/omnisharp/omnisharp-vscode) (O#) bootstraps and includes in its operation flow. Therefore, in order to provide a dev experience for testing out Razor VSCode extension changes this project attempts to replicate what O# does to bootstrap Razor's VSCode extension library.
|
||||
|
||||
## Debugging
|
||||
|
||||
1. npm install -g typescript
|
||||
1. npm install -g yarn
|
||||
1. .\build.cmd
|
||||
|
||||
### Debugging with Omnisharp-vscode
|
||||
|
||||
If you need to make changes to both the Razor-vscode extension and the Omnisharp-vscode extension you might find it useful to debug through both of them at the same time.
|
||||
|
||||
1. Do all the steps for debugging this repo
|
||||
1. Clone <https://github.com/OmniSharp/omnisharp-vscode>
|
||||
1. In that repo do all the steps to debug except pressing F5.
|
||||
1. Edit <https://github.com/OmniSharp/omnisharp-vscode/blob/master/.vscode/launch.json#L10> to include a second `--extensionDevelopmentPath` which points to our workspace.
|
||||
1. F5 on their repo.
|
|
@ -1,24 +0,0 @@
|
|||
{
|
||||
"comments": {
|
||||
"blockComment": [ "@*", "*@" ]
|
||||
},
|
||||
"brackets": [
|
||||
["<!--", "-->"],
|
||||
["<", ">"],
|
||||
["{", "}"],
|
||||
["(", ")"]
|
||||
],
|
||||
"autoClosingPairs": [
|
||||
{ "open": "{", "close": "}"},
|
||||
{ "open": "[", "close": "]"},
|
||||
{ "open": "(", "close": ")" },
|
||||
{ "open": "'", "close": "'" },
|
||||
{ "open": "\"", "close": "\"" },
|
||||
{ "open": "@*", "close": "*@" }
|
||||
],
|
||||
"surroundingPairs": [
|
||||
{ "open": "'", "close": "'" },
|
||||
{ "open": "\"", "close": "\"" },
|
||||
{ "open": "<", "close": ">" }
|
||||
]
|
||||
}
|
|
@ -1,525 +1,10 @@
|
|||
{
|
||||
"name": "razor-vscode",
|
||||
"private": true,
|
||||
"displayName": "Razor",
|
||||
"description": "Razor VS Code extension bootstrapper. This extension attempts to replicate what O# does to bootstrap Razor's VSCode extension library. ",
|
||||
"version": "0.0.1",
|
||||
"defaults": {
|
||||
"razor": "0.0.1"
|
||||
},
|
||||
"publisher": "ms-dotnettools",
|
||||
"engines": {
|
||||
"vscode": "^1.69.0"
|
||||
},
|
||||
"categories": [
|
||||
"Other"
|
||||
],
|
||||
"activationEvents": [
|
||||
"onWebviewPanel:razorReportIssue",
|
||||
"onDebugInitialConfigurations",
|
||||
"onDebugResolve:blazorwasm",
|
||||
"onDebugResolve:coreclr",
|
||||
"onDebugResolve:clr",
|
||||
"onLanguage:csharp",
|
||||
"onLanguage:aspnetcorerazor",
|
||||
"onCommand:o.restart",
|
||||
"onCommand:o.pickProjectAndStart",
|
||||
"onCommand:o.showOutput",
|
||||
"onCommand:dotnet.restore.project",
|
||||
"onCommand:dotnet.restore.all",
|
||||
"onCommand:dotnet.generateAssets",
|
||||
"onCommand:csharp.downloadDebugger",
|
||||
"onCommand:csharp.listProcess",
|
||||
"onCommand:csharp.listRemoteProcess",
|
||||
"onCommand:extension.configureRazorDevMode",
|
||||
"onCommand:extension.resetRazorDevModeConfiguration",
|
||||
"onCommand:extension.razorActivated",
|
||||
"workspaceContains:project.json",
|
||||
"workspaceContains:*.csproj",
|
||||
"workspaceContains:*.sln",
|
||||
"workspaceContains:*.csx",
|
||||
"workspaceContains:*.cake",
|
||||
"workspaceContains:**/*.csproj",
|
||||
"workspaceContains:**/*.sln",
|
||||
"workspaceContains:**/*.csx",
|
||||
"workspaceContains:**/*.cake"
|
||||
],
|
||||
"main": "../Microsoft.AspNetCore.Razor.VSCode.Extension/dist/extension.js",
|
||||
"contributes": {
|
||||
"breakpoints": [
|
||||
{
|
||||
"language": "aspnetcorerazor"
|
||||
},
|
||||
{
|
||||
"language": "csharp"
|
||||
}
|
||||
],
|
||||
"debuggers": [
|
||||
{
|
||||
"type": "blazorwasm",
|
||||
"label": "Blazor WebAssembly Debug",
|
||||
"initialConfigurations": [
|
||||
{
|
||||
"type": "blazorwasm",
|
||||
"name": "Launch and Debug Blazor WebAssembly Application",
|
||||
"request": "launch"
|
||||
}
|
||||
],
|
||||
"configurationAttributes": {
|
||||
"launch": {
|
||||
"properties": {
|
||||
"cwd": {
|
||||
"type": "string",
|
||||
"description": "The directory of the Blazor WebAssembly app, defaults to the workspace folder.",
|
||||
"default": "${workspaceFolder}"
|
||||
},
|
||||
"url": {
|
||||
"type": "string",
|
||||
"description": "The URL of the application",
|
||||
"default": "https://localhost:5001"
|
||||
},
|
||||
"browser": {
|
||||
"type": "string",
|
||||
"description": "The debugging browser to launch (Edge or Chrome)",
|
||||
"default": "edge",
|
||||
"enum": [
|
||||
"chrome",
|
||||
"edge"
|
||||
]
|
||||
},
|
||||
"trace": {
|
||||
"type": [
|
||||
"boolean",
|
||||
"string"
|
||||
],
|
||||
"default": "true",
|
||||
"enum": [
|
||||
"verbose",
|
||||
true
|
||||
],
|
||||
"description": "If true, verbose logs from JS debugger are sent to log file. If 'verbose', send logs to console."
|
||||
},
|
||||
"hosted": {
|
||||
"type": "boolean",
|
||||
"default": "false",
|
||||
"description": "True if the app is a hosted Blazor WebAssembly app, false otherwise."
|
||||
},
|
||||
"webRoot": {
|
||||
"type": "string",
|
||||
"default": "${workspaceFolder}",
|
||||
"description": "Specifies the absolute path to the webserver root."
|
||||
},
|
||||
"timeout": {
|
||||
"type": "number",
|
||||
"default": 30000,
|
||||
"description": "Retry for this number of milliseconds to connect to browser."
|
||||
},
|
||||
"program": {
|
||||
"type": "string",
|
||||
"default": "${workspaceFolder}/Server/bin/Debug/<target-framework>/<target-dll>",
|
||||
"description": "The path of the DLL to execute when launching a hosted server app"
|
||||
},
|
||||
"env": {
|
||||
"type": "object",
|
||||
"description": "Environment variables passed to dotnet. Only valid for hosted apps."
|
||||
},
|
||||
"dotNetConfig": {
|
||||
"description": "Options passed to the underlying .NET debugger. For more info, see https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger.md.",
|
||||
"type": "object",
|
||||
"required": [],
|
||||
"default": {},
|
||||
"properties": {
|
||||
"justMyCode": {
|
||||
"type": "boolean",
|
||||
"description": "Optional flag to only show user code.",
|
||||
"default": true
|
||||
},
|
||||
"sourceFileMap": {
|
||||
"type": "object",
|
||||
"description": "Optional source file mappings passed to the debug engine. Example: '{ \"C:\\foo\":\"/home/user/foo\" }'",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
},
|
||||
"default": {
|
||||
"<insert-source-path-here>": "<insert-target-path-here>"
|
||||
}
|
||||
},
|
||||
"logging": {
|
||||
"description": "Optional flags to determine what types of messages should be logged to the output window.",
|
||||
"type": "object",
|
||||
"required": [],
|
||||
"default": {},
|
||||
"properties": {
|
||||
"exceptions": {
|
||||
"type": "boolean",
|
||||
"description": "Optional flag to determine whether exception messages should be logged to the output window.",
|
||||
"default": true
|
||||
},
|
||||
"moduleLoad": {
|
||||
"type": "boolean",
|
||||
"description": "Optional flag to determine whether module load events should be logged to the output window.",
|
||||
"default": true
|
||||
},
|
||||
"programOutput": {
|
||||
"type": "boolean",
|
||||
"description": "Optional flag to determine whether program output should be logged to the output window when not using an external console.",
|
||||
"default": true
|
||||
},
|
||||
"engineLogging": {
|
||||
"type": "boolean",
|
||||
"description": "Optional flag to determine whether diagnostic engine logs should be logged to the output window.",
|
||||
"default": false
|
||||
},
|
||||
"browserStdOut": {
|
||||
"type": "boolean",
|
||||
"description": "Optional flag to determine if stdout text from the launching the web browser should be logged to the output window.",
|
||||
"default": true
|
||||
},
|
||||
"elapsedTiming": {
|
||||
"type": "boolean",
|
||||
"description": "If true, engine logging will include `adapterElapsedTime` and `engineElapsedTime` properties to indicate the amount of time, in microseconds, that a request took.",
|
||||
"default": false
|
||||
},
|
||||
"threadExit": {
|
||||
"type": "boolean",
|
||||
"description": "Controls if a message is logged when a thread in the target process exits. Default: `false`.",
|
||||
"default": false
|
||||
},
|
||||
"processExit": {
|
||||
"type": "boolean",
|
||||
"description": "Controls if a message is logged when the target process exits, or debugging is stopped. Default: `true`.",
|
||||
"default": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"browserConfig": {
|
||||
"description": "Options based to the underlying JavaScript debugger. For more info, see https://github.com/microsoft/vscode-js-debug/blob/main/OPTIONS.md.",
|
||||
"type": "object",
|
||||
"required": [],
|
||||
"default": {},
|
||||
"properties": {
|
||||
"outputCapture": {
|
||||
"enum": [
|
||||
"console",
|
||||
"std"
|
||||
],
|
||||
"description": "From where to capture output messages: the default debug API if set to `console`, or stdout/stderr streams if set to `std`.",
|
||||
"default": "console"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"attach": {
|
||||
"properties": {
|
||||
"url": {
|
||||
"type": "string",
|
||||
"description": "The URL of the application",
|
||||
"default": "https://localhost:5001"
|
||||
},
|
||||
"cwd": {
|
||||
"type": "string",
|
||||
"description": "The directory of the Blazor WebAssembly app, defaults to the workspace folder.",
|
||||
"default": "${workspaceFolder}"
|
||||
},
|
||||
"browser": {
|
||||
"type": "string",
|
||||
"description": "The debugging browser to launch (Edge or Chrome)",
|
||||
"default": "chrome",
|
||||
"enum": [
|
||||
"chrome",
|
||||
"edge"
|
||||
]
|
||||
},
|
||||
"trace": {
|
||||
"type": [
|
||||
"boolean",
|
||||
"string"
|
||||
],
|
||||
"default": "true",
|
||||
"enum": [
|
||||
"verbose",
|
||||
true
|
||||
],
|
||||
"description": "If true, verbose logs from JS debugger are sent to log file. If 'verbose', send logs to console."
|
||||
},
|
||||
"webRoot": {
|
||||
"type": "string",
|
||||
"default": "${workspaceFolder}",
|
||||
"description": "Specifies the absolute path to the webserver root."
|
||||
},
|
||||
"timeout": {
|
||||
"type": "number",
|
||||
"default": 30000,
|
||||
"description": "Retry for this number of milliseconds to connect to browser."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"semanticTokenTypes": [
|
||||
{
|
||||
"id": "razorComponentElement",
|
||||
"description": "A Razor component element"
|
||||
},
|
||||
{
|
||||
"id": "razorComponentAttribute",
|
||||
"description": "A Razor component attribute"
|
||||
},
|
||||
{
|
||||
"id": "razorTagHelperElement",
|
||||
"description": "A Razor TagHelper Element"
|
||||
},
|
||||
{
|
||||
"id": "razorTagHelperAttribute",
|
||||
"description": "A Razor TagHelper Attribute"
|
||||
},
|
||||
{
|
||||
"id": "razorTransition",
|
||||
"description": "A Razor transition"
|
||||
},
|
||||
{
|
||||
"id": "razorDirectiveAttribute",
|
||||
"description": "A Razor Directive Attribute"
|
||||
},
|
||||
{
|
||||
"id": "razorDirectiveColon",
|
||||
"description": "A colon between directive attribute parameters"
|
||||
},
|
||||
{
|
||||
"id": "razorDirective",
|
||||
"description": "A Razor directive such as 'code' or 'function'"
|
||||
},
|
||||
{
|
||||
"id": "razorComment",
|
||||
"description": "A Razor comment"
|
||||
},
|
||||
{
|
||||
"id": "markupCommentPunctuation",
|
||||
"description": "The '@' or '*' of a Razor comment."
|
||||
},
|
||||
{
|
||||
"id": "markupTagDelimiter",
|
||||
"description": "Markup delimiters like '<', '>', and '/'."
|
||||
},
|
||||
{
|
||||
"id": "markupOperator",
|
||||
"description": "Delimiter for Markup Attribute Key-Value pairs."
|
||||
},
|
||||
{
|
||||
"id": "markupElement",
|
||||
"description": "The name of a Markup element."
|
||||
},
|
||||
{
|
||||
"id": "markupAttribute",
|
||||
"description": "The name of a Markup attribute."
|
||||
},
|
||||
{
|
||||
"id": "markupComment",
|
||||
"description": "The contents of a Markup comment."
|
||||
},
|
||||
{
|
||||
"id": "markupCommentPunctuation",
|
||||
"description": "The begining or ending punctuation of a Markup comment."
|
||||
}
|
||||
],
|
||||
"semanticTokenScopes": [
|
||||
{
|
||||
"scopes": {
|
||||
"razorComponentElement": [
|
||||
"entity.name.class.element.component"
|
||||
],
|
||||
"razorComponentAttribute": [
|
||||
"entity.name.class.attribute.component"
|
||||
],
|
||||
"razorTagHelperElement": [
|
||||
"entity.name.class.element.taghelper"
|
||||
],
|
||||
"razorTagHelperAttribute": [
|
||||
"entity.name.class.attribute.taghelper"
|
||||
],
|
||||
"razorTransition": [
|
||||
"keyword.control.razor.transition"
|
||||
],
|
||||
"razorDirectiveAttribute": [
|
||||
"keyword.control.razor.directive.attribute",
|
||||
"keyword.control.cshtml.directive.attribute"
|
||||
],
|
||||
"razorDirectiveColon": [
|
||||
"keyword.control.razor.directive.colon",
|
||||
"keyword.control.cshtml.directive.colon"
|
||||
],
|
||||
"razorDirective": [
|
||||
"keyword.control.razor.directive",
|
||||
"keyword.control.cshtml.directive"
|
||||
],
|
||||
"razorComment": [
|
||||
"comment.block.razor"
|
||||
],
|
||||
"razorCommentTransition": [
|
||||
"meta.comment.razor",
|
||||
"keyword.control.cshtml.transition"
|
||||
],
|
||||
"razorCommentStar": [
|
||||
"keyword.control.razor.comment.star",
|
||||
"meta.comment.razor"
|
||||
],
|
||||
"angleBracket": [
|
||||
"punctuation.definition.tag"
|
||||
],
|
||||
"forwardSlash": [
|
||||
"punctuation.definition.tag"
|
||||
],
|
||||
"equals": [
|
||||
"punctuation.separator.key-value.html"
|
||||
],
|
||||
"markupElement": [
|
||||
"entity.name.tag.html"
|
||||
],
|
||||
"markupAttribute": [
|
||||
"entity.other.attribute-name.html"
|
||||
],
|
||||
"markupComment": [
|
||||
"comment.block.html"
|
||||
],
|
||||
"markupCommentPunctuation": [
|
||||
"punctuation.definition.comment.html",
|
||||
"comment.block.html"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"languages": [
|
||||
{
|
||||
"id": "aspnetcorerazor",
|
||||
"extensions": [
|
||||
".cshtml",
|
||||
".razor"
|
||||
],
|
||||
"mimetypes": [
|
||||
"text/x-cshtml"
|
||||
],
|
||||
"configuration": "./language-configuration.json"
|
||||
}
|
||||
],
|
||||
"grammars": [
|
||||
{
|
||||
"language": "aspnetcorerazor",
|
||||
"scopeName": "text.aspnetcorerazor",
|
||||
"path": "./syntaxes/aspnetcorerazor.tmLanguage.json",
|
||||
"embeddedLanguages": {
|
||||
"source.cs": "csharp",
|
||||
"text.html.basic": "html",
|
||||
"source.js": "javascript",
|
||||
"source.css": "css"
|
||||
}
|
||||
}
|
||||
],
|
||||
"commands": [
|
||||
{
|
||||
"command": "extension.showRazorCSharpWindow",
|
||||
"title": "Show Razor CSharp",
|
||||
"category": "Razor"
|
||||
},
|
||||
{
|
||||
"command": "extension.showRazorHtmlWindow",
|
||||
"title": "Show Razor Html",
|
||||
"category": "Razor"
|
||||
},
|
||||
{
|
||||
"command": "razor.reportIssue",
|
||||
"title": "Report a Razor issue",
|
||||
"category": "Razor"
|
||||
},
|
||||
{
|
||||
"command": "extension.configureRazorDevMode",
|
||||
"title": "Configure workspace for Razor extension development",
|
||||
"category": "Razor"
|
||||
},
|
||||
{
|
||||
"command": "extension.resetRazorDevModeConfiguration",
|
||||
"title": "Reset workspace Razor dev mode configuration",
|
||||
"category": "Razor"
|
||||
},
|
||||
{
|
||||
"command": "extension.razorActivated",
|
||||
"title": "Force activation of Razor extension",
|
||||
"category": "Razor"
|
||||
}
|
||||
],
|
||||
"menus": {
|
||||
"editor/title": [
|
||||
{
|
||||
"command": "extension.showRazorCSharpWindow",
|
||||
"when": "resourceLangId == aspnetcorerazor"
|
||||
},
|
||||
{
|
||||
"command": "extension.showRazorHtmlWindow",
|
||||
"when": "resourceLangId == aspnetcorerazor"
|
||||
},
|
||||
{
|
||||
"command": "razor.reportIssue",
|
||||
"when": "resourceLangId == aspnetcorerazor"
|
||||
},
|
||||
{
|
||||
"command": "extension.configureRazorDevMode"
|
||||
},
|
||||
{
|
||||
"command": "extension.resetRazorDevModeConfiguration"
|
||||
},
|
||||
{
|
||||
"command": "extension.razorActivated"
|
||||
}
|
||||
]
|
||||
},
|
||||
"configuration": {
|
||||
"title": "Razor Configuration",
|
||||
"properties": {
|
||||
"razor.plugin.path": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"default": null,
|
||||
"description": "Overrides the path to the Razor plugin dll."
|
||||
},
|
||||
"razor.languageServer.debug": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "Specifies whether to wait for debug attach when launching the language server."
|
||||
},
|
||||
"razor.format.enable": {
|
||||
"type": "boolean",
|
||||
"scope": "window",
|
||||
"default": true,
|
||||
"description": "Enable/disable default Razor formatter."
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"vscode:prepublish": "yarn run compile",
|
||||
"clean": "rimraf dist",
|
||||
"build": "yarn run clean && yarn run lint && tsc -p ./ && yarn run compile:TextMate",
|
||||
"lint": "tslint --project ./",
|
||||
"watch": "yarn run clean && yarn run lint && tsc -watch -p ./",
|
||||
"build": "yarn run compile:TextMate",
|
||||
"compile:TextMate": "npx js-yaml syntaxes/aspnetcorerazor.tmLanguage.yml > syntaxes/aspnetcorerazor.tmLanguage.json"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^17.0.2",
|
||||
"@types/vscode": "1.69.0",
|
||||
"cross-env": "^5.2.0",
|
||||
"js-yaml": ">=3.13.1",
|
||||
"minimatch": "3.0.5",
|
||||
"rimraf": "2.6.3",
|
||||
"tslint": "^5.11.0",
|
||||
"typescript": "^4.5.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"diff": ">=3.5.0",
|
||||
"microsoft.aspnetcore.razor.vscode": "file:../Microsoft.AspNetCore.Razor.VSCode"
|
||||
"js-yaml": ">=3.13.1"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,62 +0,0 @@
|
|||
/* --------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
* ------------------------------------------------------------------------------------------ */
|
||||
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
export async function registerRazorDevModeHelpers(context: vscode.ExtensionContext) {
|
||||
const razorConfiguration = vscode.workspace.getConfiguration('razor');
|
||||
|
||||
const unconfigureSubscription = vscode.commands.registerCommand('extension.resetRazorDevModeConfiguration', async () => {
|
||||
await razorConfiguration.update('devmode', undefined);
|
||||
|
||||
const pluginConfiguration = vscode.workspace.getConfiguration('razor.plugin');
|
||||
await pluginConfiguration.update('path', undefined);
|
||||
|
||||
// Settings have been updated, lets reload the window.
|
||||
await vscode.commands.executeCommand('workbench.action.reloadWindow');
|
||||
});
|
||||
context.subscriptions.push(unconfigureSubscription);
|
||||
|
||||
const configureSubscription = vscode.commands.registerCommand('extension.configureRazorDevMode', async () => {
|
||||
await razorConfiguration.update('devmode', true);
|
||||
await razorConfiguration.update('trace', 'Verbose');
|
||||
|
||||
const config = process.env.config ? process.env.config : 'Debug';
|
||||
const pluginPath = path.join(
|
||||
__dirname, '..', '..', '..', '..', '..', 'artifacts', 'bin', 'Microsoft.AspNetCore.Razor.OmniSharpPlugin', config, 'net472', 'Microsoft.AspNetCore.Razor.OmniSharpPlugin.dll');
|
||||
|
||||
if (!fs.existsSync(pluginPath)) {
|
||||
vscode.window.showErrorMessage(`The Razor Language Server O# plugin has not yet been built - could not find ${pluginPath}`);
|
||||
return;
|
||||
}
|
||||
|
||||
const pluginConfiguration = vscode.workspace.getConfiguration('razor.plugin');
|
||||
await pluginConfiguration.update('path', pluginPath);
|
||||
|
||||
// Settings have been updated, lets reload the window.
|
||||
await vscode.commands.executeCommand('workbench.action.reloadWindow');
|
||||
});
|
||||
context.subscriptions.push(configureSubscription);
|
||||
}
|
||||
|
||||
export function ensureWorkspaceIsConfigured() {
|
||||
const razorConfiguration = vscode.workspace.getConfiguration('razor');
|
||||
if (!razorConfiguration.get('devmode')) {
|
||||
// Running in a workspace without devmode enabled. We should prompt the user to configure the workspace.
|
||||
vscode.window.showErrorMessage(
|
||||
'This workspace is not configured to use the local Razor extension.',
|
||||
'Configure and Reload').then(async (reloadResponse) => {
|
||||
if (reloadResponse === 'Configure and Reload') {
|
||||
await vscode.commands.executeCommand('extension.configureRazorDevMode');
|
||||
}
|
||||
});
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
|
@ -1,63 +0,0 @@
|
|||
/* --------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
* ------------------------------------------------------------------------------------------ */
|
||||
|
||||
import * as fs from 'fs';
|
||||
import * as razorExtensionPackage from 'microsoft.aspnetcore.razor.vscode';
|
||||
import * as path from 'path';
|
||||
import * as vscode from 'vscode';
|
||||
import { ensureWorkspaceIsConfigured, registerRazorDevModeHelpers } from './RazorDevModeHelpers';
|
||||
|
||||
let activationResolver: (value?: any) => void;
|
||||
export const extensionActivated = new Promise(resolve => {
|
||||
activationResolver = resolve;
|
||||
});
|
||||
|
||||
export async function activate(context: vscode.ExtensionContext) {
|
||||
// Because this extension is only used for local development and tests in CI,
|
||||
// we know the Razor Language Server is at a specific path within this repo
|
||||
const config = process.env.config ? process.env.config : 'Debug';
|
||||
|
||||
const languageServerDir = path.join(
|
||||
__dirname, '..', '..', '..', '..', '..', 'artifacts', 'bin', 'rzls', config, 'net7.0');
|
||||
|
||||
if (!fs.existsSync(languageServerDir)) {
|
||||
vscode.window.showErrorMessage(`The Razor Language Server project has not yet been built - could not find ${languageServerDir}`);
|
||||
return;
|
||||
}
|
||||
|
||||
const hostEventStream = {
|
||||
post: (event: any) => {
|
||||
// 1 corresponds to the telemetry event type from OmniSharp
|
||||
if (event.type === 1) {
|
||||
console.log(`Telemetry Event: ${event.eventName}.`);
|
||||
if (event.properties) {
|
||||
const propertiesString = JSON.stringify(event.properties, null, 2);
|
||||
console.log(propertiesString);
|
||||
}
|
||||
} else {
|
||||
console.log(`Unknown event: ${event.eventName}`);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
vscode.commands.registerCommand('extension.razorActivated', () => extensionActivated);
|
||||
|
||||
await registerRazorDevModeHelpers(context);
|
||||
const workspaceConfigured = ensureWorkspaceIsConfigured();
|
||||
|
||||
if (workspaceConfigured) {
|
||||
await razorExtensionPackage.activate(
|
||||
vscode,
|
||||
context,
|
||||
languageServerDir,
|
||||
hostEventStream,
|
||||
/* enabledProposedApis */true);
|
||||
} else {
|
||||
console.warn('Razor workspace was not configured, extension activation skipped.');
|
||||
console.warn('To configure your workspace run the following command (ctrl+shift+p) in the experimental instance "Razor: Configure workspace for Razor extension development"');
|
||||
}
|
||||
|
||||
activationResolver();
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "dist"
|
||||
},
|
||||
"include": [
|
||||
"./src/**/*"
|
||||
]
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
{
|
||||
"extends": "../../tslint.json"
|
||||
}
|
|
@ -1,2 +0,0 @@
|
|||
# Auto generated file from Gardener Plugin CentralFeedServiceAdoptionPlugin
|
||||
registry=https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/
|
|
@ -1,20 +0,0 @@
|
|||
<Project DefaultTargets="Build">
|
||||
|
||||
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), Directory.Build.props))\Directory.Build.props" />
|
||||
|
||||
<PropertyGroup>
|
||||
<PackageId>microsoft.aspnetcore.razor.vscode</PackageId>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<IsPackable Condition="'$(OS)' == 'Windows_NT'">true</IsPackable>
|
||||
|
||||
<!-- We technically ship this to a blob feed and then manually integrate with O#. Don't want this package making its way anywhere else. -->
|
||||
<IsShippingPackage>false</IsShippingPackage>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<BuildOutputFiles Include="dist\extension.js" />
|
||||
</ItemGroup>
|
||||
|
||||
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), Directory.Build.targets))\Directory.Build.targets" />
|
||||
|
||||
</Project>
|
|
@ -1,35 +0,0 @@
|
|||
{
|
||||
"name": "microsoft.aspnetcore.razor.vscode",
|
||||
"private": true,
|
||||
"version": "0.0.1",
|
||||
"defaults": {
|
||||
"razor": "0.0.1"
|
||||
},
|
||||
"description": "VS Code library for Razor language support.",
|
||||
"devDependencies": {
|
||||
"@types/node": "^10.9.4",
|
||||
"@types/vscode": "1.69.0",
|
||||
"js-yaml": ">=3.13.1",
|
||||
"minimatch": "3.0.5",
|
||||
"rimraf": "2.6.3",
|
||||
"tslint": "^5.11.0",
|
||||
"typescript": "~4.5.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"ps-list": "7.2.0",
|
||||
"vscode-html-languageservice": "^5.0.1",
|
||||
"vscode-languageclient": "8.0.2",
|
||||
"vscode-languageserver-textdocument": "^1.0.5"
|
||||
},
|
||||
"main": "./dist/extension.js",
|
||||
"types": "./dist/extension.d.ts",
|
||||
"engines": {
|
||||
"vscode": "1.69.0"
|
||||
},
|
||||
"scripts": {
|
||||
"clean": "rimraf out && rimraf dist",
|
||||
"build": "yarn run clean && yarn run lint && tsc -p ./",
|
||||
"lint": "tslint ./src/**/*.ts --project ./",
|
||||
"watch": "tsc -watch -p ./"
|
||||
}
|
||||
}
|
|
@ -1,142 +0,0 @@
|
|||
/* --------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
* ------------------------------------------------------------------------------------------ */
|
||||
import { readFileSync } from 'fs';
|
||||
import { join } from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
import { RazorLogger } from '../RazorLogger';
|
||||
import { JS_DEBUG_NAME, SERVER_APP_NAME } from './Constants';
|
||||
import { onDidTerminateDebugSession } from './TerminateDebugHandler';
|
||||
|
||||
export class BlazorDebugConfigurationProvider implements vscode.DebugConfigurationProvider {
|
||||
|
||||
constructor(private readonly logger: RazorLogger, private readonly vscodeType: typeof vscode) { }
|
||||
|
||||
public async resolveDebugConfiguration(folder: vscode.WorkspaceFolder | undefined, configuration: vscode.DebugConfiguration): Promise<vscode.DebugConfiguration | undefined> {
|
||||
/**
|
||||
* The Blazor WebAssembly app should only be launched if the
|
||||
* launch configuration is a launch request. Attach requests will
|
||||
* only launch the browser.
|
||||
*/
|
||||
if (configuration.request === 'launch') {
|
||||
await this.launchApp(folder, configuration);
|
||||
}
|
||||
|
||||
let inspectUri = '{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}';
|
||||
let url = 'https://localhost:5001';
|
||||
try {
|
||||
if (folder !== undefined) {
|
||||
let folderPath = configuration.cwd ? configuration.cwd : fileURLToPath(folder.uri.toString());
|
||||
folderPath = folderPath.replace('${workspaceFolder}', fileURLToPath(folder.uri.toString()));
|
||||
const launchSettings = JSON.parse(readFileSync(join(folderPath, 'Properties', 'launchSettings.json'), 'utf8'));
|
||||
if (launchSettings?.profiles && launchSettings?.profiles[Object.keys(launchSettings.profiles)[0]]?.inspectUri) {
|
||||
inspectUri = launchSettings.profiles[Object.keys(launchSettings.profiles)[0]].inspectUri;
|
||||
url = launchSettings.profiles[Object.keys(launchSettings.profiles)[0]].applicationUrl.split(';', 1)[0];
|
||||
}
|
||||
}
|
||||
} catch (error: any) {
|
||||
this.logger.logError('[DEBUGGER] Error while getting information from launchSettings.json: ', error as Error);
|
||||
}
|
||||
|
||||
await this.launchBrowser(
|
||||
folder,
|
||||
configuration,
|
||||
inspectUri,
|
||||
url);
|
||||
|
||||
/**
|
||||
* If `resolveDebugConfiguration` returns undefined, then the debugger
|
||||
* launch is canceled. Here, we opt to manually launch the browser
|
||||
* configuration using `startDebugging` above instead of returning
|
||||
* the configuration to avoid a bug where VS Code is unable to resolve
|
||||
* the debug adapter for the browser debugger.
|
||||
*/
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private async launchApp(folder: vscode.WorkspaceFolder | undefined, configuration: vscode.DebugConfiguration) {
|
||||
const program = configuration.hosted ? configuration.program : 'dotnet';
|
||||
const cwd = configuration.cwd || '${workspaceFolder}';
|
||||
const args = configuration.hosted ? [] : ['run'];
|
||||
|
||||
const app = {
|
||||
name: SERVER_APP_NAME,
|
||||
type: 'coreclr',
|
||||
request: 'launch',
|
||||
prelaunchTask: 'build',
|
||||
program,
|
||||
args,
|
||||
cwd,
|
||||
env: {
|
||||
ASPNETCORE_ENVIRONMENT: 'Development',
|
||||
...configuration.env,
|
||||
},
|
||||
launchBrowser: {
|
||||
enabled: false,
|
||||
},
|
||||
...configuration.dotNetConfig,
|
||||
};
|
||||
|
||||
try {
|
||||
await this.vscodeType.debug.startDebugging(folder, app);
|
||||
if (process.platform !== 'win32') {
|
||||
const terminate = this.vscodeType.debug.onDidTerminateDebugSession(async event => {
|
||||
const blazorDevServer = 'blazor-devserver\\.dll';
|
||||
const dir = folder && folder.uri && folder.uri.fsPath;
|
||||
const regexEscapedDir = dir!.toLowerCase()!.replace(/\//g, '\\/');
|
||||
const launchedApp = configuration.hosted ? app.program : `${regexEscapedDir}.*${blazorDevServer}|${blazorDevServer}.*${regexEscapedDir}`;
|
||||
await onDidTerminateDebugSession(event, this.logger, launchedApp);
|
||||
terminate.dispose();
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
this.logger.logError('[DEBUGGER] Error when launching application: ', error as Error);
|
||||
}
|
||||
}
|
||||
|
||||
private async launchBrowser(folder: vscode.WorkspaceFolder | undefined, configuration: vscode.DebugConfiguration, inspectUri: string, url: string) {
|
||||
const browser = {
|
||||
name: JS_DEBUG_NAME,
|
||||
type: configuration.browser === 'edge' ? 'pwa-msedge' : 'pwa-chrome',
|
||||
request: 'launch',
|
||||
timeout: configuration.timeout || 30000,
|
||||
url: configuration.url || url,
|
||||
webRoot: configuration.webRoot || '${workspaceFolder}',
|
||||
inspectUri,
|
||||
trace: configuration.trace || false,
|
||||
noDebug: configuration.noDebug || false,
|
||||
...configuration.browserConfig,
|
||||
// When the browser debugging session is stopped, propogate
|
||||
// this and terminate the debugging session of the Blazor dev server.
|
||||
cascadeTerminateToConfigurations: [SERVER_APP_NAME],
|
||||
};
|
||||
|
||||
try {
|
||||
/**
|
||||
* The browser debugger will immediately launch after the
|
||||
* application process is started. It waits a `timeout`
|
||||
* interval before crashing after being unable to find the launched
|
||||
* process.
|
||||
*
|
||||
* We do this to provide immediate visual feedback to the user
|
||||
* that their debugger session has started.
|
||||
*/
|
||||
await this.vscodeType.debug.startDebugging(folder, browser);
|
||||
} catch (error) {
|
||||
this.logger.logError(
|
||||
'[DEBUGGER] Error when launching browser debugger: ',
|
||||
error as Error,
|
||||
);
|
||||
const message = `There was an unexpected error while launching your debugging session. Check the console for helpful logs and visit the debugging docs for more info.`;
|
||||
this.vscodeType.window.showErrorMessage(message, `View Debug Docs`, `Ignore`).then(async result => {
|
||||
if (result === 'View Debug Docs') {
|
||||
const debugDocsUri = 'https://aka.ms/blazorwasmcodedebug';
|
||||
await this.vscodeType.commands.executeCommand(`vcode.open`, debugDocsUri);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
/* --------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
* ------------------------------------------------------------------------------------------ */
|
||||
|
||||
export const SERVER_APP_NAME = '.NET Application Server';
|
||||
export const JS_DEBUG_NAME = 'Debug Blazor Web Assembly in Browser';
|
|
@ -1,86 +0,0 @@
|
|||
/* --------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
* ------------------------------------------------------------------------------------------ */
|
||||
|
||||
import * as psList from 'ps-list';
|
||||
import { DebugSession } from 'vscode';
|
||||
import { RazorLogger } from '../RazorLogger';
|
||||
import { JS_DEBUG_NAME, SERVER_APP_NAME } from './Constants';
|
||||
|
||||
export const isValidEvent = (name: string) => {
|
||||
|
||||
// The name can be of the form: `Debug Blazor Web Assembly in Browser: https://localhost:7291`
|
||||
// hence we have to examine what the name **startsWith**
|
||||
// we cannot use startsWith otherwise when we close a login window we will receive a name like this
|
||||
// `Debug Blazor Web Assembly in Browser: https://localhost:5001/authentication/login-callback#state=eyJpZCI6ImEwYjQ5MDMzL` and will kill the app
|
||||
return name === JS_DEBUG_NAME || name === SERVER_APP_NAME;
|
||||
};
|
||||
|
||||
const killProcess = (targetPid: number | undefined, logger: RazorLogger) => {
|
||||
// If no PID was provided, then exit early.
|
||||
if (!targetPid) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
logger.logVerbose(`[DEBUGGER] Terminating debugging session with PID ${targetPid}...`);
|
||||
process.kill(targetPid);
|
||||
} catch (error) {
|
||||
logger.logError(`[DEBUGGER] Error terminating debug processes with PID ${targetPid}: `, error as Error);
|
||||
}
|
||||
};
|
||||
|
||||
export async function onDidTerminateDebugSession(
|
||||
event: DebugSession,
|
||||
logger: RazorLogger,
|
||||
target: string | number | undefined,
|
||||
) {
|
||||
if (!target) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof target === 'number') {
|
||||
terminateByPid(event, logger, target);
|
||||
} else {
|
||||
await terminateByProcessName(event, logger, target);
|
||||
}
|
||||
}
|
||||
|
||||
function terminateByPid(
|
||||
event: DebugSession,
|
||||
logger: RazorLogger,
|
||||
targetPid: number | undefined,
|
||||
) {
|
||||
// Ignore debug sessions that are not applicable to us
|
||||
if (!isValidEvent(event.name)) {
|
||||
return;
|
||||
}
|
||||
|
||||
killProcess(targetPid, logger);
|
||||
}
|
||||
|
||||
async function terminateByProcessName(
|
||||
event: DebugSession,
|
||||
logger: RazorLogger,
|
||||
targetProcess: string,
|
||||
) {
|
||||
// Ignore debug sessions that are not applicable to us
|
||||
if (!isValidEvent(event.name)) {
|
||||
return;
|
||||
}
|
||||
|
||||
let processes: psList.ProcessDescriptor[] = [];
|
||||
try {
|
||||
processes = await psList();
|
||||
} catch (error) {
|
||||
logger.logError(`Error retrieving processes to clean-up: `, error as Error);
|
||||
}
|
||||
|
||||
const devserver = processes.find(
|
||||
(process: psList.ProcessDescriptor) => !!(process && process.cmd && process.cmd.toLowerCase().match(targetProcess)));
|
||||
|
||||
if (devserver) {
|
||||
killProcess(devserver.pid, logger);
|
||||
}
|
||||
}
|
|
@ -1,141 +0,0 @@
|
|||
/* --------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
* ------------------------------------------------------------------------------------------ */
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import { IRazorDocumentChangeEvent } from '../Document/IRazorDocumentChangeEvent';
|
||||
import { RazorDocumentChangeKind } from '../Document/RazorDocumentChangeKind';
|
||||
import { RazorDocumentManager } from '../Document/RazorDocumentManager';
|
||||
import { getUriPath } from '../UriPaths';
|
||||
|
||||
export class CSharpPreviewPanel {
|
||||
public static readonly viewType = 'razorCSharpPreview';
|
||||
|
||||
private panel: vscode.WebviewPanel | undefined;
|
||||
private csharpContent: string | undefined;
|
||||
|
||||
constructor(
|
||||
private readonly documentManager: RazorDocumentManager) {
|
||||
documentManager.onChange((event) => this.documentChanged(event));
|
||||
}
|
||||
|
||||
public async show() {
|
||||
if (this.panel) {
|
||||
this.panel.reveal(vscode.ViewColumn.Two);
|
||||
} else {
|
||||
this.panel = vscode.window.createWebviewPanel(
|
||||
CSharpPreviewPanel.viewType,
|
||||
'Razor C# Preview',
|
||||
vscode.ViewColumn.Two, {
|
||||
enableScripts: true,
|
||||
// Disallow any remote sources
|
||||
localResourceRoots: [],
|
||||
});
|
||||
this.attachToCurrentPanel();
|
||||
}
|
||||
|
||||
await this.update();
|
||||
}
|
||||
|
||||
public async revive(panel: vscode.WebviewPanel) {
|
||||
this.panel = panel;
|
||||
this.attachToCurrentPanel();
|
||||
await this.update();
|
||||
}
|
||||
|
||||
private async documentChanged(event: IRazorDocumentChangeEvent) {
|
||||
if (!this.panel) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.kind === RazorDocumentChangeKind.csharpChanged ||
|
||||
event.kind === RazorDocumentChangeKind.opened ||
|
||||
event.kind === RazorDocumentChangeKind.closed) {
|
||||
await this.update();
|
||||
}
|
||||
}
|
||||
|
||||
private attachToCurrentPanel() {
|
||||
if (!this.panel) {
|
||||
vscode.window.showErrorMessage('Unexpected error when attaching to C# preview window.');
|
||||
return;
|
||||
}
|
||||
|
||||
this.panel.webview.onDidReceiveMessage(async message => {
|
||||
switch (message.command) {
|
||||
case 'copy':
|
||||
if (!this.csharpContent) {
|
||||
return;
|
||||
}
|
||||
|
||||
await vscode.env.clipboard.writeText(this.csharpContent);
|
||||
vscode.window.showInformationMessage('Razor C# copied to clipboard');
|
||||
return;
|
||||
}
|
||||
});
|
||||
this.panel.onDidDispose(() => this.panel = undefined);
|
||||
}
|
||||
|
||||
private async update() {
|
||||
if (!this.panel) {
|
||||
return;
|
||||
}
|
||||
const document = await this.documentManager.getActiveDocument();
|
||||
let hostDocumentFilePath = '';
|
||||
let virtualDocumentFilePath = '';
|
||||
|
||||
if (document) {
|
||||
// The document is guaranteed to be a Razor document
|
||||
this.csharpContent = document.csharpDocument.getContent();
|
||||
hostDocumentFilePath = getUriPath(document.uri);
|
||||
virtualDocumentFilePath = getUriPath(document.csharpDocument.uri);
|
||||
} else {
|
||||
this.csharpContent = undefined;
|
||||
}
|
||||
|
||||
let content = this.csharpContent ? this.csharpContent : '';
|
||||
content = content.replace(/</g, '<').replace(/</g, '>');
|
||||
|
||||
this.panel.webview.html = `<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="Content-Security-Policy">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Report a Razor issue</title>
|
||||
<style>
|
||||
button {
|
||||
background-color: #eff3f6;
|
||||
background-image: linear-gradient(-180deg,#fafbfc,#eff3f6 90%);
|
||||
color: #24292e;
|
||||
border: 1px solid rgba(27,31,35,.2);
|
||||
border-radius: .25em;
|
||||
font-size: 1.1em;
|
||||
font-weight: 600;
|
||||
line-height: 18px;
|
||||
padding: 6px 12px;
|
||||
vertical-align: middle;
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script type="text/javascript">
|
||||
const vscode = acquireVsCodeApi();
|
||||
function copy() {
|
||||
vscode.postMessage({
|
||||
command: 'copy'
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<p>Host document file path: <strong>${hostDocumentFilePath}</strong></p>
|
||||
<p>Virtual document file path: <strong>${virtualDocumentFilePath}</strong></p
|
||||
<p><button onclick="copy()">Copy C#</button></p>
|
||||
<hr />
|
||||
<pre>${content}</pre>
|
||||
</body>
|
||||
</html>`;
|
||||
}
|
||||
}
|
|
@ -1,102 +0,0 @@
|
|||
/* --------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
* ------------------------------------------------------------------------------------------ */
|
||||
|
||||
import { IProjectedDocument } from '../Projection/IProjectedDocument';
|
||||
import { ServerTextChange } from '../RPC/ServerTextChange';
|
||||
import { getUriPath } from '../UriPaths';
|
||||
import * as vscode from '../vscodeAdapter';
|
||||
|
||||
export class CSharpProjectedDocument implements IProjectedDocument {
|
||||
public readonly path: string;
|
||||
|
||||
private content = '';
|
||||
private preProvisionalContent: string | undefined;
|
||||
private provisionalEditAt: number | undefined;
|
||||
private hostDocumentVersion: number | null = null;
|
||||
private projectedDocumentVersion = 0;
|
||||
|
||||
public constructor(public readonly uri: vscode.Uri) {
|
||||
this.path = getUriPath(uri);
|
||||
}
|
||||
|
||||
public get hostDocumentSyncVersion(): number | null {
|
||||
return this.hostDocumentVersion;
|
||||
}
|
||||
|
||||
public get projectedDocumentSyncVersion(): number {
|
||||
return this.projectedDocumentVersion;
|
||||
}
|
||||
|
||||
public update(edits: ServerTextChange[], hostDocumentVersion: number) {
|
||||
this.removeProvisionalDot();
|
||||
|
||||
this.hostDocumentVersion = hostDocumentVersion;
|
||||
|
||||
if (edits.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
let content = this.content;
|
||||
for (const edit of edits.reverse()) {
|
||||
// TODO: Use a better data structure to represent the content, string concatenation is slow.
|
||||
content = this.getEditedContent(edit.newText, edit.span.start, edit.span.start + edit.span.length, content);
|
||||
}
|
||||
|
||||
this.setContent(content);
|
||||
}
|
||||
|
||||
public reset() {
|
||||
this.provisionalEditAt = undefined;
|
||||
this.preProvisionalContent = undefined;
|
||||
this.hostDocumentVersion = null;
|
||||
this.setContent('');
|
||||
}
|
||||
|
||||
public getContent() {
|
||||
return this.content;
|
||||
}
|
||||
|
||||
// A provisional dot represents a '.' that's inserted into the projected document but will be
|
||||
// removed prior to any edits that get applied. In Razor's case a provisional dot is used to
|
||||
// show completions after an expression for a dot that's usually interpreted as Html.
|
||||
public addProvisionalDotAt(index: number) {
|
||||
if (this.provisionalEditAt === index) {
|
||||
// Edits already applied.
|
||||
return;
|
||||
}
|
||||
|
||||
this.removeProvisionalDot();
|
||||
|
||||
const newContent = this.getEditedContent('.', index, index, this.content);
|
||||
this.preProvisionalContent = this.content;
|
||||
this.provisionalEditAt = index;
|
||||
this.setContent(newContent);
|
||||
}
|
||||
|
||||
public removeProvisionalDot() {
|
||||
if (this.provisionalEditAt && this.preProvisionalContent) {
|
||||
// Undo provisional edit if one was applied.
|
||||
this.setContent(this.preProvisionalContent);
|
||||
this.provisionalEditAt = undefined;
|
||||
this.preProvisionalContent = undefined;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private getEditedContent(newText: string, start: number, end: number, content: string) {
|
||||
const before = content.substr(0, start);
|
||||
const after = content.substr(end);
|
||||
content = `${before}${newText}${after}`;
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
private setContent(content: string) {
|
||||
this.projectedDocumentVersion++;
|
||||
this.content = content;
|
||||
}
|
||||
}
|
|
@ -1,70 +0,0 @@
|
|||
/* --------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
* ------------------------------------------------------------------------------------------ */
|
||||
|
||||
import { IRazorDocumentChangeEvent } from '../Document/IRazorDocumentChangeEvent';
|
||||
import { IRazorDocumentManager } from '../Document/IRazorDocumentManager';
|
||||
import { RazorDocumentChangeKind } from '../Document/RazorDocumentChangeKind';
|
||||
import { IEventEmitterFactory } from '../IEventEmitterFactory';
|
||||
import { RazorLogger } from '../RazorLogger';
|
||||
import { getUriPath } from '../UriPaths';
|
||||
import * as vscode from '../vscodeAdapter';
|
||||
|
||||
export class CSharpProjectedDocumentContentProvider implements vscode.TextDocumentContentProvider {
|
||||
public static readonly scheme = 'virtualCSharp-razor';
|
||||
|
||||
private readonly onDidChangeEmitter: vscode.EventEmitter<vscode.Uri>;
|
||||
|
||||
constructor(
|
||||
private readonly documentManager: IRazorDocumentManager,
|
||||
eventEmitterFactory: IEventEmitterFactory,
|
||||
private readonly logger: RazorLogger) {
|
||||
documentManager.onChange((event: IRazorDocumentChangeEvent) => this.documentChanged(event));
|
||||
this.onDidChangeEmitter = eventEmitterFactory.create<vscode.Uri>();
|
||||
}
|
||||
|
||||
public get onDidChange() { return this.onDidChangeEmitter.event; }
|
||||
|
||||
public async provideTextDocumentContent(uri: vscode.Uri) {
|
||||
const razorDocument = this.findRazorDocument(uri);
|
||||
if (!razorDocument) {
|
||||
// Document was removed from the document manager, meaning there's no more content for this
|
||||
// file. Report an empty document.
|
||||
|
||||
if (this.logger.verboseEnabled) {
|
||||
this.logger.logVerbose(
|
||||
`Could not find document '${getUriPath(uri)}' when updating the C# buffer. This typically happens when a document is removed.`);
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
const content = `${razorDocument.csharpDocument.getContent()}
|
||||
// ${razorDocument.csharpDocument.projectedDocumentSyncVersion}`;
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
public ensureDocumentContent(uri: vscode.Uri) {
|
||||
this.onDidChangeEmitter.fire(uri);
|
||||
}
|
||||
|
||||
private documentChanged(event: IRazorDocumentChangeEvent) {
|
||||
if (event.kind === RazorDocumentChangeKind.csharpChanged ||
|
||||
event.kind === RazorDocumentChangeKind.opened ||
|
||||
event.kind === RazorDocumentChangeKind.removed) {
|
||||
// We also notify changes on document removal in order to tell VSCode that there's no more
|
||||
// C# content for the file.
|
||||
|
||||
this.onDidChangeEmitter.fire(event.document.csharpDocument.uri);
|
||||
}
|
||||
}
|
||||
|
||||
private findRazorDocument(uri: vscode.Uri) {
|
||||
const projectedPath = getUriPath(uri);
|
||||
|
||||
return this.documentManager.documents.find(razorDocument =>
|
||||
razorDocument.csharpDocument.path.localeCompare(
|
||||
projectedPath, undefined, { sensitivity: 'base' }) === 0);
|
||||
}
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
/* --------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
* ------------------------------------------------------------------------------------------ */
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import { RazorDocumentManager } from '../Document/RazorDocumentManager';
|
||||
import { IEventEmitterFactory } from '../IEventEmitterFactory';
|
||||
import { RazorLogger } from '../RazorLogger';
|
||||
import { CSharpPreviewPanel } from './CSharpPreviewPanel';
|
||||
import { CSharpProjectedDocumentContentProvider } from './CSharpProjectedDocumentContentProvider';
|
||||
|
||||
export class RazorCSharpFeature {
|
||||
public readonly projectionProvider: CSharpProjectedDocumentContentProvider;
|
||||
private readonly csharpPreviewPanel: CSharpPreviewPanel;
|
||||
|
||||
constructor(
|
||||
documentManager: RazorDocumentManager,
|
||||
eventEmitterFactory: IEventEmitterFactory,
|
||||
logger: RazorLogger) {
|
||||
this.projectionProvider = new CSharpProjectedDocumentContentProvider(documentManager, eventEmitterFactory, logger);
|
||||
this.csharpPreviewPanel = new CSharpPreviewPanel(documentManager);
|
||||
}
|
||||
|
||||
public register() {
|
||||
const registrations = [
|
||||
vscode.workspace.registerTextDocumentContentProvider(
|
||||
CSharpProjectedDocumentContentProvider.scheme, this.projectionProvider),
|
||||
vscode.commands.registerCommand(
|
||||
'extension.showRazorCSharpWindow', () => this.csharpPreviewPanel.show()),
|
||||
];
|
||||
|
||||
if (vscode.window.registerWebviewPanelSerializer) {
|
||||
registrations.push(vscode.window.registerWebviewPanelSerializer(CSharpPreviewPanel.viewType, {
|
||||
deserializeWebviewPanel: async (panel: vscode.WebviewPanel) => {
|
||||
await this.csharpPreviewPanel.revive(panel);
|
||||
},
|
||||
}));
|
||||
}
|
||||
|
||||
return vscode.Disposable.from(...registrations);
|
||||
}
|
||||
}
|
|
@ -1,66 +0,0 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import { RequestType } from 'vscode-languageclient';
|
||||
import { RazorDocumentManager } from '../Document/RazorDocumentManager';
|
||||
import { RazorLanguageServerClient } from '../RazorLanguageServerClient';
|
||||
import { RazorLogger } from '../RazorLogger';
|
||||
import { convertRangeFromSerializable } from '../RPC/SerializableRange';
|
||||
import { RazorCodeAction } from './RazorCodeAction';
|
||||
import { SerializableDelegatedCodeActionParams } from './SerializableDelegatedCodeActionParams';
|
||||
|
||||
export class CodeActionsHandler {
|
||||
private static readonly provideCodeActionsEndpoint = 'razor/provideCodeActions';
|
||||
private codeActionRequestType: RequestType<SerializableDelegatedCodeActionParams, RazorCodeAction[], any> = new RequestType(CodeActionsHandler.provideCodeActionsEndpoint);
|
||||
private emptyCodeActionResponse: RazorCodeAction[] = [];
|
||||
|
||||
constructor(
|
||||
private readonly documentManager: RazorDocumentManager,
|
||||
private readonly serverClient: RazorLanguageServerClient,
|
||||
private readonly logger: RazorLogger) { }
|
||||
|
||||
public register() {
|
||||
return this.serverClient.onRequestWithParams<SerializableDelegatedCodeActionParams, RazorCodeAction[], any>(
|
||||
this.codeActionRequestType,
|
||||
async (request, token) => this.provideCodeActions(request, token));
|
||||
}
|
||||
|
||||
private async provideCodeActions(
|
||||
delegatedCodeActionParams: SerializableDelegatedCodeActionParams,
|
||||
cancellationToken: vscode.CancellationToken) {
|
||||
try {
|
||||
const codeActionParams = delegatedCodeActionParams.codeActionParams;
|
||||
const razorDocumentUri = vscode.Uri.parse(codeActionParams.textDocument.uri, true);
|
||||
const razorDocument = await this.documentManager.getDocument(razorDocumentUri);
|
||||
if (razorDocument === undefined) {
|
||||
return this.emptyCodeActionResponse;
|
||||
}
|
||||
|
||||
const virtualCSharpUri = razorDocument.csharpDocument.uri;
|
||||
|
||||
const range = convertRangeFromSerializable(codeActionParams.range);
|
||||
|
||||
const commands = await vscode.commands.executeCommand<vscode.Command[]>(
|
||||
'vscode.executeCodeActionProvider',
|
||||
virtualCSharpUri,
|
||||
range) as vscode.Command[];
|
||||
|
||||
if (commands.length === 0) {
|
||||
return this.emptyCodeActionResponse;
|
||||
}
|
||||
|
||||
return commands.map(c => this.commandAsCodeAction(c));
|
||||
} catch (error) {
|
||||
this.logger.logWarning(`${CodeActionsHandler.provideCodeActionsEndpoint} failed with ${error}`);
|
||||
}
|
||||
|
||||
return this.emptyCodeActionResponse;
|
||||
}
|
||||
|
||||
private commandAsCodeAction(command: vscode.Command): RazorCodeAction {
|
||||
return { title: command.title, data: { CustomTags: ['CodeActionFromVSCode'] } } as RazorCodeAction;
|
||||
}
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
/* --------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
* ------------------------------------------------------------------------------------------ */
|
||||
|
||||
import { SerializableWorkspaceEdit } from '../RPC/SerializableWorkspaceEdit';
|
||||
import { RazorCodeActionDataParams } from './RazorCodeActionDataParams';
|
||||
import { RazorCodeActionResolutionParams } from './RazorCodeActionResolutionParams';
|
||||
|
||||
export interface RazorCodeAction {
|
||||
title: string;
|
||||
edit: SerializableWorkspaceEdit;
|
||||
data: RazorCodeActionResolutionParams | RazorCodeActionDataParams;
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
/* --------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
* ------------------------------------------------------------------------------------------ */
|
||||
|
||||
export interface RazorCodeActionDataParams {
|
||||
CustomTags: string[];
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
/* --------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
* ------------------------------------------------------------------------------------------ */
|
||||
|
||||
export interface RazorCodeActionResolutionParams {
|
||||
action: string;
|
||||
data: object;
|
||||
}
|
|
@ -1,47 +0,0 @@
|
|||
/* --------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
* ------------------------------------------------------------------------------------------ */
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import { RazorLanguageServerClient } from '../RazorLanguageServerClient';
|
||||
import { RazorLogger } from '../RazorLogger';
|
||||
import { convertWorkspaceEditFromSerializable } from '../RPC/SerializableWorkspaceEdit';
|
||||
import { RazorCodeAction } from './RazorCodeAction';
|
||||
import { RazorCodeActionResolutionParams } from './RazorCodeActionResolutionParams';
|
||||
|
||||
export class RazorCodeActionRunner {
|
||||
private static readonly codeActionResolveEndpoint = 'textDocument/codeActionResolve';
|
||||
private static readonly razorCodeActionRunnerCommand = 'razor/runCodeAction';
|
||||
|
||||
constructor(
|
||||
private readonly serverClient: RazorLanguageServerClient,
|
||||
private readonly logger: RazorLogger,
|
||||
) {}
|
||||
|
||||
public register(): vscode.Disposable {
|
||||
return vscode.commands.registerCommand(
|
||||
RazorCodeActionRunner.razorCodeActionRunnerCommand,
|
||||
(request: RazorCodeActionResolutionParams) => this.runCodeAction(request),
|
||||
this);
|
||||
}
|
||||
|
||||
private async runCodeAction(request: RazorCodeActionResolutionParams): Promise<boolean> {
|
||||
const response: RazorCodeAction = await this.serverClient.sendRequest(
|
||||
RazorCodeActionRunner.codeActionResolveEndpoint,
|
||||
{ data: request, title: request.action });
|
||||
|
||||
let changesWorkspaceEdit: vscode.WorkspaceEdit;
|
||||
let documentChangesWorkspaceEdit: vscode.WorkspaceEdit;
|
||||
|
||||
try {
|
||||
changesWorkspaceEdit = convertWorkspaceEditFromSerializable({changes: response.edit.changes});
|
||||
documentChangesWorkspaceEdit = convertWorkspaceEditFromSerializable({documentChanges: response.edit.documentChanges});
|
||||
} catch (error) {
|
||||
this.logger.logError(`Unexpected error deserializing code action for ${request.action}`, error as Error);
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
|
||||
return vscode.workspace.applyEdit(documentChangesWorkspaceEdit).then(() => vscode.workspace.applyEdit(changesWorkspaceEdit));
|
||||
}
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
/* --------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
* ------------------------------------------------------------------------------------------ */
|
||||
|
||||
import { SerializableRange } from '../RPC/SerializableRange';
|
||||
import { SerializableTextDocumentIdentifier } from '../RPC/SerializableTextDocumentIdentifier';
|
||||
|
||||
export interface SerializableCodeActionParams {
|
||||
textDocument: SerializableTextDocumentIdentifier;
|
||||
range: SerializableRange;
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
/* --------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
* ------------------------------------------------------------------------------------------ */
|
||||
|
||||
import { SerializableCodeActionParams } from './SerializableCodeActionParams';
|
||||
|
||||
export interface SerializableDelegatedCodeActionParams {
|
||||
codeActionParams: SerializableCodeActionParams;
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
/* --------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
* ------------------------------------------------------------------------------------------ */
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
export class RazorCodeLens extends vscode.CodeLens {
|
||||
constructor(
|
||||
range: vscode.Range,
|
||||
public uri: vscode.Uri,
|
||||
public document: vscode.TextDocument,
|
||||
command?: vscode.Command) {
|
||||
|
||||
super(range, command);
|
||||
}
|
||||
}
|
|
@ -1,134 +0,0 @@
|
|||
/* --------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
* ------------------------------------------------------------------------------------------ */
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import { RazorDocumentChangeKind } from '../Document/RazorDocumentChangeKind';
|
||||
import { RazorDocumentManager } from '../Document/RazorDocumentManager';
|
||||
import { RazorDocumentSynchronizer } from '../Document/RazorDocumentSynchronizer';
|
||||
import { RazorLanguageFeatureBase } from '../RazorLanguageFeatureBase';
|
||||
import { RazorLanguageServiceClient } from '../RazorLanguageServiceClient';
|
||||
import { RazorLogger } from '../RazorLogger';
|
||||
import { LanguageKind } from '../RPC/LanguageKind';
|
||||
import { RazorCodeLens } from './RazorCodeLens';
|
||||
export class RazorCodeLensProvider
|
||||
extends RazorLanguageFeatureBase
|
||||
implements vscode.CodeLensProvider {
|
||||
|
||||
public onDidChangeCodeLenses: vscode.Event<void>;
|
||||
|
||||
constructor(
|
||||
documentSynchronizer: RazorDocumentSynchronizer,
|
||||
documentManager: RazorDocumentManager,
|
||||
serviceClient: RazorLanguageServiceClient,
|
||||
logger: RazorLogger) {
|
||||
|
||||
super(documentSynchronizer, documentManager, serviceClient, logger);
|
||||
|
||||
const onCodeLensChangedEmitter = new vscode.EventEmitter<void>();
|
||||
this.onDidChangeCodeLenses = onCodeLensChangedEmitter.event;
|
||||
|
||||
documentManager.onChange(async (event) => {
|
||||
if (event.kind !== RazorDocumentChangeKind.added) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Sometimes when a file already open in the editor is renamed, provideCodeLens would return empty
|
||||
// because the background C# document is not ready yet. So, when that happens we should manually invoke
|
||||
// a code lens refresh after waiting for a little while.
|
||||
const openDocumentUris = vscode.workspace.textDocuments.filter(doc => !doc.isClosed).map(doc => doc.uri);
|
||||
if (openDocumentUris.includes(event.document.uri)) {
|
||||
await new Promise(r => setTimeout(r, 5000));
|
||||
onCodeLensChangedEmitter.fire();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public async provideCodeLenses(document: vscode.TextDocument, token: vscode.CancellationToken) {
|
||||
try {
|
||||
const razorDocument = await this.documentManager.getDocument(document.uri);
|
||||
if (!razorDocument) {
|
||||
return;
|
||||
}
|
||||
|
||||
const csharpDocument = razorDocument.csharpDocument;
|
||||
|
||||
// Get all the code lenses that applies to our projected C# document.
|
||||
const codeLenses = await vscode.commands.executeCommand<vscode.CodeLens[]>(
|
||||
'vscode.executeCodeLensProvider',
|
||||
csharpDocument.uri) as vscode.CodeLens[];
|
||||
if (!codeLenses) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Re-map the CodeLens locations to the original Razor document.
|
||||
const remappedCodeLenses = new Array<vscode.CodeLens>();
|
||||
for (const codeLens of codeLenses) {
|
||||
const result = await this.serviceClient.mapToDocumentRanges(
|
||||
LanguageKind.CSharp,
|
||||
[codeLens.range],
|
||||
razorDocument.uri);
|
||||
if (result && result.ranges.length > 0) {
|
||||
const newCodeLens = new RazorCodeLens(result.ranges[0], razorDocument.uri, document, codeLens.command);
|
||||
remappedCodeLenses.push(newCodeLens);
|
||||
} else {
|
||||
// This means this CodeLens was for non-user code. We can safely ignore those.
|
||||
}
|
||||
}
|
||||
|
||||
return remappedCodeLenses;
|
||||
|
||||
} catch (error) {
|
||||
this.logger.logWarning(`provideCodeLens failed with ${error}`);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
public async resolveCodeLens(codeLens: vscode.CodeLens, token: vscode.CancellationToken) {
|
||||
if (codeLens instanceof RazorCodeLens) {
|
||||
return this.resolveRazorCodeLens(codeLens, token);
|
||||
}
|
||||
}
|
||||
|
||||
private async resolveRazorCodeLens(codeLens: RazorCodeLens, token: vscode.CancellationToken): Promise<vscode.CodeLens> {
|
||||
// Initialize with default values.
|
||||
codeLens.command = {
|
||||
title: '',
|
||||
command: '',
|
||||
arguments: [],
|
||||
};
|
||||
|
||||
try {
|
||||
const razorDocument = await this.documentManager.getDocument(codeLens.uri);
|
||||
if (!razorDocument) {
|
||||
return codeLens;
|
||||
}
|
||||
|
||||
// Make sure this CodeLens is for a valid location in the projected C# document.
|
||||
const projection = await this.getProjection(codeLens.document, codeLens.range.start, token);
|
||||
if (!projection || projection.languageKind !== LanguageKind.CSharp) {
|
||||
return codeLens;
|
||||
}
|
||||
|
||||
const references = await vscode.commands.executeCommand<vscode.Location[]>(
|
||||
'vscode.executeReferenceProvider',
|
||||
projection.uri,
|
||||
projection.position) as vscode.Location[];
|
||||
|
||||
// We now have a list of references to show in the CodeLens.
|
||||
const count = references.length;
|
||||
codeLens.command = {
|
||||
title: count === 1 ? '1 reference' : `${count} references`,
|
||||
command: 'editor.action.showReferences',
|
||||
arguments: [razorDocument.uri, codeLens.range.start, references],
|
||||
};
|
||||
|
||||
return codeLens;
|
||||
|
||||
} catch (error) {
|
||||
this.logger.logWarning(`resolveCodeLens failed with ${error}`);
|
||||
return codeLens;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
/* --------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
* ------------------------------------------------------------------------------------------ */
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import { Range } from 'vscode-languageserver-types';
|
||||
|
||||
export class ColorPresentationContext {
|
||||
constructor(
|
||||
public readonly uri: vscode.Uri,
|
||||
public readonly range: Range) {
|
||||
}
|
||||
}
|
|
@ -1,92 +0,0 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import { RequestType } from 'vscode-languageclient';
|
||||
import { RazorDocumentManager } from '../Document/RazorDocumentManager';
|
||||
import { RazorLanguageServerClient } from '../RazorLanguageServerClient';
|
||||
import { RazorLogger } from '../RazorLogger';
|
||||
import { convertTextEditToSerializable, SerializableTextEdit } from '../RPC/SerializableTextEdit';
|
||||
import { ColorPresentationContext } from './ColorPresentationContext';
|
||||
import { SerializableColorPresentation } from './SerializableColorPresentation';
|
||||
import { SerializableColorPresentationParams } from './SerializableColorPresentationParams';
|
||||
|
||||
export class ColorPresentationHandler {
|
||||
private static readonly provideHtmlColorPresentation = 'razor/provideHtmlColorPresentation';
|
||||
private colorPresentationRequestType: RequestType<SerializableColorPresentationParams, SerializableColorPresentation[], any> =
|
||||
new RequestType(ColorPresentationHandler.provideHtmlColorPresentation);
|
||||
private emptyColorInformationResponse: SerializableColorPresentation[] = [];
|
||||
|
||||
constructor(
|
||||
private readonly documentManager: RazorDocumentManager,
|
||||
private readonly serverClient: RazorLanguageServerClient,
|
||||
private readonly logger: RazorLogger) {
|
||||
}
|
||||
|
||||
public register() {
|
||||
return this.serverClient.onRequestWithParams<SerializableColorPresentationParams, SerializableColorPresentation[], any>(
|
||||
this.colorPresentationRequestType,
|
||||
async (request: SerializableColorPresentationParams, token: vscode.CancellationToken) => this.provideHtmlColorPresentation(request, token));
|
||||
}
|
||||
|
||||
private async provideHtmlColorPresentation(
|
||||
colorPresentationParams: SerializableColorPresentationParams,
|
||||
cancellationToken: vscode.CancellationToken) {
|
||||
try {
|
||||
const razorDocumentUri = vscode.Uri.parse(`${colorPresentationParams.textDocument.uri}`, true);
|
||||
const razorDocument = await this.documentManager.getDocument(razorDocumentUri);
|
||||
if (razorDocument === undefined) {
|
||||
this.logger.logWarning(`Could not find Razor document ${razorDocumentUri}; returning empty color information.`);
|
||||
return this.emptyColorInformationResponse;
|
||||
}
|
||||
|
||||
const color = new vscode.Color(
|
||||
colorPresentationParams.color.red,
|
||||
colorPresentationParams.color.green,
|
||||
colorPresentationParams.color.blue,
|
||||
colorPresentationParams.color.alpha);
|
||||
const virtualHtmlUri = razorDocument.htmlDocument.uri;
|
||||
|
||||
const colorPresentations = await vscode.commands.executeCommand<vscode.ColorPresentation[]>(
|
||||
'vscode.executeColorPresentationProvider',
|
||||
color,
|
||||
new ColorPresentationContext(virtualHtmlUri, colorPresentationParams.range));
|
||||
|
||||
const serializableColorPresentations = this.SerializeColorPresentations(colorPresentations);
|
||||
return serializableColorPresentations;
|
||||
} catch (error) {
|
||||
this.logger.logWarning(`${ColorPresentationHandler.provideHtmlColorPresentation} failed with ${error}`);
|
||||
}
|
||||
|
||||
return this.emptyColorInformationResponse;
|
||||
}
|
||||
|
||||
private SerializeColorPresentations(colorPresentations: vscode.ColorPresentation[]) {
|
||||
const serializableColorPresentations = new Array<SerializableColorPresentation>();
|
||||
for (const colorPresentation of colorPresentations) {
|
||||
let serializedTextEdit: any = null;
|
||||
const serializableAdditionalTextEdits = new Array<SerializableTextEdit>();
|
||||
|
||||
if (colorPresentation.textEdit) {
|
||||
serializedTextEdit = convertTextEditToSerializable(colorPresentation.textEdit);
|
||||
}
|
||||
|
||||
if (colorPresentation.additionalTextEdits) {
|
||||
for (const additionalTextEdit of colorPresentation.additionalTextEdits) {
|
||||
const serializableAdditionalTextEdit = convertTextEditToSerializable(additionalTextEdit);
|
||||
serializableAdditionalTextEdits.push(serializableAdditionalTextEdit);
|
||||
}
|
||||
}
|
||||
|
||||
const serializableColorPresentation = new SerializableColorPresentation(
|
||||
colorPresentation.label,
|
||||
serializedTextEdit,
|
||||
serializableAdditionalTextEdits);
|
||||
serializableColorPresentations.push(serializableColorPresentation);
|
||||
}
|
||||
|
||||
return serializableColorPresentations;
|
||||
}
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
/* --------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
* ------------------------------------------------------------------------------------------ */
|
||||
|
||||
import { SerializableTextEdit } from '../RPC/SerializableTextEdit';
|
||||
|
||||
export class SerializableColorPresentation {
|
||||
constructor(
|
||||
public readonly label: string,
|
||||
public readonly textEdit?: SerializableTextEdit,
|
||||
public readonly additionalTextEdits?: SerializableTextEdit[]) {
|
||||
}
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
/* --------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
* ------------------------------------------------------------------------------------------ */
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import { SerializableRange } from '../RPC/SerializableRange';
|
||||
import { SerializableTextDocumentIdentifier } from '../RPC/SerializableTextDocumentIdentifier';
|
||||
|
||||
export interface SerializableColorPresentationParams {
|
||||
textDocument: SerializableTextDocumentIdentifier;
|
||||
color: vscode.Color;
|
||||
range: SerializableRange;
|
||||
}
|
|
@ -1,169 +0,0 @@
|
|||
/* --------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
* ------------------------------------------------------------------------------------------ */
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import { CSharpProjectedDocument } from '../CSharp/CSharpProjectedDocument';
|
||||
import { CSharpProjectedDocumentContentProvider } from '../CSharp/CSharpProjectedDocumentContentProvider';
|
||||
import { RazorDocumentManager } from '../Document/RazorDocumentManager';
|
||||
import { ProjectionResult } from '../Projection/ProjectionResult';
|
||||
import { RazorLanguage } from '../RazorLanguage';
|
||||
import { RazorLanguageServiceClient } from '../RazorLanguageServiceClient';
|
||||
import { RazorLogger } from '../RazorLogger';
|
||||
import { LanguageKind } from '../RPC/LanguageKind';
|
||||
import { RazorCompletionItemProvider } from './RazorCompletionItemProvider';
|
||||
|
||||
export class ProvisionalCompletionOrchestrator {
|
||||
private provisionalDotsMayBeActive = false;
|
||||
private currentActiveDocument: vscode.TextDocument | undefined;
|
||||
|
||||
constructor(
|
||||
private readonly documentManager: RazorDocumentManager,
|
||||
private readonly projectedCSharpProvider: CSharpProjectedDocumentContentProvider,
|
||||
private readonly serviceClient: RazorLanguageServiceClient,
|
||||
private readonly logger: RazorLogger) {
|
||||
}
|
||||
|
||||
public register() {
|
||||
if (vscode.window.activeTextEditor) {
|
||||
this.currentActiveDocument = vscode.window.activeTextEditor.document;
|
||||
}
|
||||
|
||||
// There's no event in VSCode to let us know when the completion window has been dismissed.
|
||||
// Because of this restriction we do a best effort to understand when the user has gone onto
|
||||
// different actions (other than viewing completion).
|
||||
|
||||
const onDidChangeSelectionRegistration = vscode.window.onDidChangeTextEditorSelection(
|
||||
args => this.tryRemoveProvisionalDot(args.textEditor.document));
|
||||
const onDidChangeRegistration = vscode.workspace.onDidChangeTextDocument(async args => {
|
||||
if (args.contentChanges.length === 1 && args.contentChanges[0].text === '.') {
|
||||
// Don't want to remove a provisional dot that we just added.
|
||||
return;
|
||||
}
|
||||
|
||||
await this.tryRemoveProvisionalDot(args.document);
|
||||
});
|
||||
const onDidChangeActiveEditorRegistration = vscode.window.onDidChangeActiveTextEditor(async args => {
|
||||
if (this.currentActiveDocument) {
|
||||
await this.tryRemoveProvisionalDot(this.currentActiveDocument);
|
||||
}
|
||||
|
||||
if (args) {
|
||||
this.currentActiveDocument = args.document;
|
||||
} else {
|
||||
this.currentActiveDocument = undefined;
|
||||
}
|
||||
});
|
||||
|
||||
return vscode.Disposable.from(
|
||||
onDidChangeRegistration,
|
||||
onDidChangeSelectionRegistration,
|
||||
onDidChangeActiveEditorRegistration);
|
||||
}
|
||||
|
||||
public async tryGetProvisionalCompletions(
|
||||
hostDocumentUri: vscode.Uri,
|
||||
projection: ProjectionResult,
|
||||
completionContext: vscode.CompletionContext) {
|
||||
// We expect to be called in scenarios where the user has just typed a dot after
|
||||
// some identifier.
|
||||
// Such as (cursor is pipe): "DateTime.| "
|
||||
// In this case Razor interprets after the dot as Html and before it as C#. We
|
||||
// use this criteria to provide a better completion experience for what we call
|
||||
// provisional changes.
|
||||
|
||||
if (projection.languageKind !== LanguageKind.Html) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (completionContext.triggerCharacter !== '.') {
|
||||
return null;
|
||||
}
|
||||
|
||||
const htmlPosition = projection.position;
|
||||
if (htmlPosition.character === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const previousCharacterPosition = new vscode.Position(
|
||||
htmlPosition.line,
|
||||
htmlPosition.character - 1,
|
||||
);
|
||||
const previousCharacterQuery = await this.serviceClient.languageQuery(
|
||||
previousCharacterPosition,
|
||||
hostDocumentUri);
|
||||
|
||||
if (previousCharacterQuery.kind !== LanguageKind.CSharp) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const document = await this.documentManager.getDocument(hostDocumentUri);
|
||||
const projectedDocument = document.csharpDocument as CSharpProjectedDocument;
|
||||
const absoluteIndex = previousCharacterQuery.positionIndex;
|
||||
|
||||
if (this.logger.verboseEnabled) {
|
||||
this.logger.logVerbose(`Applying provisional completion on ${projectedDocument.uri} ` +
|
||||
`at (${previousCharacterQuery.position.line}, ${previousCharacterQuery.position.character})`);
|
||||
}
|
||||
|
||||
// Edit the projected document to contain a '.'. This allows C# completion to provide valid completion items
|
||||
// for moments when a user has typed a '.' that's typically interpreted as Html.
|
||||
// This provisional dot is removed when one of the following is true:
|
||||
// 1. The user starts typing
|
||||
// 2. The user swaps active documents
|
||||
// 3. The user selects different content
|
||||
// 4. The projected document gets an update request
|
||||
projectedDocument.addProvisionalDotAt(absoluteIndex);
|
||||
this.projectedCSharpProvider.ensureDocumentContent(projectedDocument.uri);
|
||||
|
||||
// We open and then re-save because we're adding content to the text document within an event.
|
||||
// We need to allow the system to propogate this text document change.
|
||||
const newDocument = await vscode.workspace.openTextDocument(projectedDocument.uri);
|
||||
await newDocument.save();
|
||||
|
||||
const provisionalPosition = new vscode.Position(
|
||||
previousCharacterQuery.position.line,
|
||||
previousCharacterQuery.position.character + 1);
|
||||
const completionList = await RazorCompletionItemProvider.getCompletions(
|
||||
projectedDocument.uri,
|
||||
htmlPosition,
|
||||
provisionalPosition,
|
||||
completionContext.triggerCharacter);
|
||||
|
||||
// We track when we add provisional dots to avoid doing unnecessary work on commonly invoked events.
|
||||
this.provisionalDotsMayBeActive = true;
|
||||
|
||||
return completionList;
|
||||
}
|
||||
|
||||
private async tryRemoveProvisionalDot(document: vscode.TextDocument) {
|
||||
if (!this.provisionalDotsMayBeActive) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (document.languageId !== RazorLanguage.id) {
|
||||
return;
|
||||
}
|
||||
|
||||
const razorDocument = await this.documentManager.getActiveDocument();
|
||||
if (!razorDocument) {
|
||||
return;
|
||||
}
|
||||
|
||||
const projectedDocument = razorDocument.csharpDocument as CSharpProjectedDocument;
|
||||
if (projectedDocument.removeProvisionalDot()) {
|
||||
this.projectedCSharpProvider.ensureDocumentContent(projectedDocument.uri);
|
||||
|
||||
if (this.logger.verboseEnabled) {
|
||||
this.logger.logVerbose(`Ensured removal of provisional completion on ${projectedDocument.uri}.`);
|
||||
}
|
||||
}
|
||||
|
||||
// Don't need to force the document to refresh here by saving because the user has already
|
||||
// moved onto a different action. We only want to re-save the projected document when we
|
||||
// expect instant interactions with the projected document.
|
||||
|
||||
this.provisionalDotsMayBeActive = false;
|
||||
}
|
||||
}
|
|
@ -1,144 +0,0 @@
|
|||
/* --------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
* ------------------------------------------------------------------------------------------ */
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import { RazorDocumentManager } from '../Document/RazorDocumentManager';
|
||||
import { RazorDocumentSynchronizer } from '../Document/RazorDocumentSynchronizer';
|
||||
import { RazorLanguageFeatureBase } from '../RazorLanguageFeatureBase';
|
||||
import { RazorLanguageServiceClient } from '../RazorLanguageServiceClient';
|
||||
import { RazorLogger } from '../RazorLogger';
|
||||
import { getUriPath } from '../UriPaths';
|
||||
import { ProvisionalCompletionOrchestrator } from './ProvisionalCompletionOrchestrator';
|
||||
|
||||
export class RazorCompletionItemProvider
|
||||
extends RazorLanguageFeatureBase
|
||||
implements vscode.CompletionItemProvider {
|
||||
|
||||
public static async getCompletions(
|
||||
projectedUri: vscode.Uri, hostDocumentPosition: vscode.Position,
|
||||
projectedPosition: vscode.Position, triggerCharacter: string | undefined) {
|
||||
|
||||
if (projectedUri) {
|
||||
// "@" is not a valid trigger character for C# / HTML and therefore we need to translate
|
||||
// it into a non-trigger invocation.
|
||||
const modifiedTriggerCharacter = triggerCharacter === '@' ? undefined : triggerCharacter;
|
||||
|
||||
const completions = await vscode
|
||||
.commands
|
||||
.executeCommand<vscode.CompletionList | vscode.CompletionItem[]>(
|
||||
'vscode.executeCompletionItemProvider',
|
||||
projectedUri,
|
||||
projectedPosition,
|
||||
modifiedTriggerCharacter);
|
||||
|
||||
const completionItems =
|
||||
completions instanceof Array ? completions // was vscode.CompletionItem[]
|
||||
: completions ? completions.items // was vscode.CompletionList
|
||||
: [];
|
||||
|
||||
// There are times when the generated code will not line up with the content of the .razor/.cshtml file.
|
||||
// Therefore, we need to offset all completion items' characters by a certain amount in order
|
||||
// to have proper completion. An example of this is typing @DateTime at the beginning of a line.
|
||||
// In the code behind it's represented as __o = DateTime.
|
||||
const completionCharacterOffset = projectedPosition.character - hostDocumentPosition.character;
|
||||
for (const completionItem of completionItems) {
|
||||
const doc = completionItem.documentation as vscode.MarkdownString;
|
||||
if (doc) {
|
||||
// Without this, the documentation doesn't get rendered in the editor.
|
||||
const newDoc = new vscode.MarkdownString(doc.value);
|
||||
newDoc.isTrusted = false;
|
||||
completionItem.documentation = newDoc;
|
||||
}
|
||||
|
||||
if (completionItem.range) {
|
||||
const range = completionItem.range;
|
||||
const insertingRange = (range as any).inserting;
|
||||
if (insertingRange) {
|
||||
const insertingRangeStart = this.offsetColumn(completionCharacterOffset, hostDocumentPosition.line, insertingRange.start);
|
||||
const insertingRangeEnd = this.offsetColumn(completionCharacterOffset, hostDocumentPosition.line, insertingRange.end);
|
||||
(range as any).inserting = new vscode.Range(insertingRangeStart, insertingRangeEnd);
|
||||
}
|
||||
|
||||
const replacingRange = (range as any).replacing;
|
||||
if (replacingRange) {
|
||||
const replacingRangeStart = this.offsetColumn(completionCharacterOffset, hostDocumentPosition.line, replacingRange.start);
|
||||
const replacingRangeEnd = this.offsetColumn(completionCharacterOffset, hostDocumentPosition.line, replacingRange.end);
|
||||
(range as any).replacing = new vscode.Range(replacingRangeStart, replacingRangeEnd);
|
||||
}
|
||||
|
||||
if (range instanceof vscode.Range && range.start && range.end) {
|
||||
const rangeStart = this.offsetColumn(completionCharacterOffset, hostDocumentPosition.line, range.start);
|
||||
const rangeEnd = this.offsetColumn(completionCharacterOffset, hostDocumentPosition.line, range.end);
|
||||
completionItem.range = new vscode.Range(rangeStart, rangeEnd);
|
||||
}
|
||||
}
|
||||
|
||||
// textEdit is deprecated in favor of .range. Clear out its value to avoid any unexpected behavior.
|
||||
completionItem.textEdit = undefined;
|
||||
|
||||
if (triggerCharacter === '@' &&
|
||||
completionItem.commitCharacters) {
|
||||
// We remove `{`, '(', and '*' from the commit characters to prevent auto-completing the first
|
||||
// completion item with a curly brace when a user intended to type `@{}` or `@()`.
|
||||
completionItem.commitCharacters = completionItem.commitCharacters.filter(
|
||||
commitChar => commitChar !== '{' && commitChar !== '(' && commitChar !== '*');
|
||||
}
|
||||
}
|
||||
|
||||
const isIncomplete = completions instanceof Array ? false
|
||||
: completions ? completions.isIncomplete
|
||||
: false;
|
||||
return new vscode.CompletionList(completionItems, isIncomplete);
|
||||
}
|
||||
}
|
||||
|
||||
private static offsetColumn(offset: number, hostDocumentLine: number, projectedPosition: vscode.Position) {
|
||||
const offsetPosition = new vscode.Position(
|
||||
hostDocumentLine,
|
||||
projectedPosition.character - offset);
|
||||
return offsetPosition;
|
||||
}
|
||||
|
||||
constructor(
|
||||
documentSynchronizer: RazorDocumentSynchronizer,
|
||||
documentManager: RazorDocumentManager,
|
||||
serviceClient: RazorLanguageServiceClient,
|
||||
private readonly provisionalCompletionOrchestrator: ProvisionalCompletionOrchestrator,
|
||||
logger: RazorLogger) {
|
||||
super(documentSynchronizer, documentManager, serviceClient, logger);
|
||||
}
|
||||
|
||||
public async provideCompletionItems(
|
||||
document: vscode.TextDocument, position: vscode.Position,
|
||||
token: vscode.CancellationToken, context: vscode.CompletionContext) {
|
||||
const projection = await this.getProjection(document, position, token);
|
||||
|
||||
if (this.logger.verboseEnabled) {
|
||||
this.logger.logVerbose(`Providing completions for document ${getUriPath(document.uri)} ` +
|
||||
`at location (${position.line}, ${position.character})`);
|
||||
}
|
||||
|
||||
if (!projection) {
|
||||
return { isIncomplete: true, items: [] } as vscode.CompletionList;
|
||||
}
|
||||
|
||||
const provisionalCompletions = await this.provisionalCompletionOrchestrator.tryGetProvisionalCompletions(
|
||||
document.uri,
|
||||
projection,
|
||||
context);
|
||||
if (provisionalCompletions) {
|
||||
return provisionalCompletions;
|
||||
}
|
||||
|
||||
// Not a provisional completion
|
||||
|
||||
const completionList = await RazorCompletionItemProvider.getCompletions(
|
||||
projection.uri,
|
||||
position,
|
||||
projection.position,
|
||||
context.triggerCharacter);
|
||||
return completionList;
|
||||
}
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
/* --------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
* ------------------------------------------------------------------------------------------ */
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import { RazorLanguageServerClient } from './RazorLanguageServerClient';
|
||||
|
||||
export function listenToConfigurationChanges(
|
||||
languageServerClient: RazorLanguageServerClient): vscode.Disposable {
|
||||
return vscode.workspace.onDidChangeConfiguration(event => {
|
||||
if (event.affectsConfiguration('razor.trace')) {
|
||||
razorTraceConfigurationChangeHandler(languageServerClient);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function razorTraceConfigurationChangeHandler(languageServerClient: RazorLanguageServerClient) {
|
||||
const promptText = 'Would you like to restart the Razor Language Server to enable the Razor trace configuration change?';
|
||||
const restartButtonText = 'Restart';
|
||||
|
||||
vscode.window.showInformationMessage(promptText, restartButtonText).then(async result => {
|
||||
if (result !== restartButtonText) {
|
||||
return;
|
||||
}
|
||||
|
||||
await languageServerClient.stop();
|
||||
languageServerClient.updateTraceLevel();
|
||||
await languageServerClient.start();
|
||||
});
|
||||
}
|
|
@ -1,50 +0,0 @@
|
|||
/* --------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
* ------------------------------------------------------------------------------------------ */
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import { getRazorDocumentUri, isRazorHtmlFile } from '../RazorConventions';
|
||||
import { RazorLanguageFeatureBase } from '../RazorLanguageFeatureBase';
|
||||
import { LanguageKind } from '../RPC/LanguageKind';
|
||||
|
||||
export class RazorDefinitionProvider
|
||||
extends RazorLanguageFeatureBase
|
||||
implements vscode.DefinitionProvider {
|
||||
|
||||
public async provideDefinition(
|
||||
document: vscode.TextDocument, position: vscode.Position,
|
||||
token: vscode.CancellationToken) {
|
||||
|
||||
const projection = await this.getProjection(document, position, token);
|
||||
if (!projection || projection.languageKind === LanguageKind.Razor) {
|
||||
return;
|
||||
}
|
||||
|
||||
const definitions = await vscode.commands.executeCommand<vscode.Definition>(
|
||||
'vscode.executeDefinitionProvider',
|
||||
projection.uri,
|
||||
projection.position) as vscode.Location[];
|
||||
|
||||
const result = new Array<vscode.Location>();
|
||||
for (const definition of definitions) {
|
||||
if (projection.languageKind === LanguageKind.Html && isRazorHtmlFile(definition.uri)) {
|
||||
|
||||
// Because the line pragmas for html are generated referencing the projected document
|
||||
// we need to remap their file locations to reference the top level Razor document.
|
||||
const razorFile = getRazorDocumentUri(definition.uri);
|
||||
result.push(new vscode.Location(razorFile, definition.range));
|
||||
|
||||
} else {
|
||||
// This means it is one of the following,
|
||||
// 1. A .razor/.cshtml file (because OmniSharp already remapped the background C# to the original document)
|
||||
// 2. A .cs file
|
||||
// 3. A .html/.js file
|
||||
// In all of these cases, we don't need to remap. So accept it as is and move on.
|
||||
result.push(definition);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
/* --------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
* ------------------------------------------------------------------------------------------ */
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
export interface IReportIssueDataCollectionResult {
|
||||
readonly document: vscode.TextDocument | undefined;
|
||||
readonly logOutput: string;
|
||||
}
|
|
@ -1,48 +0,0 @@
|
|||
/* --------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
* ------------------------------------------------------------------------------------------ */
|
||||
|
||||
import * as os from 'os';
|
||||
import * as vscode from 'vscode';
|
||||
import { RazorLogger } from '../RazorLogger';
|
||||
import { IReportIssueDataCollectionResult } from './IReportIssueDataCollectionResult';
|
||||
|
||||
export class ReportIssueDataCollector {
|
||||
private readonly logMessages: string[] = [];
|
||||
private logOutput = '';
|
||||
private focusRegistration: vscode.Disposable | undefined;
|
||||
private logRegistration: vscode.Disposable | undefined;
|
||||
private lastFocusedRazorDocument: vscode.TextDocument | undefined;
|
||||
constructor(
|
||||
private readonly razorFileFocusChange: vscode.Event<vscode.TextDocument>,
|
||||
private readonly logger: RazorLogger) {
|
||||
}
|
||||
|
||||
public start() {
|
||||
this.focusRegistration = this.razorFileFocusChange((razorDocument) => this.lastFocusedRazorDocument = razorDocument);
|
||||
this.logRegistration = this.logger.onLog(message => this.logMessages.push(message));
|
||||
|
||||
this.logger.outputChannel.show(/* preserveFocus: */ true);
|
||||
this.logger.logAlways('-- Starting Issue Data Collection-- ');
|
||||
}
|
||||
|
||||
public stop() {
|
||||
this.logger.logAlways('-- Stopping Issue Data Collection-- ');
|
||||
this.logOutput = this.logMessages.join(os.EOL);
|
||||
this.logMessages.length = 0;
|
||||
if (this.focusRegistration) {
|
||||
this.focusRegistration.dispose();
|
||||
}
|
||||
if (this.logRegistration) {
|
||||
this.logRegistration.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public collect(): IReportIssueDataCollectionResult {
|
||||
return {
|
||||
document: this.lastFocusedRazorDocument,
|
||||
logOutput: this.logOutput,
|
||||
};
|
||||
}
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { RazorDocumentManager } from '../Document/RazorDocumentManager';
|
||||
import { RazorLogger } from '../RazorLogger';
|
||||
import { api } from '../vscodeAdapter';
|
||||
import * as vscode from '../vscodeAdapter';
|
||||
import { ReportIssueCreator } from './ReportIssueCreator';
|
||||
import { ReportIssueDataCollectorFactory } from './ReportIssueDataCollectorFactory';
|
||||
import { ReportIssuePanel } from './ReportIssuePanel';
|
||||
|
||||
export class ReportIssueCommand {
|
||||
private readonly issuePanel: ReportIssuePanel;
|
||||
private readonly issueCreator: ReportIssueCreator;
|
||||
private readonly dataCollectorFactory: ReportIssueDataCollectorFactory;
|
||||
|
||||
constructor(
|
||||
private readonly vscodeApi: api,
|
||||
documentManager: RazorDocumentManager,
|
||||
logger: RazorLogger) {
|
||||
this.dataCollectorFactory = new ReportIssueDataCollectorFactory(logger);
|
||||
this.issueCreator = new ReportIssueCreator(this.vscodeApi, documentManager);
|
||||
this.issuePanel = new ReportIssuePanel(this.dataCollectorFactory, this.issueCreator, logger);
|
||||
}
|
||||
|
||||
public register() {
|
||||
const registrations: vscode.Disposable[] = [];
|
||||
registrations.push(
|
||||
this.dataCollectorFactory.register(),
|
||||
this.vscodeApi.commands.registerCommand('razor.reportIssue', () => this.issuePanel.show()));
|
||||
if (this.vscodeApi.window.registerWebviewPanelSerializer) {
|
||||
registrations.push(this.vscodeApi.window.registerWebviewPanelSerializer(ReportIssuePanel.viewType, {
|
||||
deserializeWebviewPanel: async (panel: vscode.WebviewPanel) => {
|
||||
await this.issuePanel.revive(panel);
|
||||
},
|
||||
}));
|
||||
}
|
||||
|
||||
return this.vscodeApi.Disposable.from(...registrations);
|
||||
}
|
||||
}
|
|
@ -1,267 +0,0 @@
|
|||
/* --------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
* ------------------------------------------------------------------------------------------ */
|
||||
|
||||
import * as cp from 'child_process';
|
||||
import * as os from 'os';
|
||||
import { IRazorDocument } from '../Document/IRazorDocument';
|
||||
import { IRazorDocumentManager } from '../Document/IRazorDocumentManager';
|
||||
import { razorExtensionId } from '../RazorExtensionId';
|
||||
import * as vscode from '../vscodeAdapter';
|
||||
import { IReportIssueDataCollectionResult } from './IReportIssueDataCollectionResult';
|
||||
|
||||
export class ReportIssueCreator {
|
||||
constructor(
|
||||
private readonly vscodeApi: vscode.api,
|
||||
private readonly documentManager: IRazorDocumentManager) {
|
||||
}
|
||||
|
||||
public async create(collectionResult: IReportIssueDataCollectionResult) {
|
||||
let razorContent: string;
|
||||
let csharpContent: string;
|
||||
let htmlContent: string;
|
||||
|
||||
if (collectionResult.document) {
|
||||
razorContent = await this.getRazor(collectionResult.document);
|
||||
|
||||
const razorDocument = await this.documentManager.getDocument(collectionResult.document.uri);
|
||||
csharpContent = await this.getProjectedCSharp(razorDocument);
|
||||
htmlContent = await this.getProjectedHtml(razorDocument);
|
||||
} else {
|
||||
razorContent = 'Non Razor file as active document';
|
||||
csharpContent = 'Could not determine CSharp content';
|
||||
htmlContent = 'Could not determine Html content';
|
||||
}
|
||||
|
||||
const razorExtensionVersion = this.getExtensionVersion();
|
||||
let dotnetInfo = '';
|
||||
try {
|
||||
dotnetInfo = await this.getDotnetInfo();
|
||||
} catch (error) {
|
||||
dotnetInfo = `A valid dotnet installation could not be found: ${error}`;
|
||||
}
|
||||
const extensionTable = this.generateExtensionTable();
|
||||
|
||||
const sanitizedLogOutput = this.sanitize(collectionResult.logOutput);
|
||||
const sanitizedRazorContent = this.sanitize(razorContent);
|
||||
const sanitizedCSharpContent = this.sanitize(csharpContent);
|
||||
const sanitizedHtmlContent = this.sanitize(htmlContent);
|
||||
const sanitizedDotnetInfo = this.sanitize(dotnetInfo);
|
||||
return `## Is this a Bug or Feature request?:
|
||||
Bug
|
||||
|
||||
## Steps to reproduce
|
||||
------------------- Please fill in this section -------------------------
|
||||
|
||||
## Description of the problem:
|
||||
------------------- Please fill in this section -------------------------
|
||||
|
||||
Expected behavior:
|
||||
|
||||
Actual behavior:
|
||||
|
||||
## Logs
|
||||
|
||||
#### OmniSharp
|
||||
------------------- Please fill in this section -------------------------
|
||||
To find the OmniSharp log, open VS Code's "Output" pane, then in the dropdown choose "OmniSharp Log".
|
||||
|
||||
#### Razor
|
||||
<details><summary>Expand</summary>
|
||||
<p>
|
||||
|
||||
\`\`\`
|
||||
${sanitizedLogOutput}
|
||||
\`\`\`
|
||||
|
||||
</p>
|
||||
</details>
|
||||
|
||||
## Workspace information
|
||||
|
||||
#### Razor document:
|
||||
<details><summary>Expand</summary>
|
||||
<p>
|
||||
|
||||
\`\`\`Razor
|
||||
${sanitizedRazorContent}
|
||||
\`\`\`
|
||||
|
||||
</p>
|
||||
</details>
|
||||
|
||||
#### Projected CSharp document:
|
||||
<details><summary>Expand</summary>
|
||||
<p>
|
||||
|
||||
\`\`\`C#
|
||||
${sanitizedCSharpContent}
|
||||
\`\`\`
|
||||
|
||||
</p>
|
||||
</details>
|
||||
|
||||
#### Projected Html document:
|
||||
<details><summary>Expand</summary>
|
||||
<p>
|
||||
|
||||
\`\`\`Html
|
||||
${sanitizedHtmlContent}
|
||||
\`\`\`
|
||||
|
||||
</p>
|
||||
</details>
|
||||
|
||||
## Machine information
|
||||
|
||||
|
||||
**VSCode version**: ${this.vscodeApi.version}
|
||||
**Razor.VSCode version**: ${razorExtensionVersion}
|
||||
#### \`dotnet --info\`
|
||||
|
||||
<details><summary>Expand</summary>
|
||||
<p>
|
||||
|
||||
\`\`\`
|
||||
${sanitizedDotnetInfo}
|
||||
\`\`\`
|
||||
|
||||
</p>
|
||||
</details>
|
||||
|
||||
#### Extensions
|
||||
<details><summary>Expand</summary>
|
||||
<p>
|
||||
|
||||
${extensionTable}
|
||||
|
||||
</p>
|
||||
</details>`;
|
||||
}
|
||||
|
||||
// Protected for testing
|
||||
protected sanitize(content: string) {
|
||||
const user = process.env.USERNAME === undefined ? process.env.USER : process.env.USERNAME;
|
||||
|
||||
if (user === undefined) {
|
||||
// Couldn't determine user, therefore can't truly sanitize the content.
|
||||
return content;
|
||||
}
|
||||
|
||||
const replacer = new RegExp(user, 'g');
|
||||
const sanitizedContent = content.replace(replacer, 'anonymous');
|
||||
return sanitizedContent;
|
||||
}
|
||||
|
||||
// Protected for testing
|
||||
protected async getRazor(document: vscode.TextDocument) {
|
||||
const content = document.getText();
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
// Protected for testing
|
||||
protected async getProjectedCSharp(razorDocument: IRazorDocument) {
|
||||
let csharpContent = `////////////////////// Projected CSharp as seen by extension ///////////////////////
|
||||
${razorDocument.csharpDocument.getContent()}
|
||||
|
||||
|
||||
////////////////////// Projected CSharp as seen by VSCode ///////////////////////`;
|
||||
|
||||
try {
|
||||
const csharpTextDocument = await this.vscodeApi.workspace.openTextDocument(razorDocument.csharpDocument.uri);
|
||||
if (csharpTextDocument) {
|
||||
csharpContent = `${csharpContent}
|
||||
${csharpTextDocument.getText()}`;
|
||||
} else {
|
||||
csharpContent = `${csharpContent}
|
||||
Unable to resolve VSCode's version of CSharp`;
|
||||
}
|
||||
} catch (e) {
|
||||
csharpContent = `${csharpContent}
|
||||
Unable to resolve VSCode's version of CSharp`;
|
||||
}
|
||||
|
||||
return csharpContent;
|
||||
}
|
||||
|
||||
// Protected for testing
|
||||
protected async getProjectedHtml(razorDocument: IRazorDocument) {
|
||||
let htmlContent = `////////////////////// Projected Html as seen by extension ///////////////////////
|
||||
${razorDocument.htmlDocument.getContent()}
|
||||
|
||||
|
||||
////////////////////// Projected Html as seen by VSCode ///////////////////////`;
|
||||
|
||||
try {
|
||||
const htmlTextDocument = await this.vscodeApi.workspace.openTextDocument(razorDocument.htmlDocument.uri);
|
||||
if (htmlTextDocument) {
|
||||
htmlContent = `${htmlContent}
|
||||
${htmlTextDocument.getText()}`;
|
||||
} else {
|
||||
htmlContent = `${htmlContent}
|
||||
Unable to resolve VSCode's version of Html`;
|
||||
}
|
||||
} catch (e) {
|
||||
htmlContent = `${htmlContent}
|
||||
Unable to resolve VSCode's version of Html`;
|
||||
}
|
||||
|
||||
return htmlContent;
|
||||
}
|
||||
|
||||
// Protected for testing
|
||||
protected getExtensionVersion(): string {
|
||||
const extension = this.vscodeApi.extensions.getExtension(razorExtensionId);
|
||||
if (!extension) {
|
||||
return 'Unable to find Razor extension version.';
|
||||
}
|
||||
return extension.packageJSON.version;
|
||||
}
|
||||
|
||||
// Protected for testing
|
||||
protected getInstalledExtensions() {
|
||||
const extensions: Array<vscode.Extension<any>> = this.vscodeApi.extensions.all
|
||||
.filter(extension => extension.packageJSON.isBuiltin === false);
|
||||
|
||||
return extensions.sort((a, b) =>
|
||||
a.packageJSON.name.toLowerCase().localeCompare(b.packageJSON.name.toLowerCase()));
|
||||
}
|
||||
|
||||
// Protected for testing
|
||||
protected generateExtensionTable() {
|
||||
const extensions = this.getInstalledExtensions();
|
||||
if (extensions.length <= 0) {
|
||||
return 'none';
|
||||
}
|
||||
|
||||
const tableHeader = `|Extension|Author|Version|${os.EOL}|---|---|---|`;
|
||||
const table = extensions.map(
|
||||
(e) => `|${e.packageJSON.name}|${e.packageJSON.publisher}|${e.packageJSON.version}|`).join(os.EOL);
|
||||
|
||||
const extensionTable = `
|
||||
${tableHeader}${os.EOL}${table};
|
||||
`;
|
||||
|
||||
return extensionTable;
|
||||
}
|
||||
|
||||
private getDotnetInfo(): Promise<string> {
|
||||
return new Promise<string>((resolve, reject) => {
|
||||
try {
|
||||
cp.exec('dotnet --info', { cwd: process.cwd(), maxBuffer: 500 * 1024 }, (error, stdout, stderr) => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else if (stderr && stderr.length > 0) {
|
||||
reject(error);
|
||||
} else {
|
||||
resolve(stdout);
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1,46 +0,0 @@
|
|||
/* --------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
* ------------------------------------------------------------------------------------------ */
|
||||
|
||||
import * as os from 'os';
|
||||
import * as vscode from 'vscode';
|
||||
import { RazorLogger } from '../RazorLogger';
|
||||
import { IReportIssueDataCollectionResult } from './IReportIssueDataCollectionResult';
|
||||
|
||||
export class ReportIssueDataCollector {
|
||||
private readonly logMessages: string[] = [];
|
||||
private logOutput = '';
|
||||
private focusRegistration: vscode.Disposable | undefined;
|
||||
private logRegistration: vscode.Disposable | undefined;
|
||||
private lastFocusedRazorDocument: vscode.TextDocument | undefined;
|
||||
constructor(
|
||||
private readonly razorFileFocusChange: vscode.Event<vscode.TextDocument>,
|
||||
private readonly logger: RazorLogger) {
|
||||
|
||||
this.focusRegistration = this.razorFileFocusChange((razorDocument) => this.lastFocusedRazorDocument = razorDocument);
|
||||
this.logRegistration = this.logger.onLog(message => this.logMessages.push(message));
|
||||
|
||||
this.logger.outputChannel.show(/* preserveFocus: */ true);
|
||||
this.logger.logAlways('-- Starting Issue Data Collection-- ');
|
||||
}
|
||||
|
||||
public stop() {
|
||||
this.logger.logAlways('-- Stopping Issue Data Collection-- ');
|
||||
this.logOutput = this.logMessages.join(os.EOL);
|
||||
this.logMessages.length = 0;
|
||||
if (this.focusRegistration) {
|
||||
this.focusRegistration.dispose();
|
||||
}
|
||||
if (this.logRegistration) {
|
||||
this.logRegistration.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public collect(): IReportIssueDataCollectionResult {
|
||||
return {
|
||||
document: this.lastFocusedRazorDocument,
|
||||
logOutput: this.logOutput,
|
||||
};
|
||||
}
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
/* --------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
* ------------------------------------------------------------------------------------------ */
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import { RazorLanguage } from '../RazorLanguage';
|
||||
import { RazorLogger } from '../RazorLogger';
|
||||
import { ReportIssueDataCollector } from './ReportIssueDataCollector';
|
||||
|
||||
export class ReportIssueDataCollectorFactory {
|
||||
private onRazorDocumentFocusedEmitter = new vscode.EventEmitter<vscode.TextDocument>();
|
||||
|
||||
constructor(private readonly logger: RazorLogger) {
|
||||
this.onRazorDocumentFocusedEmitter = new vscode.EventEmitter<vscode.TextDocument>();
|
||||
}
|
||||
|
||||
public register() {
|
||||
return vscode.window.onDidChangeActiveTextEditor((newEditor) => {
|
||||
if (newEditor && RazorLanguage.fileExtensions.some(ext => newEditor.document.fileName.endsWith(ext))) {
|
||||
this.onRazorDocumentFocusedEmitter.fire(newEditor.document);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public create() {
|
||||
const collector = new ReportIssueDataCollector(this.onRazorDocumentFocusedEmitter.event, this.logger);
|
||||
return collector;
|
||||
}
|
||||
}
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче