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

Commit

Permalink
inlayhint functionality
Browse files Browse the repository at this point in the history
  • Loading branch information
HendrikMennen committed Jul 2, 2024
1 parent c602ace commit dceae57
Show file tree
Hide file tree
Showing 7 changed files with 205 additions and 37 deletions.
10 changes: 7 additions & 3 deletions src/OneWare.Essentials/EditorExtensions/ExtendedTextEditor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,11 @@ public class ExtendedTextEditor : TextEditor
public TextMarkerService MarkerService { get; }

public TextModificationService ModificationService { get; }
private ElementGenerator ElementGenerator { get; }
// private ElementGenerator ElementGenerator { get; }
public FoldingManager? FoldingManager { get; private set; }

public InlayHintRenderer InlayHintRenderer { get; }

public ExtendedTextEditor()
{
// //Avoid Styles to improve performance
Expand All @@ -42,13 +44,14 @@ public ExtendedTextEditor()
TextArea.TextView.LinkTextUnderline = true;
TextArea.RightClickMovesCaret = true;

ElementGenerator = new ElementGenerator();
BracketRenderer = new BracketHighlightRenderer(TextArea.TextView);
LineRenderer = new LineHighlightRenderer(this);
//ElementGenerator = new ElementGenerator();
//MergeService = new MergeService(this, ElementGenerator);
WordRenderer = new WordHighlightRenderer(TextArea.TextView);
MarkerService = new TextMarkerService(Document);
ModificationService = new TextModificationService(TextArea.TextView);
InlayHintRenderer = new InlayHintRenderer(this);

TextArea.TextView.BackgroundRenderers.Add(BracketRenderer);
TextArea.TextView.BackgroundRenderers.Add(LineRenderer);
Expand All @@ -57,7 +60,8 @@ public ExtendedTextEditor()
TextArea.TextView.BackgroundRenderers.Add(MarkerService);

TextArea.TextView.LineTransformers.Add(ModificationService);
TextArea.TextView.ElementGenerators.Add(ElementGenerator);
//TextArea.TextView.ElementGenerators.Add(ElementGenerator);
TextArea.TextView.ElementGenerators.Add(InlayHintRenderer);
}

protected override void OnDocumentChanged(DocumentChangedEventArgs e)
Expand Down
30 changes: 30 additions & 0 deletions src/OneWare.Essentials/EditorExtensions/InlayHintLineText.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using System.Diagnostics;
using Avalonia.Media;
using Avalonia.Media.TextFormatting;
using AvaloniaEdit.Rendering;

namespace OneWare.Essentials.EditorExtensions;

public class InlayHintLineText : VisualLineText
{
/// <summary>
/// Creates a visual line text element with the specified length.
/// It uses the <see cref="ITextRunConstructionContext.VisualLine"/> and its
/// <see cref="VisualLineElement.RelativeTextOffset"/> to find the actual text string.
/// </summary>
public InlayHintLineText(VisualLine parentVisualLine, int length) : base(parentVisualLine, length)
{

}

/// <inheritdoc/>
public override TextRun CreateTextRun(int startVisualColumn, ITextRunConstructionContext context)
{
this.TextRunProperties.SetForegroundBrush(context.TextView.LinkTextForegroundBrush);
this.TextRunProperties.SetBackgroundBrush(context.TextView.LinkTextBackgroundBrush);

if (context.TextView.LinkTextUnderline)
this.TextRunProperties.SetTextDecorations(TextDecorations.Underline);
return base.CreateTextRun(startVisualColumn, context);
}
}
118 changes: 118 additions & 0 deletions src/OneWare.Essentials/EditorExtensions/InlayHintRenderer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Layout;
using Avalonia.Media;
using AvaloniaEdit;
using AvaloniaEdit.Document;
using AvaloniaEdit.Rendering;
using DynamicData;

namespace OneWare.Essentials.EditorExtensions;

public class InlayHint
{
public TextLocation Location { get; init; }

//Will be used internally
public int Offset { get; set; } = -1;

public string Text { get; init; } = string.Empty;
}

