<commit_before>namespace CSharpMath.Editor {
  using System;
  using System.Collections.Generic;
  using System.Drawing;
  using Atoms;
  using Constants;
  using Display;
  using Enumerations;
  using FrontEnd;
  using Interfaces;
  using Color = Atoms.Color;

  public class MathKeyboard<TFont, TGlyph> where TFont : IFont<TGlyph> {
    public MathKeyboard(TypesettingContext<TFont, TGlyph> context) => Context = context;
    //private readonly List<MathListIndex> highlighted;

    protected TypesettingContext<TFont, TGlyph> Context { get; }

    public CaretHandle? Caret { get; protected set; }
    public IDisplay<TFont, TGlyph> Display { get; protected set; }
    public MathList MathList { get; } = new MathList();
    public string LaTeX => MathListBuilder.MathListToString(MathList);
    private MathListIndex _insertionIndex = MathListIndex.Level0Index(0);
    public MathListIndex InsertionIndex
      { get => _insertionIndex; set { _insertionIndex = value; InsertionPointChanged(); } }
    public TFont Font { get; set; }
    public LineStyle LineStyle { get; set; }
    public Color SelectColor { get; set; }
    public RectangleF Measure => Display.DisplayBounds;
    public bool HasText => MathList?.Atoms?.Count > 0;

    public void RecreateDisplayFromMathList() {
      var position = Display?.Position ?? default;
      Display = Context.CreateLine(MathList, Font, LineStyle);
      Display.Position = position;
    }

    /// <summary>Keyboard should now be hidden and input be discarded.</summary>
    public event EventHandler DismissPressed;
    /// <summary>Keyboard should now be hidden and input be saved.</summary>
    public event EventHandler ReturnPressed;
    /// <summary><see cref="Display"/> should be redrawn.</summary>
    public event EventHandler RedrawRequested;

    public PointF? ClosestPointToIndex(MathListIndex index) => Display?.PointForIndex(Context, index);
    public MathListIndex ClosestIndexToPoint(PointF point) => Display?.IndexForPoint(Context, point);

    public void KeyPress(params MathKeyboardInput[] inputs) {
      foreach (var input in inputs) KeyPress(input);
    }
    public void KeyPress(MathKeyboardInput input) {
      MathAtom AtomForKeyPress(MathKeyboardInput i) {
        var c = (char)i;
        // Get the basic conversion from MathAtoms, and then special case unicode characters and latex special characters.
        switch (i) {
          //https://github.com/kostub/MathEditor/blob/61f67c6224000c224e252f6eeba483003f11d3d5/mathEditor/editor/MTEditableMathLabel.m#L414
          case MathKeyboardInput.Multiply:
          case MathKeyboardInput.Multiply_:
            return MathAtoms.Times;
          case MathKeyboardInput.SquareRoot:
            return MathAtoms.PlaceholderSquareRoot;
          case MathKeyboardInput.CubeRoot:
            var sqroot = MathAtoms.PlaceholderSquareRoot;
            sqroot.Degree = MathLists.WithAtoms(MathAtoms.ForCharacter('3'));
            return sqroot;
          case MathKeyboardInput.Infinity:
          case MathKeyboardInput.Degree:
          case MathKeyboardInput.Angle:
            return MathAtoms.Create(MathAtomType.Ordinary, c);
          case MathKeyboardInput.Divide:
          case MathKeyboardInput.Slash:
            return MathAtoms.Divide;
          case MathKeyboardInput.Fraction:
            return MathAtoms.PlaceholderFraction;
          case MathKeyboardInput.LeftCurlyBracket:
            return MathAtoms.Create(MathAtomType.Open, c);
          case MathKeyboardInput.RightCurlyBracket:
            return MathAtoms.Create(MathAtomType.Close, c);
          case MathKeyboardInput.GreaterOrEquals:
          case MathKeyboardInput.LessOrEquals:
            return MathAtoms.Create(MathAtomType.Relation, c);
          case var _ when c >= UnicodeFontChanger.UnicodeGreekLowerStart && c <= UnicodeFontChanger.UnicodeGreekLowerEnd:
            // All greek letters are rendered as variables.
            return MathAtoms.Create(MathAtomType.Variable, c);
          case var _ when c >= UnicodeFontChanger.UnicodeGreekUpperStart && c <= UnicodeFontChanger.UnicodeGreekUpperEnd:
            // Including capital greek letters
            return MathAtoms.Create(MathAtomType.Variable, c);
          case var _ when MathAtoms.ForCharacter(c) is MathAtom a:
            return a;
          default:
            //Just an ordinary character
            return MathAtoms.Create(MathAtomType.Ordinary, c);
        }
      }

      void HandleScriptButton(bool isSuperScript) {
        var subIndexType = isSuperScript ? MathListSubIndexType.Superscript : MathListSubIndexType.Subscript;
        IMathList GetScript(IMathAtom atom) => isSuperScript ? atom.Superscript : atom.Subscript;
        void SetScript(IMathAtom atom, IMathList value) { if (isSuperScript) atom.Superscript = value; else atom.Subscript = value; }
        if (_insertionIndex.AtBeginningOfLine) {
          // Create an empty atom and move the insertion index up.
          var emptyAtom = MathAtoms.Placeholder;
          emptyAtom.Superscript = MathAtoms.PlaceholderList;
          MathList.InsertAndAdvance(ref _insertionIndex, emptyAtom, subIndexType);
        } else {
          var prevAtom = MathList.AtomAt(_insertionIndex.Previous);
#warning Simplify to tuple patterns when C# 8 is out
          switch ((GetScript(prevAtom) is null, _insertionIndex.FinalSubIndexType is MathListSubIndexType.BetweenBaseAndScripts)) {
            case var t when t == (true, true):
              SetScript(MathList.AtomAt(_insertionIndex.LevelDown()), MathAtoms.PlaceholderList);
              _insertionIndex = _insertionIndex.LevelDown().LevelUpWithSubIndex(subIndexType, MathListIndex.Level0Index(0));
              break;
            case var t when t == (true, false):
              SetScript(prevAtom, MathAtoms.PlaceholderList);
              _insertionIndex = _insertionIndex.Previous.LevelUpWithSubIndex(subIndexType, MathListIndex.Level0Index(0));
              break;
            case var t when t == (false, true):
              // If we are already inside the nucleus, then we come out and go up to the script
              _insertionIndex = _insertionIndex.LevelDown().LevelUpWithSubIndex(subIndexType, MathListIndex.Level0Index(GetScript(prevAtom).Atoms.Count));
              break;
            case var t when t == (false, false):
              _insertionIndex = _insertionIndex.Previous.LevelUpWithSubIndex(subIndexType, MathListIndex.Level0Index(GetScript(prevAtom).Atoms.Count));
              break;
          }
        }
      }

      void HandleRadical(bool degreeIsPlaceholder, bool degreeIs3) {
        Radical rad;
        if (degreeIsPlaceholder) {
          rad = MathAtoms.PlaceholderRadical;<commit_msg>fix HandleScriptButton<commit_after>namespace CSharpMath.Editor {
  using System;
  using System.Collections.Generic;
  using System.Drawing;
  using Atoms;
  using Constants;
  using Display;
  using Enumerations;
  using FrontEnd;
  using Interfaces;
  using Color = Atoms.Color;

  public class MathKeyboard<TFont, TGlyph> where TFont : IFont<TGlyph> {
    public MathKeyboard(TypesettingContext<TFont, TGlyph> context) => Context = context;
    //private readonly List<MathListIndex> highlighted;

    protected TypesettingContext<TFont, TGlyph> Context { get; }

    public CaretHandle? Caret { get; protected set; }
    public IDisplay<TFont, TGlyph> Display { get; protected set; }
    public MathList MathList { get; } = new MathList();
    public string LaTeX => MathListBuilder.MathListToString(MathList);
    private MathListIndex _insertionIndex = MathListIndex.Level0Index(0);
    public MathListIndex InsertionIndex
      { get => _insertionIndex; set { _insertionIndex = value; InsertionPointChanged(); } }
    public TFont Font { get; set; }
    public LineStyle LineStyle { get; set; }
    public Color SelectColor { get; set; }
    public RectangleF Measure => Display.DisplayBounds;
    public bool HasText => MathList?.Atoms?.Count > 0;

    public void RecreateDisplayFromMathList() {
      var position = Display?.Position ?? default;
      Display = Context.CreateLine(MathList, Font, LineStyle);
      Display.Position = position;
    }

    /// <summary>Keyboard should now be hidden and input be discarded.</summary>
    public event EventHandler DismissPressed;
    /// <summary>Keyboard should now be hidden and input be saved.</summary>
    public event EventHandler ReturnPressed;
    /// <summary><see cref="Display"/> should be redrawn.</summary>
    public event EventHandler RedrawRequested;

    public PointF? ClosestPointToIndex(MathListIndex index) => Display?.PointForIndex(Context, index);
    public MathListIndex ClosestIndexToPoint(PointF point) => Display?.IndexForPoint(Context, point);

    public void KeyPress(params MathKeyboardInput[] inputs) {
      foreach (var input in inputs) KeyPress(input);
    }
    public void KeyPress(MathKeyboardInput input) {
      MathAtom AtomForKeyPress(MathKeyboardInput i) {
        var c = (char)i;
        // Get the basic conversion from MathAtoms, and then special case unicode characters and latex special characters.
        switch (i) {
          //https://github.com/kostub/MathEditor/blob/61f67c6224000c224e252f6eeba483003f11d3d5/mathEditor/editor/MTEditableMathLabel.m#L414
          case MathKeyboardInput.Multiply:
          case MathKeyboardInput.Multiply_:
            return MathAtoms.Times;
          case MathKeyboardInput.SquareRoot:
            return MathAtoms.PlaceholderSquareRoot;
          case MathKeyboardInput.CubeRoot:
            var sqroot = MathAtoms.PlaceholderSquareRoot;
            sqroot.Degree = MathLists.WithAtoms(MathAtoms.ForCharacter('3'));
            return sqroot;
          case MathKeyboardInput.Infinity:
          case MathKeyboardInput.Degree:
          case MathKeyboardInput.Angle:
            return MathAtoms.Create(MathAtomType.Ordinary, c);
          case MathKeyboardInput.Divide:
          case MathKeyboardInput.Slash:
            return MathAtoms.Divide;
          case MathKeyboardInput.Fraction:
            return MathAtoms.PlaceholderFraction;
          case MathKeyboardInput.LeftCurlyBracket:
            return MathAtoms.Create(MathAtomType.Open, c);
          case MathKeyboardInput.RightCurlyBracket:
            return MathAtoms.Create(MathAtomType.Close, c);
          case MathKeyboardInput.GreaterOrEquals:
          case MathKeyboardInput.LessOrEquals:
            return MathAtoms.Create(MathAtomType.Relation, c);
          case var _ when c >= UnicodeFontChanger.UnicodeGreekLowerStart && c <= UnicodeFontChanger.UnicodeGreekLowerEnd:
            // All greek letters are rendered as variables.
            return MathAtoms.Create(MathAtomType.Variable, c);
          case var _ when c >= UnicodeFontChanger.UnicodeGreekUpperStart && c <= UnicodeFontChanger.UnicodeGreekUpperEnd:
            // Including capital greek letters
            return MathAtoms.Create(MathAtomType.Variable, c);
          case var _ when MathAtoms.ForCharacter(c) is MathAtom a:
            return a;
          default:
            //Just an ordinary character
            return MathAtoms.Create(MathAtomType.Ordinary, c);
        }
      }

      void HandleScriptButton(bool isSuperScript) {
        var subIndexType = isSuperScript ? MathListSubIndexType.Superscript : MathListSubIndexType.Subscript;
        IMathList GetScript(IMathAtom atom) => isSuperScript ? atom.Superscript : atom.Subscript;
        void SetScript(IMathAtom atom, IMathList value) { if (isSuperScript) atom.Superscript = value; else atom.Subscript = value; }
        if (_insertionIndex.AtBeginningOfLine) {
          // Create an empty atom and move the insertion index up.
          var emptyAtom = MathAtoms.Placeholder;
          SetScript(emptyAtom, MathAtoms.PlaceholderList);
          MathList.InsertAndAdvance(ref _insertionIndex, emptyAtom, subIndexType);
        } else {
          var prevAtom = MathList.AtomAt(_insertionIndex.Previous);
#warning Simplify to tuple patterns when C# 8 is out
          switch ((GetScript(prevAtom) is null, _insertionIndex.FinalSubIndexType is MathListSubIndexType.BetweenBaseAndScripts)) {
            case var t when t == (true, true):
              SetScript(MathList.AtomAt(_insertionIndex.LevelDown()), MathAtoms.PlaceholderList);
              _insertionIndex = _insertionIndex.LevelDown().LevelUpWithSubIndex(subIndexType, MathListIndex.Level0Index(0));
              break;
            case var t when t == (true, false):
              SetScript(prevAtom, MathAtoms.PlaceholderList);
              _insertionIndex = _insertionIndex.Previous.LevelUpWithSubIndex(subIndexType, MathListIndex.Level0Index(0));
              break;
            case var t when t == (false, true):
              // If we are already inside the nucleus, then we come out and go up to the script
              _insertionIndex = _insertionIndex.LevelDown().LevelUpWithSubIndex(subIndexType, MathListIndex.Level0Index(GetScript(prevAtom).Atoms.Count));
              break;
            case var t when t == (false, false):
              _insertionIndex = _insertionIndex.Previous.LevelUpWithSubIndex(subIndexType, MathListIndex.Level0Index(GetScript(prevAtom).Atoms.Count));
              break;
          }
        }
      }

      void HandleRadical(bool degreeIsPlaceholder, bool degreeIs3) {
        Radical rad;
        if (degreeIsPlaceholder) {
          rad = MathAtoms.PlaceholderRadical;
{'fraction_non_alphanumeric': 0.08066790352504638, 'fraction_numerical': 0.006382189239332097, 'mean_word_length': 3.0323159784560145, 'pattern_counts': {'":': 0, '<': 33, '<?xml version=': 0, '>': 51, 'https://': 2, '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': '4252977', 'n_tokens_mistral': 3769, 'n_tokens_neox': 3699, 'n_words': 904}