diff --git a/Directory.Packages.props b/Directory.Packages.props
index f29eafb..a3cd44b 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -13,24 +13,27 @@
-
-
+
+
-
-
-
-
-
-
+
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
+
+
+
-
+
@@ -41,7 +44,8 @@
-
-
-
+
+
+
+
diff --git a/src/SharpIDE.Application/Features/Analysis/RoslynAnalysis.cs b/src/SharpIDE.Application/Features/Analysis/RoslynAnalysis.cs
index ded0fb6..53a530c 100644
--- a/src/SharpIDE.Application/Features/Analysis/RoslynAnalysis.cs
+++ b/src/SharpIDE.Application/Features/Analysis/RoslynAnalysis.cs
@@ -1,4 +1,5 @@
using System.Collections.Immutable;
+using System.Composition.Hosting;
using System.Diagnostics;
using Ardalis.GuardClauses;
using Microsoft.CodeAnalysis;
@@ -9,6 +10,7 @@ using Microsoft.CodeAnalysis.CodeRefactorings;
using Microsoft.CodeAnalysis.Completion;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.MSBuild;
+using Microsoft.CodeAnalysis.Remote.Razor.ProjectSystem;
using Microsoft.CodeAnalysis.Text;
using NuGet.Packaging;
using SharpIDE.Application.Features.SolutionDiscovery;
@@ -20,6 +22,7 @@ namespace SharpIDE.Application.Features.Analysis;
public static class RoslynAnalysis
{
public static MSBuildWorkspace? _workspace;
+ private static RemoteSnapshotManager? _snapshotManager;
private static SharpIdeSolutionModel? _sharpIdeSolutionModel;
private static HashSet _codeFixProviders = [];
private static HashSet _codeRefactoringProviders = [];
@@ -45,10 +48,18 @@ public static class RoslynAnalysis
var timer = Stopwatch.StartNew();
if (_workspace is null)
{
- // is this hostServices necessary? test without it - just getting providers from assemblies instead
- var host = MefHostServices.Create(MefHostServices.DefaultAssemblies);
- _workspace ??= MSBuildWorkspace.Create(host);
+ var configuration = new ContainerConfiguration()
+ .WithAssemblies(MefHostServices.DefaultAssemblies)
+ .WithAssembly(typeof(RemoteSnapshotManager).Assembly);
+
+ // TODO: dispose container at some point?
+ var container = configuration.CreateContainer();
+
+ var host = MefHostServices.Create(container);
+ _workspace = MSBuildWorkspace.Create(host);
_workspace.RegisterWorkspaceFailedHandler(o => throw new InvalidOperationException($"Workspace failed: {o.Diagnostic.Message}"));
+ var snapshotManager = container.GetExports().FirstOrDefault();
+ _snapshotManager = snapshotManager;
}
var solution = await _workspace.OpenSolutionAsync(_sharpIdeSolutionModel.FilePath, new Progress());
timer.Stop();
@@ -57,7 +68,6 @@ public static class RoslynAnalysis
foreach (var assembly in MefHostServices.DefaultAssemblies)
{
- //var assembly = analyzer.GetAssembly();
var fixers = CodeFixProviderLoader.LoadCodeFixProviders([assembly], LanguageNames.CSharp);
_codeFixProviders.AddRange(fixers);
var refactoringProviders = CodeRefactoringProviderLoader.LoadCodeRefactoringProviders([assembly], LanguageNames.CSharp);
@@ -137,6 +147,7 @@ public static class RoslynAnalysis
var cancellationToken = CancellationToken.None;
var project = _workspace!.CurrentSolution.Projects.Single(s => s.FilePath == ((IChildSharpIdeNode)fileModel).GetNearestProjectNode()!.FilePath);
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));
@@ -155,6 +166,7 @@ public static class RoslynAnalysis
{
await _solutionLoadedTcs.Task;
var cancellationToken = CancellationToken.None;
+ var timer = Stopwatch.StartNew();
var sharpIdeProjectModel = ((IChildSharpIdeNode) fileModel).GetNearestProjectNode()!;
var project = _workspace!.CurrentSolution.Projects.Single(s => s.FilePath == sharpIdeProjectModel!.FilePath);
if (!fileModel.Name.EndsWith(".razor", StringComparison.OrdinalIgnoreCase))
@@ -162,20 +174,23 @@ public static class RoslynAnalysis
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 razorProjectSnapshot = _snapshotManager!.GetSnapshot(project);
+ var documentSnapshot = razorProjectSnapshot.GetDocument(razorDocument);
+
+ var razorCodeDocument = await razorProjectSnapshot.GetRequiredCodeDocumentAsync(documentSnapshot, cancellationToken);
+ var razorCSharpDocument = razorCodeDocument.GetRequiredCSharpDocument();
+ var generatedDocument = await razorProjectSnapshot.GetRequiredGeneratedDocumentAsync(documentSnapshot, cancellationToken);
+ var generatedDocSyntaxRoot = await generatedDocument.GetSyntaxRootAsync(cancellationToken);
+ //var razorCsharpText = razorCSharpDocument.Text.ToString();
+ //var razorSyntaxRoot = razorCodeDocument.GetRequiredSyntaxRoot();
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 (razorSpans, sourceMappings) = RazorAccessors.GetSpansAndMappingsForRazorCodeDocument(razorCodeDocument, razorCSharpDocument);
+
+ var classifiedSpans = await Classifier.GetClassifiedSpansAsync(generatedDocument, generatedDocSyntaxRoot!.FullSpan, cancellationToken);
var roslynMappedSpans = classifiedSpans.Select(s =>
{
var genSpan = s.TextSpan;
@@ -205,11 +220,13 @@ public static class RoslynAnalysis
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.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();
-
+ timer.Stop();
+ Console.WriteLine($"RoslynAnalysis: Razor syntax highlighting for {fileModel.Name} took {timer.ElapsedMilliseconds}ms");
return razorSpans;
}
diff --git a/src/SharpIDE.Application/SharpIDE.Application.csproj b/src/SharpIDE.Application/SharpIDE.Application.csproj
index 41ee303..2a89e16 100644
--- a/src/SharpIDE.Application/SharpIDE.Application.csproj
+++ b/src/SharpIDE.Application/SharpIDE.Application.csproj
@@ -7,6 +7,14 @@
enable
+
+
+
+
+
+
+
+
@@ -16,9 +24,12 @@
+
+
+
diff --git a/src/SharpIDE.Godot/CustomSyntaxHighlighter.cs b/src/SharpIDE.Godot/CustomSyntaxHighlighter.cs
index 51440d4..2d03401 100644
--- a/src/SharpIDE.Godot/CustomSyntaxHighlighter.cs
+++ b/src/SharpIDE.Godot/CustomSyntaxHighlighter.cs
@@ -139,6 +139,7 @@ public partial class CustomHighlighter : SyntaxHighlighter
"method name" => new Color("dcdcaa"),
"extension method name" => new Color("dcdcaa"),
"property name" => new Color("dcdcdc"),
+ "field name" => new Color("dcdcdc"),
"static symbol" => new Color("dcdcaa"),
"parameter name" => new Color("9cdcfe"),
"local name" => new Color("9cdcfe"),
@@ -150,7 +151,7 @@ public partial class CustomHighlighter : SyntaxHighlighter
// Misc
"excluded code" => new Color("a9a9a9"),
- _ => new Color("dcdcdc")
+ _ => new Color("f27718") // orange, warning color for unhandled classifications
};
}
}
diff --git a/src/SharpIDE.Godot/IdeRoot.cs b/src/SharpIDE.Godot/IdeRoot.cs
index febdda1..344d64c 100644
--- a/src/SharpIDE.Godot/IdeRoot.cs
+++ b/src/SharpIDE.Godot/IdeRoot.cs
@@ -96,7 +96,7 @@ public partial class IdeRoot : Control
var infraProject = solutionModel.AllProjects.Single(s => s.Name == "Infrastructure");
var diFile = infraProject.Files.Single(s => s.Name == "DependencyInjection.cs");
- await this.InvokeAsync(async () => await _sharpIdeCodeEdit.SetSharpIdeFile(diFile));
+ await this.InvokeDeferredAsync(async () => await _sharpIdeCodeEdit.SetSharpIdeFile(diFile));
//var runnableProject = solutionModel.AllProjects.First(s => s.IsRunnable);
//await this.InvokeAsync(() => _runPanel.NewRunStarted(runnableProject));
diff --git a/src/SharpIDE.Godot/SharpIdeCodeEdit.cs b/src/SharpIDE.Godot/SharpIdeCodeEdit.cs
index d64cc38..844c279 100644
--- a/src/SharpIDE.Godot/SharpIdeCodeEdit.cs
+++ b/src/SharpIDE.Godot/SharpIdeCodeEdit.cs
@@ -162,6 +162,7 @@ public partial class SharpIdeCodeEdit : CodeEdit
});
}
+ // TODO: Ensure not running on UI thread
public async Task SetSharpIdeFile(SharpIdeFile file)
{
_currentFile = file;
@@ -169,11 +170,12 @@ public partial class SharpIdeCodeEdit : CodeEdit
_fileChangingSuppressBreakpointToggleEvent = true;
SetText(fileContents);
_fileChangingSuppressBreakpointToggleEvent = false;
- var syntaxHighlighting = await RoslynAnalysis.GetDocumentSyntaxHighlighting(_currentFile);
- var razorSyntaxHighlighting = await RoslynAnalysis.GetRazorDocumentSyntaxHighlighting(_currentFile);
- SetSyntaxHighlightingModel(syntaxHighlighting, razorSyntaxHighlighting);
- var diagnostics = await RoslynAnalysis.GetDocumentDiagnostics(_currentFile);
- SetDiagnosticsModel(diagnostics);
+ var syntaxHighlighting = RoslynAnalysis.GetDocumentSyntaxHighlighting(_currentFile);
+ var razorSyntaxHighlighting = RoslynAnalysis.GetRazorDocumentSyntaxHighlighting(_currentFile);
+ var diagnostics = RoslynAnalysis.GetDocumentDiagnostics(_currentFile);
+ await Task.WhenAll(syntaxHighlighting, razorSyntaxHighlighting);
+ SetSyntaxHighlightingModel(await syntaxHighlighting, await razorSyntaxHighlighting);
+ SetDiagnosticsModel(await diagnostics);
}
public void UnderlineRange(int line, int caretStartCol, int caretEndCol, Color color, float thickness = 1.5f)
diff --git a/src/SharpIDE.RazorAccess/RazorAccessors.cs b/src/SharpIDE.RazorAccess/RazorAccessors.cs
index 1f00799..00bf702 100644
--- a/src/SharpIDE.RazorAccess/RazorAccessors.cs
+++ b/src/SharpIDE.RazorAccess/RazorAccessors.cs
@@ -1,37 +1,55 @@
extern alias WorkspaceAlias;
using System.Collections.Immutable;
using Microsoft.AspNetCore.Razor.Language;
-using Microsoft.CodeAnalysis.Text;
using RazorCodeDocumentExtensions = WorkspaceAlias::Microsoft.AspNetCore.Razor.Language.RazorCodeDocumentExtensions;
namespace SharpIDE.RazorAccess;
public static class RazorAccessors
{
- public static (ImmutableArray, SourceText Text, List) GetClassifiedSpans(SourceText sourceText, SourceText importsSourceText, string razorDocumentFilePath, string projectDirectory)
+ //private static RazorProjectEngine? _razorProjectEngine;
+
+ public static (ImmutableArray, List) GetSpansAndMappingsForRazorCodeDocument(RazorCodeDocument razorCodeDocument, RazorCSharpDocument razorCSharpDocument)
{
-
- var razorSourceDocument = RazorSourceDocument.Create(sourceText.ToString(), razorDocumentFilePath);
- var importsRazorSourceDocument = RazorSourceDocument.Create(importsSourceText.ToString(), "_Imports.razor");
-
- var projectEngine = RazorProjectEngine.Create(RazorConfiguration.Default, RazorProjectFileSystem.Create(projectDirectory),
- builder => { /* configure features if needed */ });
-
- //var razorCodeDocument = projectEngine.Process(razorSourceDocument, RazorFileKind.Component, [], []);
- var razorCodeDocument = projectEngine.ProcessDesignTime(razorSourceDocument, RazorFileKind.Component, [importsRazorSourceDocument], []);
- var razorCSharpDocument = razorCodeDocument.GetRequiredCSharpDocument();
- //var generatedSourceText = razorCSharpDocument.Text;
-
- //var filePath = razorCodeDocument.Source.FilePath.AssumeNotNull();
- //var razorSourceText = razorCodeDocument.Source.Text;
var razorSpans = RazorCodeDocumentExtensions.GetClassifiedSpans(razorCodeDocument);
-
- //var sharpIdeSpans = MemoryMarshal.Cast(razorSpans);
var sharpIdeSpans = razorSpans.Select(s => new SharpIdeRazorClassifiedSpan(s.Span.ToSharpIdeSourceSpan(), s.Kind.ToSharpIdeSpanKind())).ToList();
- return (sharpIdeSpans.ToImmutableArray(), razorCSharpDocument.Text, razorCSharpDocument.SourceMappings.Select(s => s.ToSharpIdeSourceMapping()).ToList());
+ var result = (sharpIdeSpans.ToImmutableArray(), razorCSharpDocument.SourceMappings.Select(s => s.ToSharpIdeSourceMapping()).ToList());
+ return result;
}
+ public static ImmutableArray GetClassifiedSpansForRazorCodeDocument(RazorCodeDocument razorCodeDocument)
+ {
+ var razorSpans = RazorCodeDocumentExtensions.GetClassifiedSpans(razorCodeDocument);
+ return razorSpans;
+ }
+
+ // public static (ImmutableArray, SourceText Text, List) GetClassifiedSpans(SourceText sourceText, SourceText importsSourceText, string razorDocumentFilePath, string projectDirectory)
+ // {
+ // var razorSourceDocument = RazorSourceDocument.Create(sourceText.ToString(), razorDocumentFilePath);
+ // var importsRazorSourceDocument = RazorSourceDocument.Create(importsSourceText.ToString(), "_Imports.razor");
+ //
+ // var razorProjectFileSystem = RazorProjectFileSystem.Create(projectDirectory);
+ // _razorProjectEngine ??= RazorProjectEngine.Create(RazorConfiguration.Default, razorProjectFileSystem,
+ // builder => { /* configure features if needed */ });
+ // //var projectItem = razorProjectFileSystem.GetItem(razorDocumentFilePath, RazorFileKind.Component);
+ //
+ // //var razorCodeDocument = projectEngine.Process(razorSourceDocument, RazorFileKind.Component, [], []);
+ // var razorCodeDocument = _razorProjectEngine.Process(razorSourceDocument, RazorFileKind.Component, [importsRazorSourceDocument], []);
+ // var razorCSharpDocument = razorCodeDocument.GetRequiredCSharpDocument();
+ // //var generatedSourceText = razorCSharpDocument.Text;
+ //
+ // //var filePath = razorCodeDocument.Source.FilePath.AssumeNotNull();
+ // //var razorSourceText = razorCodeDocument.Source.Text;
+ // var razorSpans = RazorCodeDocumentExtensions.GetClassifiedSpans(razorCodeDocument);
+ //
+ // //var sharpIdeSpans = MemoryMarshal.Cast(razorSpans);
+ // var sharpIdeSpans = razorSpans.Select(s => new SharpIdeRazorClassifiedSpan(s.Span.ToSharpIdeSourceSpan(), s.Kind.ToSharpIdeSpanKind())).ToList();
+ //
+ // var result = (sharpIdeSpans.ToImmutableArray(), razorCSharpDocument.Text, razorCSharpDocument.SourceMappings.Select(s => s.ToSharpIdeSourceMapping()).ToList());
+ // return result;
+ // }
+
// public static bool TryGetMappedSpans(
// TextSpan span,
// SourceText source,
@@ -59,14 +77,4 @@ public static class RazorAccessors
// linePositionSpan = new LinePositionSpan();
// return false;
// }
-
- // ///
- // /// Wrapper to avoid s in the caller during JITing
- // /// even though the method is not actually called.
- // ///
- // [MethodImpl(MethodImplOptions.NoInlining)]
- // private static object GetFileKindFromPath(string filePath)
- // {
- // return FileKinds.GetFileKindFromPath(filePath);
- // }
}
diff --git a/src/SharpIDE.RazorAccess/SharpIDE.RazorAccess.csproj b/src/SharpIDE.RazorAccess/SharpIDE.RazorAccess.csproj
index 576ea02..41f3f9f 100644
--- a/src/SharpIDE.RazorAccess/SharpIDE.RazorAccess.csproj
+++ b/src/SharpIDE.RazorAccess/SharpIDE.RazorAccess.csproj
@@ -19,7 +19,7 @@
-
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
@@ -28,7 +28,7 @@
-
+