public class InlayHintRenderer : VisualLineElementGenerator
{
private readonly TextEditor _editor;
private readonly List<InlayHint> _hints = [];
private readonly List<Control> _hintControls = [];

public InlayHintRenderer(TextEditor editor)
{
_editor = editor;
}

public void SetInlineHints(IEnumerable<InlayHint> hints)
{
_hints.Clear();
_hintControls.Clear();

_hints.AddRange(hints);

var foreground = Application.Current!.FindResource(Application.Current!.RequestedThemeVariant, "ThemeForegroundLowBrush") as IBrush;
var background = Application.Current!.FindResource(Application.Current!.RequestedThemeVariant, "ThemeBackgroundBrush") as IBrush;

foreach (var hint in _hints)
{
_hintControls.Add(new Border()
{
Margin = new Thickness(1, 0, 5, 0),
Background = background,
CornerRadius = new CornerRadius(3),
VerticalAlignment = VerticalAlignment.Center,
Child = new TextBlock()
{
Text = hint.Text,
Foreground = foreground,
Margin = new Thickness(2,0),
VerticalAlignment = VerticalAlignment.Center
}
});
}

_editor.TextArea.TextView.Redraw();
}

public void ClearInlineHints()
{
_hints.Clear();
_editor.TextArea.TextView.Redraw();
}

private static InlayHint? FindNextPosition(List<InlayHint> positions, TextLocation startPosition)
{
int left = 0;
int right = positions.Count - 1;

while (left <= right)
{
int mid = left + (right - left) / 2;
int comparison = positions[mid].Location.CompareTo(startPosition);

if (comparison >= 0)
{
if (mid == 0 || positions[mid - 1].Location.CompareTo(startPosition) < 0)
{
return positions[mid];
}
right = mid - 1;
}
else
{
left = mid + 1;
}
}

return null;
}

public override int GetFirstInterestedOffset(int startOffset)
{
var position = _editor.Document.GetLocation(startOffset);
var next = FindNextPosition(_hints, position);

if(next == null)
return -1;

next.Offset = _editor.Document.GetOffset(next.Location);
return next.Offset;
}

public override VisualLineElement? ConstructElement(int offset)
{
var index = _hints.FindIndex(hint => hint.Offset == offset);

if (index < 0) return null;

var control = _hintControls[index];
return new InlineObjectElement(0, control);
}
}
1 change: 1 addition & 0 deletions src/OneWare.Essentials/LanguageService/ILanguageService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ public interface ILanguageService
public Task<SymbolInformationOrDocumentSymbolContainer?> RequestSymbolsAsync(string fullPath);
public Task<Container<ColorInformation>?> RequestDocumentColorAsync(string fullPath);
public Task<IEnumerable<SemanticToken>?> RequestSemanticTokensFullAsync(string fullPath);
public Task<InlayHintContainer?> RequestInlayHintsAsync(string fullPath, Range range);
public Task<TextEditContainer?> RequestFormattingAsync(string fullPath);
public Task<TextEditContainer?> RequestRangeFormattingAsync(string fullPath, Range range);
public Task ExecuteCommandAsync(Command cmd);
Expand Down
5 changes: 5 additions & 0 deletions src/OneWare.Essentials/LanguageService/LanguageServiceBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,11 @@ public virtual void RefreshTextDocument(string fullPath, string newText)
return Task.FromResult<IEnumerable<SemanticToken>?>(null);
}

public virtual Task<InlayHintContainer?> RequestInlayHintsAsync(string fullPath, Range range)
{
return Task.FromResult<InlayHintContainer?>(null);
}

public virtual Task<TextEditContainer?> RequestFormattingAsync(string fullPath)
{
return Task.FromResult<TextEditContainer?>(null);
Expand Down
23 changes: 23 additions & 0 deletions src/OneWare.Essentials/LanguageService/LanguageServiceLsp.cs
Original file line number Diff line number Diff line change
Expand Up @@ -496,6 +496,29 @@ public override Task ExecuteCommandAsync(Command cmd)
return null;
}

