using System; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using Avalonia.Controls; using Avalonia.Controls.Documents; using Avalonia.Media; using AvaloniaEdit.Document; using AvaloniaEdit.Editing; using StabilityMatrix.Avalonia.Models; using StabilityMatrix.Avalonia.Styles; using StabilityMatrix.Core.Extensions; namespace StabilityMatrix.Avalonia.Controls.CodeCompletion; /// /// Provides entries in AvaloniaEdit completion window. /// public class CompletionData : ICompletionData { /// public string Text { get; } /// public string? Description { get; init; } /// public ImageSource? ImageSource { get; set; } /// /// Title of the image. /// public string? ImageTitle { get; set; } /// /// Subtitle of the image. /// public string? ImageSubtitle { get; set; } /// public IconData? Icon { get; init; } private InlineCollection? _textInlines; /// /// Get the current inlines /// public InlineCollection TextInlines => _textInlines ??= CreateInlines(); /// public double Priority { get; init; } public CompletionData(string text) { Text = text; } /// /// Create text block inline runs from text. /// private InlineCollection CreateInlines() { // Create a span for each character in the text. var chars = Text.ToCharArray(); var inlines = new InlineCollection(); foreach (var c in chars) { var run = new Run(c.ToString()); inlines.Add(run); } return inlines; } /// public void Complete( TextArea textArea, ISegment completionSegment, InsertionRequestEventArgs eventArgs, Func? prepareText = null ) { var text = Text; if (prepareText is not null) { text = prepareText(this); } // Capture initial offset before replacing text, since it will change var initialOffset = completionSegment.Offset; // Replace text textArea.Document.Replace(completionSegment, text); // Append text if requested if (eventArgs.AppendText is { } appendText && !string.IsNullOrEmpty(appendText)) { var end = initialOffset + text.Length; textArea.Document.Insert(end, appendText); textArea.Caret.Offset = end + appendText.Length; } } /// public void UpdateCharHighlighting(string searchText) { if (TextInlines is null) { throw new NullReferenceException("TextContent is null"); } var defaultColor = ThemeColors.CompletionForegroundBrush; var highlightColor = ThemeColors.CompletionSelectionForegroundBrush; // Match characters in the text with the search text from the start foreach (var (i, currentChar) in Text.Enumerate()) { var inline = TextInlines[i]; // If longer than text, set to default color if (i >= searchText.Length) { inline.Foreground = defaultColor; continue; } // If char matches, highlight it if (currentChar == searchText[i]) { inline.Foreground = highlightColor; } // For mismatch, set to default color else { inline.Foreground = defaultColor; } } } /// public void ResetCharHighlighting() { // TODO: handle light theme foreground variant var defaultColor = ThemeColors.CompletionForegroundBrush; foreach (var inline in TextInlines) { inline.Foreground = defaultColor; } } }