Skip to content
This repository has been archived by the owner on Jul 10, 2024. It is now read-only.

Commit

Permalink
impolement semantictoken feature
Browse files Browse the repository at this point in the history
  • Loading branch information
HendrikMennen committed Jul 2, 2024
1 parent 308b532 commit bf428f5
Show file tree
Hide file tree
Showing 13 changed files with 345 additions and 96 deletions.
12 changes: 12 additions & 0 deletions src/OneWare.Essentials/EditorExtensions/SemanticToken.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using OmniSharp.Extensions.LanguageServer.Protocol.Models;

namespace OneWare.Essentials.EditorExtensions;

public struct SemanticToken
{
public int Line { get; init; }
public int StartCharacter { get; init; }
public int Length { get; init; }
public SemanticTokenType TokenType { get; init; }
public SemanticTokenModifier[] TokenModifiers { get; init; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ public TextModificationSegment(int startOffset, int endOffset)
EndOffset = endOffset;
}

public IBrush? Brush { get; set; }

public IBrush? Foreground { get; set; }

public IBrush? Background { get; set; }

public TextDecorationCollection? Decorations { get; set; }
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using Avalonia.Media;
using AvaloniaEdit.Document;
using AvaloniaEdit.Document;
using AvaloniaEdit.Rendering;

namespace OneWare.Essentials.EditorExtensions;
Expand Down Expand Up @@ -70,16 +69,16 @@ protected override void ColorizeLine(DocumentLine line)
foreach (var overlap in overlaps)
{
if (overlap.EndOffset > line.EndOffset) overlap.EndOffset = line.EndOffset;
ChangeLinePart(overlap.StartOffset, overlap.EndOffset, (x) => ApplyChanges(x, overlap.Brush, overlap.Decorations));
ChangeLinePart(overlap.StartOffset, overlap.EndOffset, (x) => ApplyChanges(x, overlap));
}
}
}
}