public override async Task<InlayHintContainer?> RequestInlayHintsAsync(string fullPath, Range range)
{
if (Client?.ServerSettings.Capabilities.InlayHintProvider == null) return null;
try
{
var inlayHintContainer = await Client.RequestInlayHints(new InlayHintParams
{
TextDocument = new TextDocumentIdentifier
{
Uri = fullPath
},
Range = range
});
return inlayHintContainer;
}
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
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
using Prism.Ioc;
using CompletionList = OmniSharp.Extensions.LanguageServer.Protocol.Models.CompletionList;
using IFile = OneWare.Essentials.Models.IFile;
using InlayHint = OneWare.Essentials.EditorExtensions.InlayHint;
using Location = OmniSharp.Extensions.LanguageServer.Protocol.Models.Location;
using Range = OmniSharp.Extensions.LanguageServer.Protocol.Models.Range;

Expand Down Expand Up @@ -108,6 +109,7 @@ protected virtual void Timer_Tick(object? sender, EventArgs e)

//Execute slower actions after the caret was not changed for 100ms
_ = GetDocumentHighlightAsync();
_ = UpdateInlayHintsAsync();
}

if (_lastCompletionItemChangedTime > _lastCompletionItemResolveTime &&
Expand Down Expand Up @@ -144,6 +146,7 @@ protected override void OnAssistanceActivated()
Service.DidOpenTextDocument(CurrentFile.FullPath, Editor.CurrentDocument.Text);

_ = UpdateSemanticTokensAsync();
_ = UpdateInlayHintsAsync();
}

protected override void OnAssistanceDeactivated()
Expand All @@ -152,6 +155,7 @@ protected override void OnAssistanceDeactivated()
base.OnAssistanceDeactivated();
Editor.Editor.ModificationService.ClearModification("caretHighlight");
Editor.Editor.ModificationService.ClearModification("semanticTokens");
Editor.Editor.InlayHintRenderer.ClearInlineHints();
}

protected virtual void DocumentChanged(object? sender, DocumentChangeEventArgs e)
Expand Down Expand Up @@ -586,41 +590,24 @@ protected virtual async Task UpdateSemanticTokensAsync()
{
Editor.Editor.ModificationService.ClearModification("semanticTokens");
}
}

protected virtual async Task UpdateInlayHintsAsync()
{
var inlayHintContainer = await Service.RequestInlayHintsAsync(CurrentFile.FullPath, new Range(0,0, CodeBox.Document.LineCount, 0));

// LastDocumentSymbols = await Service.RequestSymbolsAsync(CurrentFile.FullPath);
//
// var languageManager = ContainerLocator.Container.Resolve<ILanguageManager>();
//
// if (LastDocumentSymbols is not null)
// {
// var segments = LastDocumentSymbols
// .Where(x => x.IsDocumentSymbolInformation && x.SymbolInformation != null)
// .Select(x =>
// {
// if (x.IsDocumentSymbol && x.DocumentSymbol != null)
// {
// if (!languageManager.CurrentEditorThemeColors.TryGetValue(x.DocumentSymbol.Kind,
// out var brush)) return null;
// return x.DocumentSymbol.Range.GenerateTextModification(Editor.CurrentDocument, brush);
// }
// if (x.IsDocumentSymbolInformation && x.SymbolInformation != null)
// {
// if (!languageManager.CurrentEditorThemeColors.TryGetValue(x.SymbolInformation.Kind, out var brush)) return null;
// if (!x.SymbolInformation.Location.Uri.GetFileSystemPath().EqualPaths(CurrentFile.FullPath)) return null;
// return x.SymbolInformation.Location.Range.GenerateTextModification(Editor.CurrentDocument, brush);
// }
// return null;
// })
// .Where(x => x is not null)
// .Cast<TextModificationSegment>()
// .ToArray();
//
// Editor.Editor.ModificationService.SetModification("symbols", segments);
// }
// else
// {
// Editor.Editor.ModificationService.ClearModification("symbols");
// }
if (inlayHintContainer is not null)
{
Editor.Editor.InlayHintRenderer.SetInlineHints(inlayHintContainer.Select(x => new InlayHint()
{
Location = new TextLocation(x.Position.Line + 1, x.Position.Character + 1),
Text = x.Label.ToString()
}));
}
else
{
Editor.Editor.InlayHintRenderer.ClearInlineHints();
}
}

protected virtual async Task ShowSignatureHelpAsync(SignatureHelpTriggerKind triggerKind, string? triggerChar,
Expand Down

0 comments on commit dceae57

Please sign in to comment.