aboutsummaryrefslogtreecommitdiffstats
path: root/Software/Visual_Studio/Scripting/Tango.Scripting.Editors/Editing/CaretNavigationCommandHandler.cs
diff options
context:
space:
mode:
authorRoy Ben Shabat <Roy.mail.net@gmail.com>2019-04-09 01:47:48 +0300
committerRoy Ben Shabat <Roy.mail.net@gmail.com>2019-04-09 01:47:48 +0300
commit080f1697e97e13461ec6df4d31c8924d01257a1b (patch)
treeb1fe0285de7bc9bc52e9e2195e66fe022bf8f5b3 /Software/Visual_Studio/Scripting/Tango.Scripting.Editors/Editing/CaretNavigationCommandHandler.cs
parent1608e69a417bc5e40a607c3958c4a60f19f66f1a (diff)
downloadTango-080f1697e97e13461ec6df4d31c8924d01257a1b.tar.gz
Tango-080f1697e97e13461ec6df4d31c8924d01257a1b.zip
MERGE
Diffstat (limited to 'Software/Visual_Studio/Scripting/Tango.Scripting.Editors/Editing/CaretNavigationCommandHandler.cs')
-rw-r--r--Software/Visual_Studio/Scripting/Tango.Scripting.Editors/Editing/CaretNavigationCommandHandler.cs375
1 files changed, 375 insertions, 0 deletions
diff --git a/Software/Visual_Studio/Scripting/Tango.Scripting.Editors/Editing/CaretNavigationCommandHandler.cs b/Software/Visual_Studio/Scripting/Tango.Scripting.Editors/Editing/CaretNavigationCommandHandler.cs
new file mode 100644
index 000000000..d1b812b66
--- /dev/null
+++ b/Software/Visual_Studio/Scripting/Tango.Scripting.Editors/Editing/CaretNavigationCommandHandler.cs
@@ -0,0 +1,375 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Windows;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media.TextFormatting;
+using Tango.Scripting.Editors.Document;
+using Tango.Scripting.Editors.Rendering;
+using Tango.Scripting.Editors.Utils;
+
+namespace Tango.Scripting.Editors.Editing
+{
+ static class CaretNavigationCommandHandler
+ {
+ /// <summary>
+ /// Creates a new <see cref="TextAreaInputHandler"/> for the text area.
+ /// </summary>
+ public static TextAreaInputHandler Create(TextArea textArea)
+ {
+ TextAreaInputHandler handler = new TextAreaInputHandler(textArea);
+ handler.CommandBindings.AddRange(CommandBindings);
+ handler.InputBindings.AddRange(InputBindings);
+ return handler;
+ }
+
+ static readonly List<CommandBinding> CommandBindings = new List<CommandBinding>();
+ static readonly List<InputBinding> InputBindings = new List<InputBinding>();
+
+ static void AddBinding(ICommand command, ModifierKeys modifiers, Key key, ExecutedRoutedEventHandler handler)
+ {
+ CommandBindings.Add(new CommandBinding(command, handler));
+ InputBindings.Add(TextAreaDefaultInputHandler.CreateFrozenKeyBinding(command, modifiers, key));
+ }
+
+ static CaretNavigationCommandHandler()
+ {
+ const ModifierKeys None = ModifierKeys.None;
+ const ModifierKeys Ctrl = ModifierKeys.Control;
+ const ModifierKeys Shift = ModifierKeys.Shift;
+ const ModifierKeys Alt = ModifierKeys.Alt;
+
+ AddBinding(EditingCommands.MoveLeftByCharacter, None, Key.Left, OnMoveCaret(CaretMovementType.CharLeft));
+ AddBinding(EditingCommands.SelectLeftByCharacter, Shift, Key.Left, OnMoveCaretExtendSelection(CaretMovementType.CharLeft));
+ AddBinding(RectangleSelection.BoxSelectLeftByCharacter, Alt | Shift, Key.Left, OnMoveCaretBoxSelection(CaretMovementType.CharLeft));
+ AddBinding(EditingCommands.MoveRightByCharacter, None, Key.Right, OnMoveCaret(CaretMovementType.CharRight));
+ AddBinding(EditingCommands.SelectRightByCharacter, Shift, Key.Right, OnMoveCaretExtendSelection(CaretMovementType.CharRight));
+ AddBinding(RectangleSelection.BoxSelectRightByCharacter, Alt | Shift, Key.Right, OnMoveCaretBoxSelection(CaretMovementType.CharRight));
+
+ AddBinding(EditingCommands.MoveLeftByWord, Ctrl, Key.Left, OnMoveCaret(CaretMovementType.WordLeft));
+ AddBinding(EditingCommands.SelectLeftByWord, Ctrl | Shift, Key.Left, OnMoveCaretExtendSelection(CaretMovementType.WordLeft));
+ AddBinding(RectangleSelection.BoxSelectLeftByWord, Ctrl | Alt | Shift, Key.Left, OnMoveCaretBoxSelection(CaretMovementType.WordLeft));
+ AddBinding(EditingCommands.MoveRightByWord, Ctrl, Key.Right, OnMoveCaret(CaretMovementType.WordRight));
+ AddBinding(EditingCommands.SelectRightByWord, Ctrl | Shift, Key.Right, OnMoveCaretExtendSelection(CaretMovementType.WordRight));
+ AddBinding(RectangleSelection.BoxSelectRightByWord, Ctrl | Alt | Shift, Key.Right, OnMoveCaretBoxSelection(CaretMovementType.WordRight));
+
+ AddBinding(EditingCommands.MoveUpByLine, None, Key.Up, OnMoveCaret(CaretMovementType.LineUp));
+ AddBinding(EditingCommands.SelectUpByLine, Shift, Key.Up, OnMoveCaretExtendSelection(CaretMovementType.LineUp));
+ AddBinding(RectangleSelection.BoxSelectUpByLine, Alt | Shift, Key.Up, OnMoveCaretBoxSelection(CaretMovementType.LineUp));
+ AddBinding(EditingCommands.MoveDownByLine, None, Key.Down, OnMoveCaret(CaretMovementType.LineDown));
+ AddBinding(EditingCommands.SelectDownByLine, Shift, Key.Down, OnMoveCaretExtendSelection(CaretMovementType.LineDown));
+ AddBinding(RectangleSelection.BoxSelectDownByLine, Alt | Shift, Key.Down, OnMoveCaretBoxSelection(CaretMovementType.LineDown));
+
+ AddBinding(EditingCommands.MoveDownByPage, None, Key.PageDown, OnMoveCaret(CaretMovementType.PageDown));
+ AddBinding(EditingCommands.SelectDownByPage, Shift, Key.PageDown, OnMoveCaretExtendSelection(CaretMovementType.PageDown));
+ AddBinding(EditingCommands.MoveUpByPage, None, Key.PageUp, OnMoveCaret(CaretMovementType.PageUp));
+ AddBinding(EditingCommands.SelectUpByPage, Shift, Key.PageUp, OnMoveCaretExtendSelection(CaretMovementType.PageUp));
+
+ AddBinding(EditingCommands.MoveToLineStart, None, Key.Home, OnMoveCaret(CaretMovementType.LineStart));
+ AddBinding(EditingCommands.SelectToLineStart, Shift, Key.Home, OnMoveCaretExtendSelection(CaretMovementType.LineStart));
+ AddBinding(RectangleSelection.BoxSelectToLineStart, Alt | Shift, Key.Home, OnMoveCaretBoxSelection(CaretMovementType.LineStart));
+ AddBinding(EditingCommands.MoveToLineEnd, None, Key.End, OnMoveCaret(CaretMovementType.LineEnd));
+ AddBinding(EditingCommands.SelectToLineEnd, Shift, Key.End, OnMoveCaretExtendSelection(CaretMovementType.LineEnd));
+ AddBinding(RectangleSelection.BoxSelectToLineEnd, Alt | Shift, Key.End, OnMoveCaretBoxSelection(CaretMovementType.LineEnd));
+
+ AddBinding(EditingCommands.MoveToDocumentStart, Ctrl, Key.Home, OnMoveCaret(CaretMovementType.DocumentStart));
+ AddBinding(EditingCommands.SelectToDocumentStart, Ctrl | Shift, Key.Home, OnMoveCaretExtendSelection(CaretMovementType.DocumentStart));
+ AddBinding(EditingCommands.MoveToDocumentEnd, Ctrl, Key.End, OnMoveCaret(CaretMovementType.DocumentEnd));
+ AddBinding(EditingCommands.SelectToDocumentEnd, Ctrl | Shift, Key.End, OnMoveCaretExtendSelection(CaretMovementType.DocumentEnd));
+
+ CommandBindings.Add(new CommandBinding(ApplicationCommands.SelectAll, OnSelectAll));
+
+ TextAreaDefaultInputHandler.WorkaroundWPFMemoryLeak(InputBindings);
+ }
+
+ static void OnSelectAll(object target, ExecutedRoutedEventArgs args)
+ {
+ TextArea textArea = GetTextArea(target);
+ if (textArea != null && textArea.Document != null) {
+ args.Handled = true;
+ textArea.Caret.Offset = textArea.Document.TextLength;
+ textArea.Selection = SimpleSelection.Create(textArea, 0, textArea.Document.TextLength);
+ }
+ }
+
+ static TextArea GetTextArea(object target)
+ {
+ return target as TextArea;
+ }
+
+ enum CaretMovementType
+ {
+ CharLeft,
+ CharRight,
+ WordLeft,
+ WordRight,
+ LineUp,
+ LineDown,
+ PageUp,
+ PageDown,
+ LineStart,
+ LineEnd,
+ DocumentStart,
+ DocumentEnd
+ }
+
+ static ExecutedRoutedEventHandler OnMoveCaret(CaretMovementType direction)
+ {
+ return (target, args) => {
+ TextArea textArea = GetTextArea(target);
+ if (textArea != null && textArea.Document != null) {
+ args.Handled = true;
+ textArea.ClearSelection();
+ MoveCaret(textArea, direction);
+ textArea.Caret.BringCaretToView();
+ }
+ };
+ }
+
+ static ExecutedRoutedEventHandler OnMoveCaretExtendSelection(CaretMovementType direction)
+ {
+ return (target, args) => {
+ TextArea textArea = GetTextArea(target);
+ if (textArea != null && textArea.Document != null) {
+ args.Handled = true;
+ TextViewPosition oldPosition = textArea.Caret.Position;
+ MoveCaret(textArea, direction);
+ textArea.Selection = textArea.Selection.StartSelectionOrSetEndpoint(oldPosition, textArea.Caret.Position);
+ if (!textArea.Document.IsInUpdate) // if we're inside a larger update (e.g. called by EditingCommandHandler.OnDelete()), avoid calculating the caret rectangle now
+ textArea.Caret.BringCaretToView();
+ }
+ };
+ }
+
+ static ExecutedRoutedEventHandler OnMoveCaretBoxSelection(CaretMovementType direction)
+ {
+ return (target, args) => {
+ TextArea textArea = GetTextArea(target);
+ if (textArea != null && textArea.Document != null) {
+ args.Handled = true;
+ // First, convert the selection into a rectangle selection
+ // (this is required so that virtual space gets enabled for the caret movement)
+ if (textArea.Options.EnableRectangularSelection && !(textArea.Selection is RectangleSelection)) {
+ if (textArea.Selection.IsEmpty) {
+ textArea.Selection = new RectangleSelection(textArea, textArea.Caret.Position, textArea.Caret.Position);
+ } else {
+ // Convert normal selection to rectangle selection
+ textArea.Selection = new RectangleSelection(textArea, textArea.Selection.StartPosition, textArea.Caret.Position);
+ }
+ }
+ // Now move the caret and extend the selection
+ TextViewPosition oldPosition = textArea.Caret.Position;
+ MoveCaret(textArea, direction);
+ textArea.Selection = textArea.Selection.StartSelectionOrSetEndpoint(oldPosition, textArea.Caret.Position);
+ textArea.Caret.BringCaretToView();
+ }
+ };
+ }
+
+ #region Caret movement
+ static void MoveCaret(TextArea textArea, CaretMovementType direction)
+ {
+ DocumentLine caretLine = textArea.Document.GetLineByNumber(textArea.Caret.Line);
+ VisualLine visualLine = textArea.TextView.GetOrConstructVisualLine(caretLine);
+ TextViewPosition caretPosition = textArea.Caret.Position;
+ TextLine textLine = visualLine.GetTextLine(caretPosition.VisualColumn);
+ switch (direction) {
+ case CaretMovementType.CharLeft:
+ MoveCaretLeft(textArea, caretPosition, visualLine, CaretPositioningMode.Normal);
+ break;
+ case CaretMovementType.CharRight:
+ MoveCaretRight(textArea, caretPosition, visualLine, CaretPositioningMode.Normal);
+ break;
+ case CaretMovementType.WordLeft:
+ MoveCaretLeft(textArea, caretPosition, visualLine, CaretPositioningMode.WordStart);
+ break;
+ case CaretMovementType.WordRight:
+ MoveCaretRight(textArea, caretPosition, visualLine, CaretPositioningMode.WordStart);
+ break;
+ case CaretMovementType.LineUp:
+ case CaretMovementType.LineDown:
+ case CaretMovementType.PageUp:
+ case CaretMovementType.PageDown:
+ MoveCaretUpDown(textArea, direction, visualLine, textLine, caretPosition.VisualColumn);
+ break;
+ case CaretMovementType.DocumentStart:
+ SetCaretPosition(textArea, 0, 0);
+ break;
+ case CaretMovementType.DocumentEnd:
+ SetCaretPosition(textArea, -1, textArea.Document.TextLength);
+ break;
+ case CaretMovementType.LineStart:
+ MoveCaretToStartOfLine(textArea, visualLine);
+ break;
+ case CaretMovementType.LineEnd:
+ MoveCaretToEndOfLine(textArea, visualLine);
+ break;
+ default:
+ throw new NotSupportedException(direction.ToString());
+ }
+ }
+ #endregion
+
+ #region Home/End
+ static void MoveCaretToStartOfLine(TextArea textArea, VisualLine visualLine)
+ {
+ int newVC = visualLine.GetNextCaretPosition(-1, LogicalDirection.Forward, CaretPositioningMode.WordStart, textArea.Selection.EnableVirtualSpace);
+ if (newVC < 0)
+ throw ThrowUtil.NoValidCaretPosition();
+ // when the caret is already at the start of the text, jump to start before whitespace
+ if (newVC == textArea.Caret.VisualColumn)
+ newVC = 0;
+ int offset = visualLine.FirstDocumentLine.Offset + visualLine.GetRelativeOffset(newVC);
+ SetCaretPosition(textArea, newVC, offset);
+ }
+
+ static void MoveCaretToEndOfLine(TextArea textArea, VisualLine visualLine)
+ {
+ int newVC = visualLine.VisualLength;
+ int offset = visualLine.FirstDocumentLine.Offset + visualLine.GetRelativeOffset(newVC);
+ SetCaretPosition(textArea, newVC, offset);
+ }
+ #endregion
+
+ #region By-character / By-word movement
+ static void MoveCaretRight(TextArea textArea, TextViewPosition caretPosition, VisualLine visualLine, CaretPositioningMode mode)
+ {
+ int pos = visualLine.GetNextCaretPosition(caretPosition.VisualColumn, LogicalDirection.Forward, mode, textArea.Selection.EnableVirtualSpace);
+ if (pos >= 0) {
+ SetCaretPosition(textArea, pos, visualLine.GetRelativeOffset(pos) + visualLine.FirstDocumentLine.Offset);
+ } else {
+ // move to start of next line
+ DocumentLine nextDocumentLine = visualLine.LastDocumentLine.NextLine;
+ if (nextDocumentLine != null) {
+ VisualLine nextLine = textArea.TextView.GetOrConstructVisualLine(nextDocumentLine);
+ pos = nextLine.GetNextCaretPosition(-1, LogicalDirection.Forward, mode, textArea.Selection.EnableVirtualSpace);
+ if (pos < 0)
+ throw ThrowUtil.NoValidCaretPosition();
+ SetCaretPosition(textArea, pos, nextLine.GetRelativeOffset(pos) + nextLine.FirstDocumentLine.Offset);
+ } else {
+ // at end of document
+ Debug.Assert(visualLine.LastDocumentLine.Offset + visualLine.LastDocumentLine.TotalLength == textArea.Document.TextLength);
+ SetCaretPosition(textArea, -1, textArea.Document.TextLength);
+ }
+ }
+ }
+
+ static void MoveCaretLeft(TextArea textArea, TextViewPosition caretPosition, VisualLine visualLine, CaretPositioningMode mode)
+ {
+ int pos = visualLine.GetNextCaretPosition(caretPosition.VisualColumn, LogicalDirection.Backward, mode, textArea.Selection.EnableVirtualSpace);
+ if (pos >= 0) {
+ SetCaretPosition(textArea, pos, visualLine.GetRelativeOffset(pos) + visualLine.FirstDocumentLine.Offset);
+ } else {
+ // move to end of previous line
+ DocumentLine previousDocumentLine = visualLine.FirstDocumentLine.PreviousLine;
+ if (previousDocumentLine != null) {
+ VisualLine previousLine = textArea.TextView.GetOrConstructVisualLine(previousDocumentLine);
+ pos = previousLine.GetNextCaretPosition(previousLine.VisualLength + 1, LogicalDirection.Backward, mode, textArea.Selection.EnableVirtualSpace);
+ if (pos < 0)
+ throw ThrowUtil.NoValidCaretPosition();
+ SetCaretPosition(textArea, pos, previousLine.GetRelativeOffset(pos) + previousLine.FirstDocumentLine.Offset);
+ } else {
+ // at start of document
+ Debug.Assert(visualLine.FirstDocumentLine.Offset == 0);
+ SetCaretPosition(textArea, 0, 0);
+ }
+ }
+ }
+ #endregion
+
+ #region Line+Page up/down
+ static void MoveCaretUpDown(TextArea textArea, CaretMovementType direction, VisualLine visualLine, TextLine textLine, int caretVisualColumn)
+ {
+ // moving up/down happens using the desired visual X position
+ double xPos = textArea.Caret.DesiredXPos;
+ if (double.IsNaN(xPos))
+ xPos = visualLine.GetTextLineVisualXPosition(textLine, caretVisualColumn);
+ // now find the TextLine+VisualLine where the caret will end up in
+ VisualLine targetVisualLine = visualLine;
+ TextLine targetLine;
+ int textLineIndex = visualLine.TextLines.IndexOf(textLine);
+ switch (direction) {
+ case CaretMovementType.LineUp:
+ {
+ // Move up: move to the previous TextLine in the same visual line
+ // or move to the last TextLine of the previous visual line
+ int prevLineNumber = visualLine.FirstDocumentLine.LineNumber - 1;
+ if (textLineIndex > 0) {
+ targetLine = visualLine.TextLines[textLineIndex - 1];
+ } else if (prevLineNumber >= 1) {
+ DocumentLine prevLine = textArea.Document.GetLineByNumber(prevLineNumber);
+ targetVisualLine = textArea.TextView.GetOrConstructVisualLine(prevLine);
+ targetLine = targetVisualLine.TextLines[targetVisualLine.TextLines.Count - 1];
+ } else {
+ targetLine = null;
+ }
+ break;
+ }
+ case CaretMovementType.LineDown:
+ {
+ // Move down: move to the next TextLine in the same visual line
+ // or move to the first TextLine of the next visual line
+ int nextLineNumber = visualLine.LastDocumentLine.LineNumber + 1;
+ if (textLineIndex < visualLine.TextLines.Count - 1) {
+ targetLine = visualLine.TextLines[textLineIndex + 1];
+ } else if (nextLineNumber <= textArea.Document.LineCount) {
+ DocumentLine nextLine = textArea.Document.GetLineByNumber(nextLineNumber);
+ targetVisualLine = textArea.TextView.GetOrConstructVisualLine(nextLine);
+ targetLine = targetVisualLine.TextLines[0];
+ } else {
+ targetLine = null;
+ }
+ break;
+ }
+ case CaretMovementType.PageUp:
+ case CaretMovementType.PageDown:
+ {
+ // Page up/down: find the target line using its visual position
+ double yPos = visualLine.GetTextLineVisualYPosition(textLine, VisualYPosition.LineMiddle);
+ if (direction == CaretMovementType.PageUp)
+ yPos -= textArea.TextView.RenderSize.Height;
+ else
+ yPos += textArea.TextView.RenderSize.Height;
+ DocumentLine newLine = textArea.TextView.GetDocumentLineByVisualTop(yPos);
+ targetVisualLine = textArea.TextView.GetOrConstructVisualLine(newLine);
+ targetLine = targetVisualLine.GetTextLineByVisualYPosition(yPos);
+ break;
+ }
+ default:
+ throw new NotSupportedException(direction.ToString());
+ }
+ if (targetLine != null) {
+ double yPos = targetVisualLine.GetTextLineVisualYPosition(targetLine, VisualYPosition.LineMiddle);
+ int newVisualColumn = targetVisualLine.GetVisualColumn(new Point(xPos, yPos), textArea.Selection.EnableVirtualSpace);
+ SetCaretPosition(textArea, targetVisualLine, targetLine, newVisualColumn, false);
+ textArea.Caret.DesiredXPos = xPos;
+ }
+ }
+ #endregion
+
+ #region SetCaretPosition
+ static void SetCaretPosition(TextArea textArea, VisualLine targetVisualLine, TextLine targetLine,
+ int newVisualColumn, bool allowWrapToNextLine)
+ {
+ int targetLineStartCol = targetVisualLine.GetTextLineVisualStartColumn(targetLine);
+ if (!allowWrapToNextLine && newVisualColumn >= targetLineStartCol + targetLine.Length) {
+ if (newVisualColumn <= targetVisualLine.VisualLength)
+ newVisualColumn = targetLineStartCol + targetLine.Length - 1;
+ }
+ int newOffset = targetVisualLine.GetRelativeOffset(newVisualColumn) + targetVisualLine.FirstDocumentLine.Offset;
+ SetCaretPosition(textArea, newVisualColumn, newOffset);
+ }
+
+ static void SetCaretPosition(TextArea textArea, int newVisualColumn, int newOffset)
+ {
+ textArea.Caret.Position = new TextViewPosition(textArea.Document.GetLocation(newOffset), newVisualColumn);
+ textArea.Caret.DesiredXPos = double.NaN;
+ }
+ #endregion
+ }
+}