private void ApplyChanges(VisualLineElement element, IBrush? color, TextDecorationCollection? decorations)
private void ApplyChanges(VisualLineElement element, TextModificationSegment segment)
{
// This is where you do anything with the line
if (color != null) element.TextRunProperties.SetForegroundBrush(color);
if (decorations != null) element.TextRunProperties.SetTextDecorations(decorations);
if (segment.Foreground != null) element.TextRunProperties.SetForegroundBrush(segment.Foreground);
if (segment.Background != null) element.TextRunProperties.SetBackgroundBrush(segment.Background);
if (segment.Decorations != null) element.TextRunProperties.SetTextDecorations(segment.Decorations);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ namespace OneWare.Essentials.EditorExtensions
{
public class WordHighlightRenderer : IBackgroundRenderer
{
public const string BracketHighlight = "Bracket highlight";

public static readonly Color DefaultBackground = Color.FromArgb(20, 0, 200, 255);
public static readonly Color DefaultBorder = Color.FromArgb(150, 0, 200, 255);

private readonly TextView _textView;
private Brush? _backgroundBrush;
private Pen? _borderPen;

public WordSearchResult? Result { get; set; }

public WordHighlightRenderer(TextView textView)
Expand Down
6 changes: 4 additions & 2 deletions src/OneWare.Essentials/Helpers/RangeHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@ namespace OneWare.Essentials.Helpers;

public static class RangeHelper
{
public static TextModificationSegment GenerateTextModification(this Range range, TextDocument document, IBrush brush)
public static TextModificationSegment GenerateTextModification(this Range range, TextDocument document, IBrush? foreground, IBrush? background = null, TextDecorationCollection? decorations = null)
{
var offset = document.GetStartAndEndOffset(range);
return new TextModificationSegment(offset.startOffset, offset.endOffset)
{
Brush = brush
Foreground = foreground,
Background = background,
Decorations = decorations
};
}
}
60 changes: 60 additions & 0 deletions src/OneWare.Essentials/Helpers/SemanticTokenHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
using System.Collections.Immutable;
using OmniSharp.Extensions.LanguageServer.Protocol.Models;
using OneWare.Essentials.EditorExtensions;

namespace OneWare.Essentials.Helpers;

public static class SemanticTokenHelper
{
public static List<SemanticToken> ParseSemanticTokens(ImmutableArray<int> data, SemanticTokensLegend legend)
{
var tokens = new List<SemanticToken>();
var line = 0;
var character = 0;

var types = legend.TokenTypes.ToArray();
var modifiers = legend.TokenModifiers.ToArray();

for (var i = 0; i < data.Length; i += 5)
{
var deltaLine = data[i];
var deltaStartCharacter = data[i + 1];
var length = data[i + 2];
var tokenType = data[i + 3];
var tokenModifiersBitset = data[i + 4];

line += deltaLine;
if (deltaLine == 0)
{
character += deltaStartCharacter;
}
else
{
character = deltaStartCharacter;
}

if(tokenType < 0 || tokenType >= types!.Length)
throw new InvalidOperationException("Invalid token type");

var tokenModifiers = new List<SemanticTokenModifier>();
for (var bit = 0; bit < modifiers.Length; bit++)
{
if ((tokenModifiersBitset & (1 << bit)) != 0)
{
tokenModifiers.Add(modifiers[bit]);
}
}

tokens.Add(new SemanticToken
{
Line = line,
StartCharacter = character,
Length = length,
TokenType = types![tokenType],
TokenModifiers = tokenModifiers.ToArray()
});
}

return tokens;
}
}
2 changes: 2 additions & 0 deletions src/OneWare.Essentials/LanguageService/ILanguageService.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using OmniSharp.Extensions.LanguageServer.Protocol.Models;
using OneWare.Essentials.EditorExtensions;
using OneWare.Essentials.ViewModels;
using Range = OmniSharp.Extensions.LanguageServer.Protocol.Models.Range;

Expand Down Expand Up @@ -54,6 +55,7 @@ public interface ILanguageService
public Task<IEnumerable<LocationOrLocationLink>?> RequestDeclarationAsync(string fullPath, Position pos);
public Task<SymbolInformationOrDocumentSymbolContainer?> RequestSymbolsAsync(string fullPath);
public Task<Container<ColorInformation>?> RequestDocumentColorAsync(string fullPath);
public Task<IEnumerable<SemanticToken>?> RequestSemanticTokensFullAsync(string fullPath);
public Task<TextEditContainer?> RequestFormattingAsync(string fullPath);
public Task<TextEditContainer?> RequestRangeFormattingAsync(string fullPath, Range range);
public Task ExecuteCommandAsync(Command cmd);
Expand Down
1 change: 1 addition & 0 deletions src/OneWare.Essentials/LanguageService/ITypeAssistance.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ public interface ITypeAssistance
void Format();
void TextEntering(TextInputEventArgs e);
void TextEntered(TextInputEventArgs e);
void CaretPositionChanged(int offset);
Task<List<MenuItemViewModel>?> GetQuickMenuAsync(int offset);
Task<string?> GetHoverInfoAsync(int offset);
Task<Action?> GetActionOnControlWordAsync(int offset);
Expand Down
6 changes: 6 additions & 0 deletions src/OneWare.Essentials/LanguageService/LanguageServiceBase.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Avalonia.Threading;
using OmniSharp.Extensions.LanguageServer.Protocol.Models;
using OneWare.Essentials.EditorExtensions;
using OneWare.Essentials.Enums;
using OneWare.Essentials.Extensions;
using OneWare.Essentials.Models;
Expand Down Expand Up @@ -156,6 +157,11 @@ public virtual void RefreshTextDocument(string fullPath, string newText)
return Task.FromResult<Container<ColorInformation>?>(null);
}

public virtual Task<IEnumerable<SemanticToken>?> RequestSemanticTokensFullAsync(string fullPath)
{
return Task.FromResult<IEnumerable<SemanticToken>?>(null);
}

public virtual Task<TextEditContainer?> RequestFormattingAsync(string fullPath)
{
return Task.FromResult<TextEditContainer?>(null);
Expand Down
41 changes: 41 additions & 0 deletions src/OneWare.Essentials/LanguageService/LanguageServiceLsp.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
using OmniSharp.Extensions.LanguageServer.Protocol.Server.Capabilities;
using OmniSharp.Extensions.LanguageServer.Protocol.Window;
using OmniSharp.Extensions.LanguageServer.Protocol.Workspace;
using OneWare.Essentials.EditorExtensions;
using OneWare.Essentials.Helpers;
using OneWare.Essentials.Models;
using OneWare.Essentials.Services;
Expand Down Expand Up @@ -187,6 +188,20 @@ private async Task InitAsync(Stream input, Stream output, Action<LanguageClientO
{
LinkSupport = false
});
// options.WithCapability(new SemanticTokensCapability()
// {
// OverlappingTokenSupport = false,
// ServerCancelSupport = true,
// Requests = new SemanticTokensCapabilityRequests()
// {
// Range = new Supports<SemanticTokensCapabilityRequestRange?>(false),
// Full = new Supports<SemanticTokensCapabilityRequestFull?>(true)
// },
// TokenTypes = new Container<SemanticTokenType>(SemanticTokenType.Type, SemanticTokenType.Function, SemanticTokenType.Variable, SemanticTokenType.Class, SemanticTokenType.Keyword),
// Formats = new Container<SemanticTokenFormat>(SemanticTokenFormat.Relative),
// AugmentsSyntaxTokens = true,
// MultilineTokenSupport = true
// });
// options.WithCapability(new DidChangeWatchedFilesCapability()
// {
//
Expand Down Expand Up @@ -456,6 +471,32 @@ public override Task ExecuteCommandAsync(Command cmd)
return Client.ExecuteCommand(cmd);
}

public override async Task<IEnumerable<SemanticToken>?> RequestSemanticTokensFullAsync(string fullPath)
{
if (Client?.ServerSettings.Capabilities.SemanticTokensProvider == null) return null;
try
{
var semanticTokens = await Client.RequestSemanticTokensFull(new SemanticTokensParams()
{
TextDocument = new TextDocumentIdentifier
{
Uri = fullPath
},
});
if (semanticTokens == null) return null;

var legend = Client.ServerSettings.Capabilities.SemanticTokensProvider.Legend;
var parsedTokens = SemanticTokenHelper.ParseSemanticTokens(semanticTokens.Data, legend);
return parsedTokens;
}
catch (Exception e)
{
ContainerLocator.Container.Resolve<ILogger>()?.Error(e.Message, e);
}

return null;
}

public override async Task<CompletionList?> RequestCompletionAsync(string fullPath, Position pos, CompletionTriggerKind triggerKind, string? triggerChar)
{
var cts = new CancellationTokenSource();
Expand Down
7 changes: 6 additions & 1 deletion src/OneWare.Essentials/LanguageService/TypeAssistanceBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ protected virtual Task TextEnteredAsync(TextInputEventArgs e)

return Task.CompletedTask;
}

public virtual void TextEntering(TextInputEventArgs e)
{
}
Expand All @@ -116,6 +116,11 @@ public void TextEntered(TextInputEventArgs e)
_ = TextEnteredAsync(e);
}

public virtual void CaretPositionChanged(int offset)
{

}

public virtual Task<List<MenuItemViewModel>?> GetQuickMenuAsync(int offset)
{
return Task.FromResult<List<MenuItemViewModel>?>(null);
Expand Down
Loading

0 comments on commit bf428f5

Please sign in to comment.