浏览代码

WIP: Refactored Mouse stuff.

Tig 7 月之前
父节点
当前提交
76c0ab81d8

+ 2 - 2
Terminal.Gui/Application/Application.Keyboard.cs

@@ -54,7 +54,7 @@ public static partial class Application // Keyboard handling
                     return false;
                 }
 
-                bool? handled = binding.Value.BoundView?.InvokeCommands (binding.Value.Commands, binding.Key, binding.Value);
+                bool? handled = binding.Value.BoundView?.InvokeCommands<KeyBinding> (binding.Value.Commands, binding.Value);
 
                 if (handled != null && (bool)handled)
                 {
@@ -92,7 +92,7 @@ public static partial class Application // Keyboard handling
 
             if (CommandImplementations.TryGetValue (command, out View.CommandImplementation? implementation))
             {
-                var context = new CommandContext (command, key, appBinding); // Create the context here
+                var context = new CommandContext<KeyBinding> (command, appBinding); // Create the context here
 
                 return implementation (context);
             }

+ 1 - 1
Terminal.Gui/Input/Command.cs

@@ -5,7 +5,7 @@ namespace Terminal.Gui;
 
 /// <summary>
 ///     Actions which can be performed by a <see cref="View"/>. Commands are typically invoked via
-///     <see cref="View.KeyBindings"/> and mouse events.
+///     <see cref="KeyBindings"/> and <see cref="MouseBindings"/>.
 /// </summary>
 public enum Command
 {

+ 14 - 6
Terminal.Gui/Input/CommandContext.cs

@@ -12,13 +12,12 @@ namespace Terminal.Gui;
 ///     </para>
 /// </remarks>
 #pragma warning restore CS1574 // XML comment has cref attribute that could not be resolved
-public record struct CommandContext<TBindingType>
+public record struct CommandContext<TBindingType> : ICommandContext
 {
     /// <summary>
-    ///     Initializes a new instance of <see cref="CommandContext"/> with the specified <see cref="Command"/>,
+    ///     Initializes a new instance with the specified <see cref="Command"/>,
     /// </summary>
     /// <param name="command"></param>
-    /// <param name="key"></param>
     /// <param name="binding"></param>
     /// <param name="data"></param>
     public CommandContext (Command command, TBindingType? binding, object? data = null)
@@ -29,14 +28,23 @@ public record struct CommandContext<TBindingType>
     }
 
     /// <summary>
-    ///     The <see cref="Command"/> that is being invoked.
+    /// The keyboard or mouse minding that was used to invoke the <see cref="Command"/>, if any.
     /// </summary>
+    public TBindingType? Binding { get; set; }
+
+    /// <inheritdoc />
     public Command Command { get; set; }
 
+    /// <inheritdoc />
+    public object? Data { get; set; }
+}
+
+public interface ICommandContext
+{
     /// <summary>
-    /// The keyboard or mouse minding that was used to invoke the <see cref="Command"/>, if any.
+    ///     The <see cref="Command"/> that is being invoked.
     /// </summary>
-    public TBindingType? Binding { get; set; }
+    public Command Command { get; set; }
 
     /// <summary>
     ///     Arbitrary data.

+ 1 - 1
Terminal.Gui/Input/CommandEventArgs.cs

@@ -11,5 +11,5 @@ public class CommandEventArgs : CancelEventArgs
     /// <summary>
     ///     The context for the command.
     /// </summary>
-    public CommandContext Context { get; init; }
+    public required ICommandContext Context { get; init; }
 }

+ 0 - 0
Terminal.Gui/Input/Key.cs → Terminal.Gui/Input/Keyboard/Key.cs


+ 0 - 0
Terminal.Gui/Input/KeyBinding.cs → Terminal.Gui/Input/Keyboard/KeyBinding.cs


+ 0 - 0
Terminal.Gui/Input/KeyBindingScope.cs → Terminal.Gui/Input/Keyboard/KeyBindingScope.cs


+ 0 - 3
Terminal.Gui/Input/KeyBindings.cs → Terminal.Gui/Input/Keyboard/KeyBindings.cs

@@ -1,7 +1,4 @@
 #nullable enable
-
-using static System.Formats.Asn1.AsnWriter;
-
 namespace Terminal.Gui;
 
 /// <summary>

+ 0 - 0
Terminal.Gui/Input/KeyChangedEventArgs.cs → Terminal.Gui/Input/Keyboard/KeyChangedEventArgs.cs


+ 0 - 0
Terminal.Gui/Input/KeyEqualityComparer.cs → Terminal.Gui/Input/Keyboard/KeyEqualityComparer.cs


+ 0 - 0
Terminal.Gui/Input/KeystrokeNavigatorEventArgs.cs → Terminal.Gui/Input/Keyboard/KeystrokeNavigatorEventArgs.cs


+ 0 - 0
Terminal.Gui/Input/GrabMouseEventArgs.cs → Terminal.Gui/Input/Mouse/GrabMouseEventArgs.cs


+ 20 - 0
Terminal.Gui/Input/Mouse/MouseBinding.cs

@@ -0,0 +1,20 @@
+#nullable enable
+
+namespace Terminal.Gui;
+
+/// <summary>
+/// Provides a collection of <see cref="Command"/> objects for mouse events.
+/// </summary>
+/// <seealso cref="Command"/>
+public record struct MouseBinding
+{
+    /// <summary>Initializes a new instance.</summary>
+    /// <param name="commands">The commands this mouse binding will invoke.</param>
+    public MouseBinding (Command [] commands)
+    {
+        Commands = commands;
+    }
+
+    /// <summary>The commands this key binding will invoke.</summary>
+    public Command [] Commands { get; set; }
+}

+ 216 - 0
Terminal.Gui/Input/Mouse/MouseBindings.cs

@@ -0,0 +1,216 @@
+#nullable enable
+namespace Terminal.Gui;
+
+/// <summary>
+///     Provides a collection of <see cref="MouseBinding"/> objects bound to a combination of <see cref="MouseFlags"/>.
+/// </summary>
+/// <seealso cref="View.MouseBindings"/>
+/// <seealso cref="Command"/>
+public class MouseBindings
+{
+    /// <summary>
+    ///     Initializes a new instance. This constructor is used when the <see cref="MouseBindings"/> are not bound to a
+    ///     <see cref="View"/>. This is used for Application.MouseBindings and unit tests.
+    /// </summary>
+    public MouseBindings () { }
+
+    /// <summary>Adds a <see cref="MouseBinding"/> to the collection.</summary>
+    /// <param name="mouseFlag"></param>
+    /// <param name="binding"></param>
+    public void Add (MouseFlags mouseFlag, MouseBinding binding)
+    {
+        if (TryGet (mouseFlag, out MouseBinding _))
+        {
+            throw new InvalidOperationException (@$"A binding for {mouseFlag} exists ({binding}).");
+
+            //Bindings [key] = binding;
+        }
+
+
+        // IMPORTANT: Add a COPY of the key. This is needed because ConfigurationManager.Apply uses DeepMemberWiseCopy 
+        // IMPORTANT: update the memory referenced by the key, and Dictionary uses caching for performance, and thus 
+        // IMPORTANT: Apply will update the Dictionary with the new key, but the old key will still be in the dictionary.
+        // IMPORTANT: See the ConfigurationManager.Illustrate_DeepMemberWiseCopy_Breaks_Dictionary test for details.
+        Bindings.Add (mouseFlag, binding);
+    }
+
+    /// <summary>
+    ///     <para>Adds a new mouse flag combination that will trigger the commands in <paramref name="commands"/>.</para>
+    ///     <para>
+    ///         If the key is already bound to a different array of <see cref="Command"/>s it will be rebound
+    ///         <paramref name="commands"/>.
+    ///     </para>
+    /// </summary>
+    /// <remarks>
+    ///     Commands are only ever applied to the current <see cref="View"/> (i.e. this feature cannot be used to switch
+    ///     focus to another view and perform multiple commands there).
+    /// </remarks>
+    /// <param name="mouseFlags">The mouse flags to check.</param>
+    /// <param name="commands">
+    ///     The command to invoked on the <see cref="View"/> when <paramref name="mouseFlags"/> is received. When
+    ///     multiple commands are provided,they will be applied in sequence. The bound <paramref name="mouseFlags"/> event will be
+    ///     consumed if any took effect.
+    /// </param>
+    public void Add (MouseFlags mouseFlags, params Command [] commands)
+    {
+        if (mouseFlags == MouseFlags.None)
+        {
+            throw new ArgumentException (@"Invalid MouseFlag", nameof (commands));
+        }
+
+        if (commands.Length == 0)
+        {
+            throw new ArgumentException (@"At least one command must be specified", nameof (commands));
+        }
+
+        if (TryGet (mouseFlags, out MouseBinding binding))
+        {
+            throw new InvalidOperationException (@$"A binding for {mouseFlags} exists ({binding}).");
+        }
+
+        Add (mouseFlags, new MouseBinding (commands));
+    }
+
+    // TODO: Add a dictionary comparer that ignores Scope
+    // TODO: This should not be public!
+    /// <summary>The collection of <see cref="MouseBinding"/> objects.</summary>
+    public Dictionary<MouseFlags, MouseBinding> Bindings { get; } = new ();
+
+    /// <summary>
+    ///     Gets the <see cref="MouseFlags"/> that are bound.
+    /// </summary>
+    /// <returns></returns>
+    public IEnumerable<MouseFlags> GetBoundMouseFlags ()
+    {
+        return Bindings.Keys;
+    }
+
+    /// <summary>Removes all <see cref="MouseBinding"/> objects from the collection.</summary>
+    public void Clear () { Bindings.Clear (); }
+
+    /// <summary>
+    ///     Removes all bindings that trigger the given command set. Views can have multiple different events bound to
+    ///     the same command sets and this method will clear all of them.
+    /// </summary>
+    /// <param name="command"></param>
+    public void Clear (params Command [] command)
+    {
+        KeyValuePair<MouseFlags, MouseBinding> [] kvps = Bindings
+                                                .Where (kvp => kvp.Value.Commands.SequenceEqual (command))
+                                                .ToArray ();
+
+        foreach (KeyValuePair<MouseFlags, MouseBinding> kvp in kvps)
+        {
+            Remove (kvp.Key);
+        }
+    }
+
+    /// <summary>Gets the <see cref="MouseBinding"/> for the specified combination of <see cref="MouseFlags"/>.</summary>
+    /// <param name="mouseFlags"></param>
+    /// <returns></returns>
+    public MouseBinding Get (MouseFlags mouseFlags)
+    {
+        if (TryGet (mouseFlags, out MouseBinding binding))
+        {
+            return binding;
+        }
+
+        throw new InvalidOperationException ($"{mouseFlags} is not bound.");
+    }
+
+    /// <summary>Gets the array of <see cref="Command"/>s bound to <paramref name="mouseFlags"/> if it exists.</summary>
+    /// <param name="mouseFlags">The key to check.</param>
+    /// <returns>
+    ///     The array of <see cref="Command"/>s if <paramref name="mouseFlags"/> is bound. An empty <see cref="Command"/> array
+    ///     if not.
+    /// </returns>
+    public Command [] GetCommands (MouseFlags mouseFlags)
+    {
+        if (TryGet (mouseFlags, out MouseBinding bindings))
+        {
+            return bindings.Commands;
+        }
+
+        return [];
+    }
+
+    /// <summary>Gets the first combination of <see cref="MouseFlags"/> bound to the set of commands specified by <paramref name="commands"/>.</summary>
+    /// <param name="commands">The set of commands to search.</param>
+    /// <returns>The first combination of <see cref="MouseFlags"/> bound to the set of commands specified by <paramref name="commands"/>. <see langword="null"/> if the set of caommands was not found.</returns>
+    public MouseFlags? GetMouseFlagsFromCommands (params Command [] commands)
+    {
+        return Bindings.FirstOrDefault (a => a.Value.Commands.SequenceEqual (commands)).Key;
+    }
+
+    /// <summary>Gets combination of <see cref="MouseFlags"/> bound to the set of commands specified by <paramref name="commands"/>.</summary>
+    /// <param name="commands">The set of commands to search.</param>
+    /// <returns>The combination of <see cref="MouseFlags"/> bound to the set of commands specified by <paramref name="commands"/>. An empty list if the set of caommands was not found.</returns>
+    public IEnumerable<MouseFlags> GetAllMouseFlagsFromCommands (params Command [] commands)
+    {
+        return Bindings.Where (a => a.Value.Commands.SequenceEqual (commands)).Select (a => a.Key);
+    }
+
+    /// <summary>Removes a <see cref="MouseBinding"/> from the collection.</summary>
+    /// <param name="mouseFlags"></param>
+    public void Remove (MouseFlags mouseFlags)
+    {
+        if (!TryGet (mouseFlags, out MouseBinding _))
+        {
+            return;
+        }
+
+        Bindings.Remove (mouseFlags);
+    }
+
+    /// <summary>Replaces the commands already bound to a combination of <see cref="MouseFlags"/>.</summary>
+    /// <remarks>
+    ///     <para>
+    ///         If the combination of <see cref="MouseFlags"/> is not already bound, it will be added.
+    ///     </para>
+    /// </remarks>
+    /// <param name="mouseFlags">The combination of <see cref="MouseFlags"/> bound to the command to be replaced.</param>
+    /// <param name="commands">The set of commands to replace the old ones with.</param>
+    public void ReplaceCommands (MouseFlags mouseFlags, params Command [] commands)
+    {
+        if (TryGet (mouseFlags, out MouseBinding binding))
+        {
+            binding.Commands = commands;
+        }
+        else
+        {
+            Add (mouseFlags, commands);
+        }
+    }
+
+    /// <summary>Replaces a <see cref="MouseFlags"/> combination already bound to a set of <see cref="Command"/>s.</summary>
+    /// <remarks></remarks>
+    /// <param name="oldMouseFlags">The <see cref="MouseFlags"/> to be replaced.</param>
+    /// <param name="newMouseFlags">The new <see cref="MouseFlags"/> to be used. If <see cref="Key.Empty"/> no action will be taken.</param>
+    public void ReplaceKey (MouseFlags oldMouseFlags, MouseFlags newMouseFlags)
+    {
+        if (!TryGet (oldMouseFlags, out MouseBinding _))
+        {
+            throw new InvalidOperationException ($"Key {oldMouseFlags} is not bound.");
+        }
+
+        MouseBinding value = Bindings [oldMouseFlags];
+        Remove (oldMouseFlags);
+        Add (newMouseFlags, value);
+    }
+
+    /// <summary>Gets the commands bound with the specified <see cref="MouseFlags"/>.</summary>
+    /// <remarks></remarks>
+    /// <param name="mouseFlags">The key to check.</param>
+    /// <param name="binding">
+    ///     When this method returns, contains the commands bound with the specified mouse flags, if the mouse flags are
+    ///     found; otherwise, null. This parameter is passed uninitialized.
+    /// </param>
+    /// <returns><see langword="true"/> if the mouse flags are bound; otherwise <see langword="false"/>.</returns>
+    public bool TryGet (MouseFlags mouseFlags, out MouseBinding binding)
+    {
+
+        binding = new ([]);
+
+        return Bindings.TryGetValue (mouseFlags, out binding);
+    }
+}

+ 0 - 0
Terminal.Gui/Input/MouseEventArgs.cs → Terminal.Gui/Input/Mouse/MouseEventArgs.cs


+ 0 - 0
Terminal.Gui/Input/MouseFlags.cs → Terminal.Gui/Input/Mouse/MouseFlags.cs


+ 0 - 0
Terminal.Gui/Input/MouseFlagsChangedEventArgs.cs → Terminal.Gui/Input/Mouse/MouseFlagsChangedEventArgs.cs


+ 0 - 12
Terminal.Gui/Input/PointEventArgs.cs

@@ -1,12 +0,0 @@
-namespace Terminal.Gui;
-
-/// <summary>Event args for events which relate to a single <see cref="Point"/></summary>
-public class PointEventArgs : EventArgs
-{
-    /// <summary>Creates a new instance of the <see cref="PointEventArgs"/> class</summary>
-    /// <param name="p"></param>
-    public PointEventArgs (Point p) { Point = p; }
-
-    /// <summary>The point the event happened at</summary>
-    public Point Point { get; }
-}

+ 12 - 32
Terminal.Gui/View/View.Command.cs

@@ -69,7 +69,7 @@ public partial class View // Command APIs
     ///     <see langword="false"/> if the event was raised and was not handled (or cancelled); input proessing should continue.
     ///     <see langword="true"/> if the event was raised and handled (or cancelled); input proessing should stop.
     /// </returns>
-    protected bool? RaiseAccepting (CommandContext ctx)
+    protected bool? RaiseAccepting (ICommandContext ctx)
     {
         CommandEventArgs args = new () { Context = ctx };
 
@@ -93,14 +93,14 @@ public partial class View // Command APIs
 
             if (isDefaultView != this && isDefaultView is Button { IsDefault: true } button)
             {
-                bool? handled = isDefaultView.InvokeCommand (Command.Accept, ctx: new (Command.Accept, null, null, this));
+                bool? handled = isDefaultView.InvokeCommand<KeyBinding> (Command.Accept, new ([Command.Accept], 0, null, this));
                 if (handled == true)
                 {
                     return true;
                 }
             }
 
-            return SuperView?.InvokeCommand (Command.Accept, ctx: new (Command.Accept, null, null, this)) == true;
+            return SuperView?.InvokeCommand<KeyBinding> (Command.Accept, new ([Command.Accept], 0, null, this)) == true;
         }
 
         return Accepting is null ? null : args.Cancel;
@@ -142,7 +142,7 @@ public partial class View // Command APIs
     ///     <see langword="false"/> if the event was raised and was not handled (or cancelled); input proessing should continue.
     ///     <see langword="true"/> if the event was raised and handled (or cancelled); input proessing should stop.
     /// </returns>
-    protected bool? RaiseSelecting (CommandContext ctx)
+    protected bool? RaiseSelecting (ICommandContext ctx)
     {
         CommandEventArgs args = new () { Context = ctx };
 
@@ -185,7 +185,7 @@ public partial class View // Command APIs
     /// </returns>
     protected bool? RaiseHandlingHotKey ()
     {
-        CommandEventArgs args = new ();
+        CommandEventArgs args = new () { Context = new CommandContext<KeyBinding> () { Command = Command.HotKey } };
 
         // Best practice is to invoke the virtual method first.
         // This allows derived classes to handle the event and potentially cancel it.
@@ -225,7 +225,7 @@ public partial class View // Command APIs
     ///     <see langword="false"/> if the command was invoked and was not handled (or cancelled); input proessing should continue.
     ///     <see langword="true"/> if the command was invoked the command was handled (or cancelled); input proessing should stop.
     /// </returns>
-    public delegate bool? CommandImplementation (CommandContext ctx);
+    public delegate bool? CommandImplementation (ICommandContext? ctx);
 
     /// <summary>
     ///     <para>
@@ -282,7 +282,7 @@ public partial class View // Command APIs
     ///     <see langword="false"/> if at least one command was invoked and was not handled (or cancelled); input proessing should continue.
     ///     <see langword="true"/> if at least one command was invoked the command was handled (or cancelled); input proessing should stop.
     /// </returns>
-    public bool? InvokeCommands (Command [] commands, Key? key = null, KeyBinding? keyBinding = null)
+    public bool? InvokeCommands<TBindingType> (Command [] commands, TBindingType binding)
     {
         bool? toReturn = null;
 
@@ -291,12 +291,12 @@ public partial class View // Command APIs
             if (!CommandImplementations.ContainsKey (command))
             {
                 throw new NotSupportedException (
-                                                 @$"A KeyBinding was set up for the command {command} ({key}) but that command is not supported by this View ({GetType ().Name})"
+                                                 @$"A Binding was set up for the command {command} ({binding}) but that command is not supported by this View ({GetType ().Name})"
                                                 );
             }
 
             // each command has its own return value
-            bool? thisReturn = InvokeCommand (command, key, keyBinding);
+            bool? thisReturn = InvokeCommand<TBindingType> (command, binding);
 
             // if we haven't got anything yet, the current command result should be used
             toReturn ??= thisReturn;
@@ -311,41 +311,21 @@ public partial class View // Command APIs
         return toReturn;
     }
 
-    /// <summary>Invokes the specified command.</summary>
-    /// <param name="command">The command to invoke.</param>
-    /// <param name="key">The key that caused the command to be invoked, if any. This will be passed as context with the command.</param>
-    /// <param name="keyBinding">The key binding that was bound to the key and caused the invocation, if any. This will be passed as context with the command.</param>
-    /// <returns>
-    ///     <see langword="null"/> if no command was found; input proessing should continue.
-    ///     <see langword="false"/> if the command was invoked and was not handled (or cancelled); input proessing should continue.
-    ///     <see langword="true"/> if the command was invoked the command was handled (or cancelled); input proessing should stop.
-    /// </returns>
-    public bool? InvokeCommand (Command command, Key? key = null, KeyBinding? keyBinding = null)
-    {
-        if (CommandImplementations.TryGetValue (command, out CommandImplementation? implementation))
-        {
-            var context = new CommandContext (command, key, keyBinding); // Create the context here
-            return implementation (context);
-        }
-
-        return null;
-    }
-
     /// <summary>
     /// Invokes the specified command.
     /// </summary>
     /// <param name="command">The command to invoke.</param>
-    /// <param name="ctx">Context to pass with the invocation.</param>
+    /// <param name="binding"></param>
     /// <returns>
     ///     <see langword="null"/> if no command was found; input proessing should continue.
     ///     <see langword="false"/> if the command was invoked and was not handled (or cancelled); input proessing should continue.
     ///     <see langword="true"/> if the command was invoked the command was handled (or cancelled); input proessing should stop.
     /// </returns>
-    public bool? InvokeCommand (Command command, CommandContext ctx)
+    public bool? InvokeCommand<TBindingType> (Command command, TBindingType binding)
     {
         if (CommandImplementations.TryGetValue (command, out CommandImplementation? implementation))
         {
-            return implementation (ctx);
+            return implementation (binding as ICommandContext);
         }
 
         return null;

+ 1 - 1
Terminal.Gui/View/View.Keyboard.cs

@@ -705,7 +705,7 @@ public partial class View // Keyboard APIs
         }
 
 #endif
-        return InvokeCommands (binding.Commands, key, binding);
+        return InvokeCommands<KeyBinding> (binding.Commands, binding);
     }
 
     #endregion Key Bindings

+ 15 - 15
Terminal.Gui/Views/TextView.cs

@@ -3710,8 +3710,8 @@ public class TextView : View
     /// <summary>Invoke the <see cref="UnwrappedCursorPosition"/> event with the unwrapped <see cref="CursorPosition"/>.</summary>
     public virtual void OnUnwrappedCursorPosition (int? cRow = null, int? cCol = null)
     {
-        int? row = cRow is null ? CurrentRow : cRow;
-        int? col = cCol is null ? CurrentColumn : cCol;
+        int? row = cRow ?? CurrentRow;
+        int? col = cCol ?? CurrentColumn;
 
         if (cRow is null && cCol is null && _wordWrap)
         {
@@ -3719,7 +3719,7 @@ public class TextView : View
             col = _wrapManager.GetModelColFromWrappedLines (CurrentRow, CurrentColumn);
         }
 
-        UnwrappedCursorPosition?.Invoke (this, new (new ((int)col, (int)row)));
+        UnwrappedCursorPosition?.Invoke (this, new Point (row.Value, col.Value));
     }
 
     /// <summary>Paste the clipboard contents into the current selected position.</summary>
@@ -3956,7 +3956,7 @@ public class TextView : View
     }
 
     /// <summary>Invoked with the unwrapped <see cref="CursorPosition"/>.</summary>
-    public event EventHandler<PointEventArgs>? UnwrappedCursorPosition;
+    public event EventHandler<Point>? UnwrappedCursorPosition;
 
     /// <summary>
     ///     Sets the <see cref="View.Driver"/> to an appropriate color for rendering the given <paramref name="idxCol"/>
@@ -4465,12 +4465,12 @@ public class TextView : View
         }
         else
         {
-            _historyText.Add ([[.. currentLine]], CursorPosition);
+            _historyText.Add ([ [.. currentLine]], CursorPosition);
 
             currentLine.RemoveAt (CurrentColumn);
 
             _historyText.Add (
-                              [[.. currentLine]],
+                              [ [.. currentLine]],
                               CursorPosition,
                               HistoryText.LineStatus.Replaced
                              );
@@ -5057,7 +5057,7 @@ public class TextView : View
         }
 
         _historyText.Add (
-                          [[.. GetCurrentLine ()]],
+                          [ [.. GetCurrentLine ()]],
                           CursorPosition,
                           HistoryText.LineStatus.Replaced
                          );
