<commit_before>﻿using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using tterm.Ansi;
using tterm.Terminal;

namespace tterm.Ui
{
    internal class TerminalControl : StackPanel
    {
        private readonly Dictionary<int, Brush> _brushDictionary = new Dictionary<int, Brush>();
        private readonly List<TextBlock> _textBlocks = new List<TextBlock>();

        private TerminalSession _session;

        private Brush _foreground;
        private Brush _cursorBrush = new SolidColorBrush(Color.FromRgb(150, 150, 150));

        private FontFamily _fontFamily;
        private double _fontSize;
        private FontStyle _fontStyle;
        private FontWeight _fontWeight;
        private FontStretch _fontStretch;

        private int _lastCursorY;

        public TerminalSession Session
        {
            get => _session;
            set
            {
                if (_session != value)
                {
                    if (_session != null)
                    {
                        _session.OutputReceived -= OnOutputReceived;
                    }
                    _session = value;
                    _session.OutputReceived += OnOutputReceived;
                }
            }
        }

        public TerminalBuffer Buffer => _session.Buffer;

        public Brush Foreground
        {
            get => _foreground;
        }

        public FontFamily FontFamily
        {
            get => _fontFamily;
        }

        public double FontSize
        {
            get => _fontSize;
            set
            {
                if (_fontSize != value)
                {
                    _fontSize = value;
                    foreach (TextBlock textBlock in _textBlocks)
                    {
                        textBlock.FontSize = value;
                    }
                }
            }
        }

        public FontStyle FontStyle
        {
            get => _fontStyle;
        }

        public FontWeight FontWeight
        {
            get => _fontWeight;
        }
        public FontStretch FontStretch
        {
            get => _fontStretch;
        }

