add razor file syntax highlighting
This commit is contained in:
@@ -1,5 +1,4 @@
|
||||
using System.Collections.Immutable;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Diagnostics;
|
||||
using Ardalis.GuardClauses;
|
||||
using Microsoft.CodeAnalysis;
|
||||
@@ -12,9 +11,9 @@ using Microsoft.CodeAnalysis.Host.Mef;
|
||||
using Microsoft.CodeAnalysis.MSBuild;
|
||||
using Microsoft.CodeAnalysis.Text;
|
||||
using NuGet.Packaging;
|
||||
using ObservableCollections;
|
||||
using SharpIDE.Application.Features.SolutionDiscovery;
|
||||
using SharpIDE.Application.Features.SolutionDiscovery.VsPersistence;
|
||||
using SharpIDE.RazorAccess;
|
||||
|
||||
namespace SharpIDE.Application.Features.Analysis;
|
||||
|
||||
@@ -137,7 +136,8 @@ public static class RoslynAnalysis
|
||||
await _solutionLoadedTcs.Task;
|
||||
var cancellationToken = CancellationToken.None;
|
||||
var project = _workspace!.CurrentSolution.Projects.Single(s => s.FilePath == ((IChildSharpIdeNode)fileModel).GetNearestProjectNode()!.FilePath);
|
||||
var document = project.Documents.Single(s => s.FilePath == fileModel.Path);
|
||||
var document = project.Documents.SingleOrDefault(s => s.FilePath == fileModel.Path);
|
||||
if (document is null) return [];
|
||||
//var document = _workspace!.CurrentSolution.GetDocument(fileModel.Path);
|
||||
Guard.Against.Null(document, nameof(document));
|
||||
|
||||
@@ -150,11 +150,80 @@ public static class RoslynAnalysis
|
||||
return result;
|
||||
}
|
||||
|
||||
public record SharpIdeRazorMappedClassifiedSpan(SharpIdeRazorSourceSpan SourceSpanInRazor, string CsharpClassificationType);
|
||||
public static async Task<IEnumerable<SharpIdeRazorClassifiedSpan>> GetRazorDocumentSyntaxHighlighting(SharpIdeFile fileModel)
|
||||
{
|
||||
await _solutionLoadedTcs.Task;
|
||||
var cancellationToken = CancellationToken.None;
|
||||
var sharpIdeProjectModel = ((IChildSharpIdeNode) fileModel).GetNearestProjectNode()!;
|
||||
var project = _workspace!.CurrentSolution.Projects.Single(s => s.FilePath == sharpIdeProjectModel!.FilePath);
|
||||
if (!fileModel.Name.EndsWith(".razor", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return [];
|
||||
//throw new InvalidOperationException("File is not a .razor file");
|
||||
}
|
||||
|
||||
var importsFile = sharpIdeProjectModel.Files.Single(s => s.Name.Equals("_Imports.razor", StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
var razorDocument = project.AdditionalDocuments.Single(s => s.FilePath == fileModel.Path);
|
||||
var importsDocument = project.AdditionalDocuments.Single(s => s.FilePath == importsFile.Path);
|
||||
|
||||
var razorText = await razorDocument.GetTextAsync(cancellationToken);
|
||||
var importsText = await importsDocument.GetTextAsync(cancellationToken);
|
||||
var (razorSpans, razorGeneratedSourceText, sourceMappings) = RazorAccessors.GetClassifiedSpans(razorText, importsText, razorDocument.FilePath!, Path.GetDirectoryName(project.FilePath!)!);
|
||||
|
||||
var razorGeneratedDocument = project.AddDocument(fileModel.Name + ".g.cs", razorGeneratedSourceText);
|
||||
var razorSyntaxTree = await razorGeneratedDocument.GetSyntaxTreeAsync(cancellationToken);
|
||||
var razorSyntaxRoot = await razorSyntaxTree!.GetRootAsync(cancellationToken);
|
||||
var classifiedSpans = await Classifier.GetClassifiedSpansAsync(razorGeneratedDocument, razorSyntaxRoot.FullSpan, cancellationToken);
|
||||
var roslynMappedSpans = classifiedSpans.Select(s =>
|
||||
{
|
||||
var genSpan = s.TextSpan;
|
||||
var mapping = sourceMappings.SingleOrDefault(m => m.GeneratedSpan.AsTextSpan().IntersectsWith(genSpan));
|
||||
if (mapping != null)
|
||||
{
|
||||
// Translate generated span back to Razor span
|
||||
var offset = genSpan.Start - mapping.GeneratedSpan.AbsoluteIndex;
|
||||
var mappedStart = mapping.OriginalSpan.AbsoluteIndex + offset;
|
||||
var mappedSpan = new TextSpan(mappedStart, genSpan.Length);
|
||||
var sharpIdeSpan = new SharpIdeRazorSourceSpan(
|
||||
mapping.OriginalSpan.FilePath,
|
||||
mappedSpan.Start,
|
||||
razorText.Lines.GetLineFromPosition(mappedSpan.Start).LineNumber,
|
||||
mappedSpan.Start - razorText.Lines.GetLineFromPosition(mappedSpan.Start).Start,
|
||||
mappedSpan.Length,
|
||||
1,
|
||||
mappedSpan.Start - razorText.Lines.GetLineFromPosition(mappedSpan.Start).Start + mappedSpan.Length
|
||||
);
|
||||
|
||||
return new SharpIdeRazorMappedClassifiedSpan(
|
||||
sharpIdeSpan,
|
||||
s.ClassificationType
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
}).Where(s => s is not null).ToList();
|
||||
|
||||
razorSpans = [..razorSpans.Where(s => s.Kind is not SharpIdeRazorSpanKind.Code), ..roslynMappedSpans
|
||||
.Select(s => new SharpIdeRazorClassifiedSpan(s!.SourceSpanInRazor, SharpIdeRazorSpanKind.Code, s.CsharpClassificationType))
|
||||
];
|
||||
razorSpans = razorSpans.OrderBy(s => s.Span.AbsoluteIndex).ToImmutableArray();
|
||||
|
||||
return razorSpans;
|
||||
}
|
||||
|
||||
public static async Task<IEnumerable<(FileLinePositionSpan fileSpan, ClassifiedSpan classifiedSpan)>> GetDocumentSyntaxHighlighting(SharpIdeFile fileModel)
|
||||
{
|
||||
await _solutionLoadedTcs.Task;
|
||||
var cancellationToken = CancellationToken.None;
|
||||
var project = _workspace!.CurrentSolution.Projects.Single(s => s.FilePath == ((IChildSharpIdeNode)fileModel).GetNearestProjectNode()!.FilePath);
|
||||
if (fileModel.Name.EndsWith(".cs", StringComparison.OrdinalIgnoreCase) is false)
|
||||
{
|
||||
//throw new InvalidOperationException("File is not a .cs");
|
||||
return [];
|
||||
}
|
||||
|
||||
var document = project.Documents.Single(s => s.FilePath == fileModel.Path);
|
||||
Guard.Against.Null(document, nameof(document));
|
||||
|
||||
@@ -300,11 +369,18 @@ public static class RoslynAnalysis
|
||||
Guard.Against.NullOrEmpty(newContent, nameof(newContent));
|
||||
|
||||
var project = _workspace!.CurrentSolution.Projects.Single(s => s.FilePath == ((IChildSharpIdeNode)fileModel).GetNearestProjectNode()!.FilePath);
|
||||
var document = project.Documents.Single(s => s.FilePath == fileModel.Path);
|
||||
Guard.Against.Null(document, nameof(document));
|
||||
|
||||
//var updatedDocument = document.WithText(SourceText.From(newContent));
|
||||
var newSolution = _workspace.CurrentSolution.WithDocumentText(document.Id, SourceText.From(newContent));
|
||||
_workspace.TryApplyChanges(newSolution);
|
||||
if (fileModel.IsRazorFile)
|
||||
{
|
||||
var razorDocument = project.AdditionalDocuments.Single(s => s.FilePath == fileModel.Path);
|
||||
var newSolution = _workspace.CurrentSolution.WithAdditionalDocumentText(razorDocument.Id, SourceText.From(newContent));
|
||||
_workspace.TryApplyChanges(newSolution);
|
||||
}
|
||||
else
|
||||
{
|
||||
var document = project.Documents.Single(s => s.FilePath == fileModel.Path);
|
||||
Guard.Against.Null(document, nameof(document));
|
||||
var newSolution = _workspace.CurrentSolution.WithDocumentText(document.Id, SourceText.From(newContent));
|
||||
_workspace.TryApplyChanges(newSolution);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ public class SharpIdeFile : ISharpIdeNode, IChildSharpIdeNode
|
||||
public required IExpandableSharpIdeNode Parent { get; set; }
|
||||
public required string Path { get; set; }
|
||||
public required string Name { get; set; }
|
||||
public bool IsRazorFile => Path.EndsWith(".razor", StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
[SetsRequiredMembers]
|
||||
internal SharpIdeFile(string fullPath, string name, IExpandableSharpIdeNode parent, ConcurrentBag<SharpIdeFile> allFiles)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
@@ -8,22 +8,26 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Diagnostics.NETCore.Client" Version="0.2.645901" PrivateAssets="all" />
|
||||
<PackageReference Include="Microsoft.VisualStudio.Shared.VSCodeDebugProtocol" Version="18.0.10427.1" />
|
||||
<PackageReference Include="Microsoft.Diagnostics.NETCore.Client" PrivateAssets="all" />
|
||||
<PackageReference Include="Microsoft.VisualStudio.Shared.VSCodeDebugProtocol" />
|
||||
<!-- If any Microsoft.Build.*.dll (Excluding Locator) ends up in the output, it will be prioritised for loading by MSBuild Nodes -->
|
||||
<PackageReference Include="Ardalis.GuardClauses" Version="5.0.0" />
|
||||
<PackageReference Include="AsyncReadProcess" Version="1.0.0-preview15" />
|
||||
<PackageReference Include="Microsoft.Build" Version="17.15.0-preview-25459-108" ExcludeAssets="runtime" />
|
||||
<PackageReference Include="Microsoft.Build.Framework" Version="17.15.0-preview-25459-108" ExcludeAssets="runtime" />
|
||||
<PackageReference Include="Microsoft.Build.Locator" Version="1.9.1" />
|
||||
<PackageReference Include="Microsoft.Build.Tasks.Core" Version="17.15.0-preview-25459-108" ExcludeAssets="runtime" />
|
||||
<PackageReference Include="Microsoft.Build.Utilities.Core" Version="17.15.0-preview-25459-108" ExcludeAssets="runtime" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Features" Version="5.0.0-2.25459.108" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="5.0.0-2.25459.108" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.Features" Version="5.0.0-2.25459.108" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.Workspaces.MSBuild" Version="5.0.0-2.25459.108" PrivateAssets="all" />
|
||||
<PackageReference Include="Microsoft.VisualStudio.SolutionPersistence" Version="1.0.52" />
|
||||
<PackageReference Include="NuGet.Protocol" Version="7.0.0-preview.1.46008" />
|
||||
<PackageReference Include="ObservableCollections" Version="3.3.4" />
|
||||
<PackageReference Include="Ardalis.GuardClauses" />
|
||||
<PackageReference Include="AsyncReadProcess" />
|
||||
<PackageReference Include="Microsoft.Build" ExcludeAssets="runtime" />
|
||||
<PackageReference Include="Microsoft.Build.Framework" ExcludeAssets="runtime" />
|
||||
<PackageReference Include="Microsoft.Build.Locator" />
|
||||
<PackageReference Include="Microsoft.Build.Tasks.Core" ExcludeAssets="runtime" />
|
||||
<PackageReference Include="Microsoft.Build.Utilities.Core" ExcludeAssets="runtime" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Features" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.Features" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.Workspaces.MSBuild" PrivateAssets="all" />
|
||||
<PackageReference Include="Microsoft.VisualStudio.SolutionPersistence" />
|
||||
<PackageReference Include="NuGet.Protocol" />
|
||||
<PackageReference Include="ObservableCollections" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\SharpIDE.RazorAccess\SharpIDE.RazorAccess.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
Reference in New Issue
Block a user