document syntax highlighting
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
using System.Diagnostics;
|
||||
using Ardalis.GuardClauses;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.Classification;
|
||||
using Microsoft.CodeAnalysis.CodeActions;
|
||||
using Microsoft.CodeAnalysis.CodeFixes;
|
||||
using Microsoft.CodeAnalysis.CodeRefactorings;
|
||||
@@ -143,6 +144,23 @@ public static class RoslynAnalysis
|
||||
return diagnostics;
|
||||
}
|
||||
|
||||
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);
|
||||
var document = project.Documents.Single(s => s.FilePath == fileModel.Path);
|
||||
Guard.Against.Null(document, nameof(document));
|
||||
|
||||
var syntaxTree = await document.GetSyntaxTreeAsync(cancellationToken);
|
||||
var root = await syntaxTree!.GetRootAsync(cancellationToken);
|
||||
var classifiedSpans = await Classifier.GetClassifiedSpansAsync(document, root.FullSpan, cancellationToken);
|
||||
|
||||
var result = classifiedSpans.Select(s => (syntaxTree.GetMappedLineSpan(s.TextSpan), s));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static async Task<ImmutableArray<CodeAction>> GetCodeFixesAsync(Diagnostic diagnostic)
|
||||
{
|
||||
var cancellationToken = CancellationToken.None;
|
||||
|
||||
@@ -1,30 +1,87 @@
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using Godot;
|
||||
using Godot.Collections;
|
||||
using System.Text.RegularExpressions;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.Classification;
|
||||
|
||||
namespace SharpIDE.Godot;
|
||||
|
||||
public partial class CustomHighlighter : SyntaxHighlighter
|
||||
{
|
||||
public IEnumerable<(FileLinePositionSpan fileSpan, ClassifiedSpan classifiedSpan)> ClassifiedSpans = [];
|
||||
public override Dictionary _GetLineSyntaxHighlighting(int line)
|
||||
{
|
||||
var highlights = MapClassifiedSpansToHighlights(line);
|
||||
|
||||
return highlights;
|
||||
}
|
||||
|
||||
private static readonly StringName ColorStringName = "color";
|
||||
private Dictionary MapClassifiedSpansToHighlights(int line)
|
||||
{
|
||||
var highlights = new Dictionary();
|
||||
var text = GetTextEdit().GetLine(line);
|
||||
|
||||
var regex = new Regex(@"\bTODO\b");
|
||||
var matches = regex.Matches(text);
|
||||
|
||||
foreach (Match match in matches)
|
||||
foreach (var (fileSpan, classifiedSpan) in ClassifiedSpans)
|
||||
{
|
||||
highlights[match.Index] = new Dictionary
|
||||
// Only take spans on the requested line
|
||||
if (fileSpan.StartLinePosition.Line != line)
|
||||
continue;
|
||||
|
||||
if (classifiedSpan.TextSpan.Length == 0)
|
||||
continue; // Skip empty spans
|
||||
|
||||
// Column index of the first character in this span
|
||||
int columnIndex = fileSpan.StartLinePosition.Character;
|
||||
|
||||
// Build the highlight entry
|
||||
var highlightInfo = new Dictionary
|
||||
{
|
||||
{ "color", new Color(1, 0, 0) }, // red
|
||||
{ "underline", true }, // not implemented
|
||||
{ "length", match.Length } // not implemented
|
||||
{ ColorStringName, GetColorForClassification(classifiedSpan.ClassificationType) }
|
||||
};
|
||||
|
||||
highlights[columnIndex] = highlightInfo;
|
||||
}
|
||||
|
||||
return highlights;
|
||||
}
|
||||
|
||||
private Color GetColorForClassification(string classificationType)
|
||||
{
|
||||
return classificationType switch
|
||||
{
|
||||
// Keywords
|
||||
"keyword" => new Color("569cd6"),
|
||||
"keyword - control" => new Color("569cd6"),
|
||||
"preprocessor keyword" => new Color("569cd6"),
|
||||
|
||||
// Literals & comments
|
||||
"string" => new Color("d69d85"),
|
||||
"comment" => new Color("57a64a"),
|
||||
"number" => new Color("b5cea8"),
|
||||
|
||||
// Types (User Types)
|
||||
"class name" => new Color("4ec9b0"),
|
||||
"struct name" => new Color("4ec9b0"),
|
||||
"interface name" => new Color("b8d7a3"),
|
||||
"namespace name" => new Color("dcdcdc"),
|
||||
|
||||
// Identifiers & members
|
||||
"identifier" => new Color("dcdcdc"),
|
||||
"method name" => new Color("dcdcaa"),
|
||||
"extension method name" => new Color("dcdcaa"),
|
||||
"property name" => new Color("dcdcdc"),
|
||||
"static symbol" => new Color("dcdcdc"),
|
||||
"parameter name" => new Color("9cdcfe"),
|
||||
"local name" => new Color("9cdcfe"),
|
||||
|
||||
// Punctuation & operators
|
||||
"operator" => new Color("dcdcdc"),
|
||||
"punctuation" => new Color("dcdcdc"),
|
||||
|
||||
// Misc
|
||||
"excluded code" => new Color("a9a9a9"),
|
||||
|
||||
_ => new Color("dcdcdc")
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,8 +37,11 @@ public partial class IdeRoot : Control
|
||||
var diFile = infraProject.Files.Single(s => s.Name == "DependencyInjection.cs");
|
||||
var fileContents = await File.ReadAllTextAsync(diFile.Path);
|
||||
_sharpIdeCodeEdit.SetText(fileContents);
|
||||
var syntaxHighlighting = await RoslynAnalysis.GetDocumentSyntaxHighlighting(diFile);
|
||||
_sharpIdeCodeEdit.ProvideSyntaxHighlighting(syntaxHighlighting);
|
||||
var diagnostics = await RoslynAnalysis.GetDocumentDiagnostics(diFile);
|
||||
_sharpIdeCodeEdit.ProvideDiagnostics(diagnostics);
|
||||
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
||||
@@ -51,7 +51,10 @@ layout_mode = 2
|
||||
[node name="SharpIdeCodeEdit" type="CodeEdit" parent="VBoxContainer/HBoxContainer/HSplitContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
theme_override_colors/current_line_color = Color(0.0588235, 0.0588235, 0.0588235, 1)
|
||||
theme_override_colors/background_color = Color(0.117647, 0.117647, 0.117647, 1)
|
||||
theme_override_fonts/font = ExtResource("2_rk34b")
|
||||
theme_override_font_sizes/font_size = 18
|
||||
highlight_current_line = true
|
||||
gutters_draw_line_numbers = true
|
||||
code_completion_enabled = true
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using Godot;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.Classification;
|
||||
|
||||
namespace SharpIDE.Godot;
|
||||
|
||||
@@ -12,6 +14,8 @@ public partial class SharpIdeCodeEdit : CodeEdit
|
||||
private int _currentLine;
|
||||
private int _selectionStartCol;
|
||||
private int _selectionEndCol;
|
||||
|
||||
private CustomHighlighter _syntaxHighlighter = new();
|
||||
|
||||
private ImmutableArray<Diagnostic> _diagnostics = [];
|
||||
|
||||
@@ -27,7 +31,7 @@ public partial class SharpIdeCodeEdit : CodeEdit
|
||||
_currentLine = GetCaretLine();
|
||||
GD.Print($"Selection changed to line {_currentLine}, start {_selectionStartCol}, end {_selectionEndCol}");
|
||||
};
|
||||
this.SyntaxHighlighter = new CustomHighlighter();
|
||||
this.SyntaxHighlighter = _syntaxHighlighter;
|
||||
}
|
||||
|
||||
public void UnderlineRange(int line, int caretStartCol, int caretEndCol, Color color, float thickness = 1.5f)
|
||||
@@ -85,6 +89,12 @@ public partial class SharpIdeCodeEdit : CodeEdit
|
||||
{
|
||||
_diagnostics = diagnostics;
|
||||
}
|
||||
public void ProvideSyntaxHighlighting(IEnumerable<(FileLinePositionSpan fileSpan, ClassifiedSpan classifiedSpan)> classifiedSpans)
|
||||
{
|
||||
_syntaxHighlighter.ClassifiedSpans = classifiedSpans;
|
||||
_syntaxHighlighter.UpdateCache(); // not sure if correct
|
||||
QueueRedraw(); // TODO: Not working
|
||||
}
|
||||
|
||||
private void OnCodeFixesRequested()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user