        static TerminalControl()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(TerminalControl), new FrameworkPropertyMetadata(typeof(TerminalControl)));
        }

        public TerminalControl()
        {
            Background = new BrushConverter().ConvertFromString("#FF1E1E1E") as Brush;
            _foreground = new BrushConverter().ConvertFromString("#FFCCCCCC") as Brush;
            _fontFamily = new FontFamily("Consolas");
            _fontSize = 20;
            _fontStyle = FontStyles.Normal;
            _fontWeight = FontWeights.Regular;
            _fontStretch = FontStretches.Normal;
            Focusable = true;
            FocusVisualStyle = null;
        }

        private TextBlock CreateTextBlock()
        {
            var textBlock = new TextBlock()
            {
                Background = Background,
                Foreground = _foreground,
                FontFamily = _fontFamily,
                FontSize = _fontSize,
                FontStyle = _fontStyle,
                FontStretch = _fontStretch,
                FontWeight = _fontWeight
            };
            return textBlock;
        }

        private void EnsureLineCount(int lineCount)
        {
            while (_textBlocks.Count < lineCount)
            {
                var textBlock = CreateTextBlock();
                _textBlocks.Add(textBlock);
                Children.Add(textBlock);
            }
        }

        public void UpdateContent()
        {
            int lineCount = Buffer.Size.Rows;
            EnsureLineCount(lineCount);
            for (int y = 0; y < lineCount; y++)
            {
                UpdateLine(y);
            }
            _lastCursorY = Buffer.CursorY;
        }

        private void UpdateLine(int y)
        {
            var textBlock = _textBlocks[y];

            var lineTags = Buffer.GetFormattedLine(y);
            if (textBlock.Tag != null && y != _lastCursorY && y != Buffer.CursorY)
            {
                if ((TerminalTagArray)textBlock.Tag == lineTags)
                {
                    return;
                }
            }

            var inlines = GetInlines(y, lineTags);
            textBlock.Inlines.Clear();
            textBlock.Inlines.AddRange(inlines);
            textBlock.Tag = lineTags;
        }

        private List<Inline> GetInlines(int y, TerminalTagArray lineTags)
        {
            var inlines = new List<Inline>();
            int x = 0;
            foreach (var tag in lineTags)
            {
                int length = tag.Text.Length;
                if (x <= Buffer.CursorX && y == Buffer.CursorY && x + length > Buffer.CursorX)
                {
                    int offset = Buffer.CursorX - x;
                    var tagA = tag.Substring(0, offset);
                    var tagB = tag.Substring(offset, 1);
                    var tagC = tag.Substring(offset + 1);
                    inlines.Add(CreateInline(tagA));

                    var cursorInline = CreateInline(tagB);
                    cursorInline.Background = _cursorBrush;
                    cursorInline.Foreground = _cursorBrush;
                    inlines.Add(cursorInline);

                    inlines.Add(CreateInline(tagC));
                }
                else
                {
                    inlines.Add(CreateInline(tag));
                }

                x += tag.Text.Length;
            }
            return inlines;
        }

        private Run CreateInline(TerminalTag tag)
        {
            var run = new Run(tag.Text)
            {
                Background = GetBackgroundBrush(tag.Attributes.BackgroundColour),
                Foreground = GetForegroundBrush(tag.Attributes.ForegroundColour)<commit_msg>Show foreground on cursor<commit_after>﻿using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using tterm.Ansi;
using tterm.Terminal;

namespace tterm.Ui
{
    internal class TerminalControl : StackPanel
    {
        private readonly Dictionary<int, Brush> _brushDictionary = new Dictionary<int, Brush>();
        private readonly List<TextBlock> _textBlocks = new List<TextBlock>();

        private TerminalSession _session;

        private Brush _foreground;
        private Brush _cursorBrush = new SolidColorBrush(Color.FromRgb(150, 150, 150));

        private FontFamily _fontFamily;
        private double _fontSize;
        private FontStyle _fontStyle;
        private FontWeight _fontWeight;
        private FontStretch _fontStretch;

        private int _lastCursorY;

        public TerminalSession Session
        {
            get => _session;
            set
            {
                if (_session != value)
                {
                    if (_session != null)
                    {
                        _session.OutputReceived -= OnOutputReceived;
                    }
                    _session = value;
                    _session.OutputReceived += OnOutputReceived;
                }
            }
        }

        public TerminalBuffer Buffer => _session.Buffer;

        public Brush Foreground
        {
            get => _foreground;
        }

        public FontFamily FontFamily
        {
            get => _fontFamily;
        }

        public double FontSize
        {
            get => _fontSize;
            set
            {
                if (_fontSize != value)
                {
                    _fontSize = value;
                    foreach (TextBlock textBlock in _textBlocks)
                    {
                        textBlock.FontSize = value;
                    }
                }
            }
        }

        public FontStyle FontStyle
        {
            get => _fontStyle;
        }

        public FontWeight FontWeight
        {
            get => _fontWeight;
        }
        public FontStretch FontStretch
        {
            get => _fontStretch;
        }

        static TerminalControl()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(TerminalControl), new FrameworkPropertyMetadata(typeof(TerminalControl)));
        }

        public TerminalControl()
        {
            Background = new BrushConverter().ConvertFromString("#FF1E1E1E") as Brush;
            _foreground = new BrushConverter().ConvertFromString("#FFCCCCCC") as Brush;
            _fontFamily = new FontFamily("Consolas");
            _fontSize = 20;
            _fontStyle = FontStyles.Normal;
            _fontWeight = FontWeights.Regular;
            _fontStretch = FontStretches.Normal;
            Focusable = true;
            FocusVisualStyle = null;
        }

        private TextBlock CreateTextBlock()
        {
            var textBlock = new TextBlock()
            {
                Background = Background,
                Foreground = _foreground,
                FontFamily = _fontFamily,
                FontSize = _fontSize,
                FontStyle = _fontStyle,
                FontStretch = _fontStretch,
                FontWeight = _fontWeight
            };
            return textBlock;
        }

        private void EnsureLineCount(int lineCount)
        {
            while (_textBlocks.Count < lineCount)
            {
                var textBlock = CreateTextBlock();
                _textBlocks.Add(textBlock);
                Children.Add(textBlock);
            }
        }

        public void UpdateContent()
        {
            int lineCount = Buffer.Size.Rows;
            EnsureLineCount(lineCount);
            for (int y = 0; y < lineCount; y++)
            {
                UpdateLine(y);
            }
            _lastCursorY = Buffer.CursorY;
        }

        private void UpdateLine(int y)
        {
            var textBlock = _textBlocks[y];

            var lineTags = Buffer.GetFormattedLine(y);
            if (textBlock.Tag != null && y != _lastCursorY && y != Buffer.CursorY)
            {
                if ((TerminalTagArray)textBlock.Tag == lineTags)
                {
                    return;
                }
            }

            var inlines = GetInlines(y, lineTags);
            textBlock.Inlines.Clear();
            textBlock.Inlines.AddRange(inlines);
            textBlock.Tag = lineTags;
        }

        private List<Inline> GetInlines(int y, TerminalTagArray lineTags)
        {
            var inlines = new List<Inline>();
            int x = 0;
            foreach (var tag in lineTags)
            {
                int length = tag.Text.Length;
                if (x <= Buffer.CursorX && y == Buffer.CursorY && x + length > Buffer.CursorX)
                {
                    int offset = Buffer.CursorX - x;
                    var tagA = tag.Substring(0, offset);
                    var tagB = tag.Substring(offset, 1);
                    var tagC = tag.Substring(offset + 1);
                    inlines.Add(CreateInline(tagA));

                    var cursorInline = CreateInline(tagB);
                    cursorInline.Background = _cursorBrush;
                    inlines.Add(cursorInline);

                    inlines.Add(CreateInline(tagC));
                }
                else
                {
                    inlines.Add(CreateInline(tag));
                }

                x += tag.Text.Length;
            }
            return inlines;
        }

        private Run CreateInline(TerminalTag tag)
        {
            var run = new Run(tag.Text)
            {
                Background = GetBackgroundBrush(tag.Attributes.BackgroundColour),
                Foreground = GetForegroundBrush(tag.Attributes.ForegroundColour)
{'fraction_non_alphanumeric': 0.07581317764804003, 'fraction_numerical': 0.00316930775646372, 'mean_word_length': 1.330159347065682, 'pattern_counts': {'":': 0, '<': 21, '<?xml version=': 0, '>': 33, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6172692', 'n_tokens_mistral': 3063, 'n_tokens_neox': 2851, 'n_words': 640}