diff --git a/src/SharpIDE.Application/Features/Analysis/RoslynAnalysis.cs b/src/SharpIDE.Application/Features/Analysis/RoslynAnalysis.cs index e6e365e..621d4d5 100644 --- a/src/SharpIDE.Application/Features/Analysis/RoslynAnalysis.cs +++ b/src/SharpIDE.Application/Features/Analysis/RoslynAnalysis.cs @@ -339,7 +339,7 @@ public static class RoslynAnalysis 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)) + if (fileModel.IsRazorFile is false) { return []; //throw new InvalidOperationException("File is not a .razor file"); @@ -444,7 +444,7 @@ public static class RoslynAnalysis using var _ = SharpIdeOtel.Source.StartActivity($"{nameof(RoslynAnalysis)}.{nameof(GetDocumentSyntaxHighlighting)}"); await _solutionLoadedTcs.Task; var project = _workspace!.CurrentSolution.Projects.Single(s => s.FilePath == ((IChildSharpIdeNode)fileModel).GetNearestProjectNode()!.FilePath); - if (fileModel.Name.EndsWith(".cs", StringComparison.OrdinalIgnoreCase) is false) + if (fileModel.IsCsharpFile is false) { //throw new InvalidOperationException("File is not a .cs"); return []; @@ -603,7 +603,10 @@ public static class RoslynAnalysis public static async Task<(ISymbol?, LinePositionSpan?)> LookupSymbol(SharpIdeFile fileModel, LinePosition linePosition) { await _solutionLoadedTcs.Task; - var (symbol, linePositionSpan) = fileModel.IsRazorFile ? await LookupSymbolInRazor(fileModel, linePosition) : await LookupSymbolInCs(fileModel, linePosition); + var (symbol, linePositionSpan) = + fileModel.IsRazorFile ? await LookupSymbolInRazor(fileModel, linePosition) + : fileModel.IsCsharpFile ? await LookupSymbolInCs(fileModel, linePosition) + : (null, null); return (symbol, linePositionSpan); } diff --git a/src/SharpIDE.Godot/Features/CodeEditor/CustomSyntaxHighlighter.cs b/src/SharpIDE.Godot/Features/CodeEditor/CustomSyntaxHighlighter.cs index fc57c11..7025abf 100644 --- a/src/SharpIDE.Godot/Features/CodeEditor/CustomSyntaxHighlighter.cs +++ b/src/SharpIDE.Godot/Features/CodeEditor/CustomSyntaxHighlighter.cs @@ -1,4 +1,5 @@ -using Godot; +using System.Collections.Immutable; +using Godot; using Godot.Collections; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Classification; @@ -9,11 +10,29 @@ namespace SharpIDE.Godot; public partial class CustomHighlighter : SyntaxHighlighter { private readonly Dictionary _emptyDict = new(); - public HashSet<(FileLinePositionSpan fileSpan, ClassifiedSpan classifiedSpan)> ClassifiedSpans = []; - public HashSet RazorClassifiedSpans = []; + + private System.Collections.Generic.Dictionary> _razorClassifiedSpansByLine = []; + private System.Collections.Generic.Dictionary> _classifiedSpansByLine = []; + + + public void SetHighlightingData(IEnumerable<(FileLinePositionSpan fileSpan, ClassifiedSpan classifiedSpan)> classifiedSpans, IEnumerable razorClassifiedSpans) + { + // separate each line here + var razorSpansForLine = razorClassifiedSpans + .Where(s => s.Span.Length is not 0) + .GroupBy(s => s.Span.LineIndex) + .ToList(); + _razorClassifiedSpansByLine = razorSpansForLine.ToDictionary(g => g.Key, g => g.ToImmutableArray()); + + var spansGroupedByFileSpan = classifiedSpans + .Where(s => s.classifiedSpan.TextSpan.Length is not 0) + .GroupBy(span => span.fileSpan.StartLinePosition.Line) + .ToList(); + _classifiedSpansByLine = spansGroupedByFileSpan.ToDictionary(g => g.Key, g => g.ToImmutableArray()); + } public override Dictionary _GetLineSyntaxHighlighting(int line) { - var highlights = (ClassifiedSpans, RazorClassifiedSpans) switch + var highlights = (_classifiedSpansByLine, _razorClassifiedSpansByLine) switch { ({ Count: 0 }, { Count: 0 }) => _emptyDict, ({ Count: > 0 }, _) => MapClassifiedSpansToHighlights(line), @@ -28,14 +47,12 @@ public partial class CustomHighlighter : SyntaxHighlighter private Dictionary MapRazorClassifiedSpansToHighlights(int line) { var highlights = new Dictionary(); + if (_razorClassifiedSpansByLine.TryGetValue(line, out var razorSpansForLine) is false) return highlights; + + // group by span (start, length matches) + var spansGroupedByFileSpan = razorSpansForLine.GroupBy(span => span.Span); - // Filter spans on the given line, ignore empty spans - var spansForLine = RazorClassifiedSpans - .Where(s => s.Span.LineIndex == line && s.Span.Length is not 0) - .GroupBy(s => s.Span) - .ToList(); - - foreach (var razorSpanGrouping in spansForLine) + foreach (var razorSpanGrouping in spansGroupedByFileSpan) { var spans = razorSpanGrouping.ToList(); if (spans.Count > 2) throw new NotImplementedException("More than 2 classified spans is not supported yet."); @@ -101,10 +118,11 @@ public partial class CustomHighlighter : SyntaxHighlighter private Dictionary MapClassifiedSpansToHighlights(int line) { var highlights = new Dictionary(); + if (_classifiedSpansByLine.TryGetValue(line, out var spansForLine) is false) return highlights; // consider no linq or ZLinq - var spansGroupedByFileSpan = ClassifiedSpans - .Where(s => s.fileSpan.StartLinePosition.Line == line && s.classifiedSpan.TextSpan.Length is not 0) + // group by span (start, length matches) + var spansGroupedByFileSpan = spansForLine .GroupBy(span => span.fileSpan) .Select(group => (fileSpan: group.Key, classifiedSpans: group.Select(s => s.classifiedSpan).ToList())); diff --git a/src/SharpIDE.Godot/Features/CodeEditor/SharpIdeCodeEdit.cs b/src/SharpIDE.Godot/Features/CodeEditor/SharpIdeCodeEdit.cs index f77d1fd..fc59ebf 100644 --- a/src/SharpIDE.Godot/Features/CodeEditor/SharpIdeCodeEdit.cs +++ b/src/SharpIDE.Godot/Features/CodeEditor/SharpIdeCodeEdit.cs @@ -412,8 +412,7 @@ public partial class SharpIdeCodeEdit : CodeEdit private void SetSyntaxHighlightingModel(IEnumerable<(FileLinePositionSpan fileSpan, ClassifiedSpan classifiedSpan)> classifiedSpans, IEnumerable razorClassifiedSpans) { - _syntaxHighlighter.ClassifiedSpans = classifiedSpans.ToHashSet(); - _syntaxHighlighter.RazorClassifiedSpans = razorClassifiedSpans.ToHashSet(); + _syntaxHighlighter.SetHighlightingData(classifiedSpans, razorClassifiedSpans); Callable.From(() => { _syntaxHighlighter.ClearHighlightingCache();