@@ -5097,7 +5097,7 @@ public class TextView : View
             return;
         }
 
-        _historyText.Add ([[.. currentLine]], CursorPosition);
+        _historyText.Add ([ [.. currentLine]], CursorPosition);
 
         if (currentLine.Count == 0)
         {
@@ -5164,7 +5164,7 @@ public class TextView : View
         }
 
         _historyText.Add (
-                          [[.. GetCurrentLine ()]],
+                          [ [.. GetCurrentLine ()]],
                           CursorPosition,
                           HistoryText.LineStatus.Replaced
                          );
@@ -5188,14 +5188,14 @@ public class TextView : View
 
         List<Cell> currentLine = GetCurrentLine ();
 
-        _historyText.Add ([[.. GetCurrentLine ()]], CursorPosition);
+        _historyText.Add ([ [.. GetCurrentLine ()]], CursorPosition);
 
         if (CurrentColumn == 0)
         {
             DeleteTextBackwards ();
 
             _historyText.ReplaceLast (
-                                      [[.. GetCurrentLine ()]],
+                                      [ [.. GetCurrentLine ()]],
                                       CursorPosition,
                                       HistoryText.LineStatus.Replaced
                                      );
@@ -5234,7 +5234,7 @@ public class TextView : View
         }
 
         _historyText.Add (
-                          [[.. GetCurrentLine ()]],
+                          [ [.. GetCurrentLine ()]],
                           CursorPosition,
                           HistoryText.LineStatus.Replaced
                          );
@@ -5256,14 +5256,14 @@ public class TextView : View
 
         List<Cell> currentLine = GetCurrentLine ();
 
-        _historyText.Add ([[.. GetCurrentLine ()]], CursorPosition);
+        _historyText.Add ([ [.. GetCurrentLine ()]], CursorPosition);
 
         if (currentLine.Count == 0 || CurrentColumn == currentLine.Count)
         {
             DeleteTextForwards ();
 
             _historyText.ReplaceLast (
-                                      [[.. GetCurrentLine ()]],
+                                      [ [.. GetCurrentLine ()]],
                                       CursorPosition,
                                       HistoryText.LineStatus.Replaced
                                      );
@@ -5293,7 +5293,7 @@ public class TextView : View
         }
 
         _historyText.Add (
-                          [[.. GetCurrentLine ()]],
+                          [ [.. GetCurrentLine ()]],
                           CursorPosition,
                           HistoryText.LineStatus.Replaced
                          );