/// <inheritdoc/> public override int GetNextCaretPosition(int visualColumn, LogicalDirection direction, CaretPositioningMode mode) { int textOffset = parentVisualLine.StartOffset + this.RelativeTextOffset; int pos = TextUtilities.GetNextCaretPosition(parentVisualLine.Document, textOffset + visualColumn - this.VisualColumn, direction, mode); if (pos < textOffset || pos > textOffset + this.DocumentLength) return -1; else return this.VisualColumn + pos - textOffset; }
public override int GetNextCaretPosition(int visualColumn, LogicalDirection direction, CaretPositioningMode mode) { // only place a caret stop before the newline, no caret stop after it if (visualColumn > this.VisualColumn && direction == LogicalDirection.Backward || visualColumn < this.VisualColumn && direction == LogicalDirection.Forward) { return this.VisualColumn; } else { return -1; } }
/// <summary> /// Gets the next caret position inside this element. /// </summary> /// <param name="visualColumn">The visual column from which the search should be started.</param> /// <param name="direction">The search direction (forwards or backwards).</param> /// <param name="mode">Whether to stop only at word borders.</param> /// <returns>The visual column of the next caret position, or -1 if there is no next caret position.</returns> /// <remarks> /// In the space between two line elements, it is sufficient that one of them contains a caret position; /// though in many cases, both of them contain one. /// </remarks> public virtual int GetNextCaretPosition(int visualColumn, LogicalDirection direction, CaretPositioningMode mode) { int stop1 = this.VisualColumn; int stop2 = this.VisualColumn + this.VisualLength; if (direction == LogicalDirection.Backward) { if (visualColumn > stop2 && mode != CaretPositioningMode.WordStart && mode != CaretPositioningMode.WordStartOrSymbol) { return(stop2); } else if (visualColumn > stop1) { return(stop1); } } else { if (visualColumn < stop1) { return(stop1); } else if (visualColumn < stop2 && mode != CaretPositioningMode.WordStart && mode != CaretPositioningMode.WordStartOrSymbol) { return(stop2); } } return(-1); }
/// <summary> /// Gets the next caret position inside this element. /// </summary> /// <param name="visualColumn">The visual column from which the search should be started.</param> /// <param name="direction">The search direction (forwards or backwards).</param> /// <param name="mode">Whether to stop only at word borders.</param> /// <returns>The visual column of the next caret position, or -1 if there is no next caret position.</returns> /// <remarks> /// In the space between two line elements, it is sufficient that one of them contains a caret position; /// though in many cases, both of them contain one. /// </remarks> public virtual int GetNextCaretPosition(int visualColumn, LogicalDirection direction, CaretPositioningMode mode) { int stop1 = VisualColumn; int stop2 = VisualColumn + VisualLength; if (direction == LogicalDirection.Backward) { if (visualColumn > stop2 && mode != CaretPositioningMode.WordStart && mode != CaretPositioningMode.WordStartOrSymbol) return stop2; else if (visualColumn > stop1) return stop1; } else { if (visualColumn < stop1) return stop1; else if (visualColumn < stop2 && mode != CaretPositioningMode.WordStart && mode != CaretPositioningMode.WordStartOrSymbol) return stop2; } return -1; }
public override int GetNextCaretPosition(int visualColumn, bool backwords, CaretPositioningMode mode) { return base.GetNextCaretPosition(visualColumn, backwords, mode); }
static void MoveCaretRight(TextArea textArea, TextViewPosition caretPosition, VisualLine visualLine, CaretPositioningMode mode) { int pos = visualLine.GetNextCaretPosition(caretPosition.VisualColumn, LogicalDirection.Forward, mode); 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); 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 bool IsNormal(CaretPositioningMode mode) { return mode == CaretPositioningMode.Normal || mode == CaretPositioningMode.EveryCodepoint; }
private static bool HasStopsInVirtualSpace(CaretPositioningMode mode) { return(mode == CaretPositioningMode.Normal || mode == CaretPositioningMode.EveryCodepoint); }
public int GetNextCaretPosition(int visualColumn, bool backwords, CaretPositioningMode mode) { //TODO: VisualLine.GetNextCaretPosition() throw new NotImplementedException(); }
/// <summary> /// Gets the next possible caret position after visualColumn, or -1 if there is no caret position. /// </summary> public int GetNextCaretPosition(int visualColumn, LogicalDirection direction, CaretPositioningMode mode, bool allowVirtualSpace) { if (!HasStopsInVirtualSpace(mode)) allowVirtualSpace = false; if (elements.Count == 0) { // special handling for empty visual lines: if (allowVirtualSpace) { if (direction == LogicalDirection.Forward) return Math.Max(0, visualColumn + 1); else if (visualColumn > 0) return visualColumn - 1; else return -1; } else { // even though we don't have any elements, // there's a single caret stop at visualColumn 0 if (visualColumn < 0 && direction == LogicalDirection.Forward) return 0; else if (visualColumn > 0 && direction == LogicalDirection.Backward) return 0; else return -1; } } int i; if (direction == LogicalDirection.Backward) { // Search Backwards: // If the last element doesn't handle line borders, return the line end as caret stop if (visualColumn > this.VisualLength && !elements[elements.Count-1].HandlesLineBorders && HasImplicitStopAtLineEnd(mode)) { if (allowVirtualSpace) return visualColumn - 1; else return this.VisualLength; } // skip elements that start after or at visualColumn for (i = elements.Count - 1; i >= 0; i--) { if (elements[i].VisualColumn < visualColumn) break; } // search last element that has a caret stop for (; i >= 0; i--) { int pos = elements[i].GetNextCaretPosition( Math.Min(visualColumn, elements[i].VisualColumn + elements[i].VisualLength + 1), direction, mode); if (pos >= 0) return pos; } // If we've found nothing, and the first element doesn't handle line borders, // return the line start as normal caret stop. if (visualColumn > 0 && !elements[0].HandlesLineBorders && HasImplicitStopAtLineStart(mode)) return 0; } else { // Search Forwards: // If the first element doesn't handle line borders, return the line start as caret stop if (visualColumn < 0 && !elements[0].HandlesLineBorders && HasImplicitStopAtLineStart(mode)) return 0; // skip elements that end before or at visualColumn for (i = 0; i < elements.Count; i++) { if (elements[i].VisualColumn + elements[i].VisualLength > visualColumn) break; } // search first element that has a caret stop for (; i < elements.Count; i++) { int pos = elements[i].GetNextCaretPosition( Math.Max(visualColumn, elements[i].VisualColumn - 1), direction, mode); if (pos >= 0) return pos; } // if we've found nothing, and the last element doesn't handle line borders, // return the line end as caret stop if ((allowVirtualSpace || !elements[elements.Count-1].HandlesLineBorders) && HasImplicitStopAtLineEnd(mode)) { if (visualColumn < this.VisualLength) return this.VisualLength; else if (allowVirtualSpace) return visualColumn + 1; } } // we've found nothing, return -1 and let the caret search continue in the next line return -1; }
private void ExpandToEnclosingUnit(CaretPositioningMode mode) { int start = TextUtilities.GetNextCaretPosition(doc, segment.Offset + 1, LogicalDirection.Backward, mode); if (start < 0) return; int end = TextUtilities.GetNextCaretPosition(doc, start, LogicalDirection.Forward, mode); if (end < 0) return; segment = new AnchorSegment(doc, start, end - start); }
static bool HasImplicitStopAtLineStart(CaretPositioningMode mode) { return(mode == CaretPositioningMode.Normal); }
static bool HasImplicitStopAtLineEnd(CaretPositioningMode mode) { return(true); }
/// <summary> /// Gets the next possible caret position after visualColumn, or -1 if there is no caret position. /// </summary> public int GetNextCaretPosition(int visualColumn, LogicalDirection direction, CaretPositioningMode mode) { if (elements.Count == 0) { // special handling for empty visual lines: // even though we don't have any elements, // there's a single caret stop at visualColumn 0 if (visualColumn < 0 && direction == LogicalDirection.Forward) { return(0); } else if (visualColumn > 0 && direction == LogicalDirection.Backward) { return(0); } else { return(-1); } } int i; if (direction == LogicalDirection.Backward) { // Search Backwards: // If the last element doesn't handle line borders, return the line end as caret stop if (visualColumn > this.VisualLength && !elements[elements.Count - 1].HandlesLineBorders && HasImplicitStopAtLineEnd(mode)) { return(this.VisualLength); } // skip elements that start after or at visualColumn for (i = elements.Count - 1; i >= 0; i--) { if (elements[i].VisualColumn < visualColumn) { break; } } // search last element that has a caret stop for (; i >= 0; i--) { int pos = elements[i].GetNextCaretPosition( Math.Min(visualColumn, elements[i].VisualColumn + elements[i].VisualLength + 1), direction, mode); if (pos >= 0) { return(pos); } } // If we've found nothing, and the first element doesn't handle line borders, // return the line start as normal caret stop. if (visualColumn > 0 && !elements[0].HandlesLineBorders && HasImplicitStopAtLineStart(mode)) { return(0); } } else { // Search Forwards: // If the first element doesn't handle line borders, return the line start as caret stop if (visualColumn < 0 && !elements[0].HandlesLineBorders && HasImplicitStopAtLineStart(mode)) { return(0); } // skip elements that end before or at visualColumn for (i = 0; i < elements.Count; i++) { if (elements[i].VisualColumn + elements[i].VisualLength > visualColumn) { break; } } // search first element that has a caret stop for (; i < elements.Count; i++) { int pos = elements[i].GetNextCaretPosition( Math.Max(visualColumn, elements[i].VisualColumn - 1), direction, mode); if (pos >= 0) { return(pos); } } // if we've found nothing, and the last element doesn't handle line borders, // return the line end as caret stop if (visualColumn < this.VisualLength && !elements[elements.Count - 1].HandlesLineBorders && HasImplicitStopAtLineEnd(mode)) { return(this.VisualLength); } } // we've found nothing, return -1 and let the caret search continue in the next line return(-1); }
int GetPrevCaretStop(string text, int offset, CaretPositioningMode mode) { return(TextUtilities.GetNextCaretPosition(new StringTextSource(text), offset, LogicalDirection.Backward, mode)); }
/// <inheritdoc/> public override int GetNextCaretPosition(int visualColumn, LogicalDirection direction, CaretPositioningMode mode) { int textOffset = parentVisualLine.StartOffset + this.RelativeTextOffset; int pos = TextUtilities.GetNextCaretPosition(parentVisualLine.Document, textOffset + visualColumn - this.VisualColumn, direction, mode); if (pos < textOffset || pos > textOffset + this.DocumentLength) { return(-1); } else { return(this.VisualColumn + pos - textOffset); } }
static TextViewPosition GetPrevCaretPosition(TextView textView, TextViewPosition caretPosition, VisualLine visualLine, CaretPositioningMode mode, bool enableVirtualSpace) { int pos = visualLine.GetNextCaretPosition(caretPosition.VisualColumn, LogicalDirection.Backward, mode, enableVirtualSpace); if (pos >= 0) { return(visualLine.GetTextViewPosition(pos)); } var previousDocumentLine = visualLine.FirstDocumentLine.PreviousLine; if (previousDocumentLine != null) { var previousLine = textView.GetOrConstructVisualLine(previousDocumentLine); pos = previousLine.GetNextCaretPosition(previousLine.VisualLength + 1, LogicalDirection.Backward, mode, enableVirtualSpace); return(previousLine.GetTextViewPosition(pos)); } return(new TextViewPosition(0, 0)); }
private static bool HasImplicitStopAtLineStart(CaretPositioningMode mode) { return(mode == CaretPositioningMode.Normal || mode == CaretPositioningMode.EveryCodepoint); }
static bool HasImplicitStopAtLineStart(CaretPositioningMode mode) { return mode == CaretPositioningMode.Normal; }
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); } } }
/// <summary> /// Gets the next caret position. /// </summary> /// <param name="textSource">The text source.</param> /// <param name="offset">The start offset inside the text source.</param> /// <param name="direction">The search direction (forwards or backwards).</param> /// <param name="mode">The mode for caret positioning.</param> /// <returns>The offset of the next caret position, or -1 if there is no further caret position /// in the text source.</returns> /// <remarks> /// This method is NOT equivalent to the actual caret movement when using VisualLine.GetNextCaretPosition. /// In real caret movement, there are additional caret stops at line starts and ends. This method /// treats linefeeds as simple whitespace. /// </remarks> public static int GetNextCaretPosition(ITextSource textSource, int offset, LogicalDirection direction, CaretPositioningMode mode) { if (textSource == null) throw new ArgumentNullException(nameof(textSource)); if (mode != CaretPositioningMode.Normal && mode != CaretPositioningMode.WordBorder && mode != CaretPositioningMode.WordStart && mode != CaretPositioningMode.WordBorderOrSymbol && mode != CaretPositioningMode.WordStartOrSymbol) { throw new ArgumentException("Unsupported CaretPositioningMode: " + mode, nameof(mode)); } if (direction != LogicalDirection.Backward && direction != LogicalDirection.Forward) { throw new ArgumentException("Invalid LogicalDirection: " + direction, nameof(direction)); } int textLength = textSource.TextLength; if (textLength <= 0) { // empty document? has a normal caret position at 0, though no word borders if (mode == CaretPositioningMode.Normal) { if (offset > 0 && direction == LogicalDirection.Backward) return 0; if (offset < 0 && direction == LogicalDirection.Forward) return 0; } return -1; } while (true) { int nextPos = (direction == LogicalDirection.Backward) ? offset - 1 : offset + 1; // return -1 if there is no further caret position in the text source // we also need this to handle offset values outside the valid range if (nextPos < 0 || nextPos > textLength) return -1; // stop at every caret position? we can stop immediately. if (mode == CaretPositioningMode.Normal) return nextPos; // not normal mode? we're looking for word borders... // check if we've run against the textSource borders. // a 'textSource' usually isn't the whole document, but a single VisualLineElement. if (nextPos == 0) { // at the document start, there's only a word border // if the first character is not whitespace if (!char.IsWhiteSpace(textSource.GetCharAt(0))) return nextPos; } else if (nextPos == textLength) { // at the document end, there's never a word start if (mode != CaretPositioningMode.WordStart && mode != CaretPositioningMode.WordStartOrSymbol) { // at the document end, there's only a word border // if the last character is not whitespace if (!char.IsWhiteSpace(textSource.GetCharAt(textLength - 1))) return nextPos; } } else { CharacterClass charBefore = GetCharacterClass(textSource.GetCharAt(nextPos - 1)); CharacterClass charAfter = GetCharacterClass(textSource.GetCharAt(nextPos)); if (charBefore == charAfter) { if (charBefore == CharacterClass.Other && (mode == CaretPositioningMode.WordBorderOrSymbol || mode == CaretPositioningMode.WordStartOrSymbol)) { // With the "OrSymbol" modes, there's a word border and start between any two unknown characters return nextPos; } } else { // this looks like a possible border // if we're looking for word starts, check that this is a word start (and not a word end) // if we're just checking for word borders, accept unconditionally if (!((mode == CaretPositioningMode.WordStart || mode == CaretPositioningMode.WordStartOrSymbol) && (charAfter == CharacterClass.Whitespace || charAfter == CharacterClass.LineTerminator))) { return nextPos; } } } // we'll have to continue searching... offset = nextPos; } }
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); } } }
static bool HasImplicitStopAtLineStart(CaretPositioningMode mode) { return mode == CaretPositioningMode.Normal || mode == CaretPositioningMode.EveryCodepoint; }
public override int GetNextCaretPosition(int visualColumn, LogicalDirection direction, CaretPositioningMode mode) { if (mode == CaretPositioningMode.Normal || mode == CaretPositioningMode.EveryCodepoint) { return(base.GetNextCaretPosition(visualColumn, direction, mode)); } else { return(-1); } }
static void MoveCaretLeft(TextArea textArea, TextViewPosition caretPosition, VisualLine visualLine, CaretPositioningMode mode) { int pos = visualLine.GetNextCaretPosition(caretPosition.VisualColumn, LogicalDirection.Backward, mode); 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); 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); } } }
static TextViewPosition GetNextCaretPosition(TextView textView, TextViewPosition caretPosition, VisualLine visualLine, CaretPositioningMode mode, bool enableVirtualSpace) { int pos = visualLine.GetNextCaretPosition(caretPosition.VisualColumn, LogicalDirection.Forward, mode, enableVirtualSpace); if (pos >= 0) { return(visualLine.GetTextViewPosition(pos)); } else { // move to start of next line DocumentLine nextDocumentLine = visualLine.LastDocumentLine.NextLine; if (nextDocumentLine != null) { VisualLine nextLine = textView.GetOrConstructVisualLine(nextDocumentLine); pos = nextLine.GetNextCaretPosition(-1, LogicalDirection.Forward, mode, enableVirtualSpace); if (pos < 0) { throw ThrowUtil.NoValidCaretPosition(); } return(nextLine.GetTextViewPosition(pos)); } else { // at end of document Debug.Assert(visualLine.LastDocumentLine.Offset + visualLine.LastDocumentLine.TotalLength == textView.Document.TextLength); return(new TextViewPosition(textView.Document.GetLocation(textView.Document.TextLength))); } } }
/// <summary> /// Gets the next caret position. /// </summary> /// <param name="textSource">The text source.</param> /// <param name="offset">The start offset inside the text source.</param> /// <param name="direction">The search direction (forwards or backwards).</param> /// <param name="mode">The mode for caret positioning.</param> /// <returns>The offset of the next caret position, or -1 if there is no further caret position /// in the text source.</returns> /// <remarks> /// This method is NOT equivalent to the actual caret movement when using VisualLine.GetNextCaretPosition. /// In real caret movement, there are additional caret stops at line starts and ends. This method /// treats linefeeds as simple whitespace. /// </remarks> public static int GetNextCaretPosition(ITextSource textSource, int offset, LogicalDirection direction, CaretPositioningMode mode) { if (textSource == null) throw new ArgumentNullException("textSource"); switch (mode) { case CaretPositioningMode.Normal: case CaretPositioningMode.EveryCodepoint: case CaretPositioningMode.WordBorder: case CaretPositioningMode.WordBorderOrSymbol: case CaretPositioningMode.WordStart: case CaretPositioningMode.WordStartOrSymbol: break; // OK default: throw new ArgumentException("Unsupported CaretPositioningMode: " + mode, "mode"); } if (direction != LogicalDirection.Backward && direction != LogicalDirection.Forward) { throw new ArgumentException("Invalid LogicalDirection: " + direction, "direction"); } int textLength = textSource.TextLength; if (textLength <= 0) { // empty document? has a normal caret position at 0, though no word borders if (IsNormal(mode)) { if (offset > 0 && direction == LogicalDirection.Backward) return 0; if (offset < 0 && direction == LogicalDirection.Forward) return 0; } return -1; } while (true) { int nextPos = (direction == LogicalDirection.Backward) ? offset - 1 : offset + 1; // return -1 if there is no further caret position in the text source // we also need this to handle offset values outside the valid range if (nextPos < 0 || nextPos > textLength) return -1; // check if we've run against the textSource borders. // a 'textSource' usually isn't the whole document, but a single VisualLineElement. if (nextPos == 0) { // at the document start, there's only a word border // if the first character is not whitespace if (IsNormal(mode) || !char.IsWhiteSpace(textSource.GetCharAt(0))) return nextPos; } else if (nextPos == textLength) { // at the document end, there's never a word start if (mode != CaretPositioningMode.WordStart && mode != CaretPositioningMode.WordStartOrSymbol) { // at the document end, there's only a word border // if the last character is not whitespace if (IsNormal(mode) || !char.IsWhiteSpace(textSource.GetCharAt(textLength - 1))) return nextPos; } } else { char charBefore = textSource.GetCharAt(nextPos - 1); char charAfter = textSource.GetCharAt(nextPos); // Don't stop in the middle of a surrogate pair if (!char.IsSurrogatePair(charBefore, charAfter)) { CharacterClass classBefore = GetCharacterClass(charBefore); CharacterClass classAfter = GetCharacterClass(charAfter); // get correct class for characters outside BMP: if (char.IsLowSurrogate(charBefore) && nextPos >= 2) { classBefore = GetCharacterClass(textSource.GetCharAt(nextPos - 2), charBefore); } if (char.IsHighSurrogate(charAfter) && nextPos + 1 < textLength) { classAfter = GetCharacterClass(charAfter, textSource.GetCharAt(nextPos + 1)); } if (StopBetweenCharacters(mode, classBefore, classAfter)) { return nextPos; } } } // we'll have to continue searching... offset = nextPos; } }
static TextViewPosition GetPrevCaretPosition(TextView textView, TextViewPosition caretPosition, VisualLine visualLine, CaretPositioningMode mode, bool enableVirtualSpace) { int pos = visualLine.GetNextCaretPosition(caretPosition.VisualColumn, LogicalDirection.Backward, mode, enableVirtualSpace); if (pos >= 0) { return(visualLine.GetTextViewPosition(pos)); } else { // move to end of previous line DocumentLine previousDocumentLine = visualLine.FirstDocumentLine.PreviousLine; if (previousDocumentLine != null) { VisualLine previousLine = textView.GetOrConstructVisualLine(previousDocumentLine); pos = previousLine.GetNextCaretPosition(previousLine.VisualLength + 1, LogicalDirection.Backward, mode, enableVirtualSpace); if (pos < 0) { throw ThrowUtil.NoValidCaretPosition(); } return(previousLine.GetTextViewPosition(pos)); } else { // at start of document Debug.Assert(visualLine.FirstDocumentLine.Offset == 0); return(new TextViewPosition(0, 0)); } } }
static bool StopBetweenCharacters(CaretPositioningMode mode, CharacterClass charBefore, CharacterClass charAfter) { if (mode == CaretPositioningMode.EveryCodepoint) return true; // Don't stop in the middle of a grapheme if (charAfter == CharacterClass.CombiningMark) return false; // Stop after every grapheme in normal mode if (mode == CaretPositioningMode.Normal) return true; if (charBefore == charAfter) { if (charBefore == CharacterClass.Other && (mode == CaretPositioningMode.WordBorderOrSymbol || mode == CaretPositioningMode.WordStartOrSymbol)) { // With the "OrSymbol" modes, there's a word border and start between any two unknown characters return true; } } else { // this looks like a possible border // if we're looking for word starts, check that this is a word start (and not a word end) // if we're just checking for word borders, accept unconditionally if (!((mode == CaretPositioningMode.WordStart || mode == CaretPositioningMode.WordStartOrSymbol) && (charAfter == CharacterClass.Whitespace || charAfter == CharacterClass.LineTerminator))) { return true; } } return false; }
public override int GetNextCaretPosition(int visualColumn, LogicalDirection direction, CaretPositioningMode mode) { // only place a caret stop before the newline, no caret stop after it if (visualColumn > this.VisualColumn && direction == LogicalDirection.Backward || visualColumn < this.VisualColumn && direction == LogicalDirection.Forward) { return(this.VisualColumn); } else { return(-1); } }
static TextViewPosition GetPrevCaretPosition(TextView textView, TextViewPosition caretPosition, VisualLine visualLine, CaretPositioningMode mode, bool enableVirtualSpace) { int pos = visualLine.GetNextCaretPosition(caretPosition.VisualColumn, LogicalDirection.Backward, mode, enableVirtualSpace); if (pos >= 0) return visualLine.GetTextViewPosition(pos); var previousDocumentLine = visualLine.FirstDocumentLine.PreviousLine; if (previousDocumentLine != null) { var previousLine = textView.GetOrConstructVisualLine(previousDocumentLine); pos = previousLine.GetNextCaretPosition(previousLine.VisualLength + 1, LogicalDirection.Backward, mode, enableVirtualSpace); return previousLine.GetTextViewPosition(pos); } return new TextViewPosition(0, 0); }
/// <summary> /// Gets the next caret position. /// </summary> /// <param name="textSource">The text source.</param> /// <param name="offset">The start offset inside the text source.</param> /// <param name="direction">The search direction (forwards or backwards).</param> /// <param name="mode">The mode for caret positioning.</param> /// <returns>The offset of the next caret position, or -1 if there is no further caret position /// in the text source.</returns> /// <remarks> /// This method is NOT equivalent to the actual caret movement when using VisualLine.GetNextCaretPosition. /// In real caret movement, there are additional caret stops at line starts and ends. This method /// treats linefeeds as simple whitespace. /// </remarks> public static int GetNextCaretPosition(ITextSource textSource, int offset, LogicalDirection direction, CaretPositioningMode mode) { if (textSource == null) { throw new ArgumentNullException("textSource"); } if (mode != CaretPositioningMode.Normal && mode != CaretPositioningMode.WordBorder && mode != CaretPositioningMode.WordStart && mode != CaretPositioningMode.WordBorderOrSymbol && mode != CaretPositioningMode.WordStartOrSymbol) { throw new ArgumentException("Unsupported CaretPositioningMode: " + mode, "mode"); } if (direction != LogicalDirection.Backward && direction != LogicalDirection.Forward) { throw new ArgumentException("Invalid LogicalDirection: " + direction, "direction"); } int textLength = textSource.TextLength; if (textLength <= 0) { // empty document? has a normal caret position at 0, though no word borders if (mode == CaretPositioningMode.Normal) { if (offset > 0 && direction == LogicalDirection.Backward) { return(0); } if (offset < 0 && direction == LogicalDirection.Forward) { return(0); } } return(-1); } while (true) { int nextPos = (direction == LogicalDirection.Backward) ? offset - 1 : offset + 1; // return -1 if there is no further caret position in the text source // we also need this to handle offset values outside the valid range if (nextPos < 0 || nextPos > textLength) { return(-1); } // stop at every caret position? we can stop immediately. if (mode == CaretPositioningMode.Normal) { return(nextPos); } // not normal mode? we're looking for word borders... // check if we've run against the textSource borders. // a 'textSource' usually isn't the whole document, but a single VisualLineElement. if (nextPos == 0) { // at the document start, there's only a word border // if the first character is not whitespace if (!char.IsWhiteSpace(textSource.GetCharAt(0))) { return(nextPos); } } else if (nextPos == textLength) { // at the document end, there's never a word start if (mode != CaretPositioningMode.WordStart && mode != CaretPositioningMode.WordStartOrSymbol) { // at the document end, there's only a word border // if the last character is not whitespace if (!char.IsWhiteSpace(textSource.GetCharAt(textLength - 1))) { return(nextPos); } } } else { CharacterClass charBefore = GetCharacterClass(textSource.GetCharAt(nextPos - 1)); CharacterClass charAfter = GetCharacterClass(textSource.GetCharAt(nextPos)); if (charBefore == charAfter) { if (charBefore == CharacterClass.Other && (mode == CaretPositioningMode.WordBorderOrSymbol || mode == CaretPositioningMode.WordStartOrSymbol)) { // With the "OrSymbol" modes, there's a word border and start between any two unknown characters return(nextPos); } } else { // this looks like a possible border // if we're looking for word starts, check that this is a word start (and not a word end) // if we're just checking for word borders, accept unconditionally if (!((mode == CaretPositioningMode.WordStart || mode == CaretPositioningMode.WordStartOrSymbol) && (charAfter == CharacterClass.Whitespace || charAfter == CharacterClass.LineTerminator))) { return(nextPos); } } } // we'll have to continue searching... offset = nextPos; } }
public override int GetNextCaretPosition(int visualColumn, LogicalDirection direction, CaretPositioningMode mode) { if (mode == CaretPositioningMode.Normal || mode == CaretPositioningMode.EveryCodepoint) return base.GetNextCaretPosition(visualColumn, direction, mode); else return -1; }
/// <inheritdoc/> public override int GetNextCaretPosition(int visualColumn, LogicalDirection direction, CaretPositioningMode mode) { int textOffset = parentVisualLine.FirstDocumentLine.Offset + this.RelativeTextOffset; TextSourceView view = new TextSourceView( parentVisualLine.FirstDocumentLine.Document, new SimpleSegment(textOffset, this.DocumentLength)); int pos = TextUtilities.GetNextCaretPosition(view, visualColumn - this.VisualColumn, direction, mode); if (pos < 0) return pos; else return this.VisualColumn + pos; }
/// <summary> /// Gets the next caret position. /// </summary> /// <param name="textSource">The text source.</param> /// <param name="offset">The start offset inside the text source.</param> /// <param name="direction">The search direction (forwards or backwards).</param> /// <param name="mode">The mode for caret positioning.</param> /// <returns>The offset of the next caret position, or -1 if there is no further caret position /// in the text source.</returns> /// <remarks> /// This method is NOT equivalent to the actual caret movement when using VisualLine.GetNextCaretPosition. /// In real caret movement, there are additional caret stops at line starts and ends. This method /// treats linefeeds as simple whitespace. /// </remarks> public static int GetNextCaretPosition(ITextSource textSource, int offset, LogicalDirection direction, CaretPositioningMode mode) { if (textSource == null) { throw new ArgumentNullException("textSource"); } switch (mode) { case CaretPositioningMode.Normal: case CaretPositioningMode.EveryCodepoint: case CaretPositioningMode.WordBorder: case CaretPositioningMode.WordBorderOrSymbol: case CaretPositioningMode.WordStart: case CaretPositioningMode.WordStartOrSymbol: break; // OK default: throw new ArgumentException("Unsupported CaretPositioningMode: " + mode, "mode"); } if (direction != LogicalDirection.Backward && direction != LogicalDirection.Forward) { throw new ArgumentException("Invalid LogicalDirection: " + direction, "direction"); } int textLength = textSource.TextLength; if (textLength <= 0) { // empty document? has a normal caret position at 0, though no word borders if (IsNormal(mode)) { if (offset > 0 && direction == LogicalDirection.Backward) { return(0); } if (offset < 0 && direction == LogicalDirection.Forward) { return(0); } } return(-1); } while (true) { int nextPos = (direction == LogicalDirection.Backward) ? offset - 1 : offset + 1; // return -1 if there is no further caret position in the text source // we also need this to handle offset values outside the valid range if (nextPos < 0 || nextPos > textLength) { return(-1); } // check if we've run against the textSource borders. // a 'textSource' usually isn't the whole document, but a single VisualLineElement. if (nextPos == 0) { // at the document start, there's only a word border // if the first character is not whitespace if (IsNormal(mode) || !char.IsWhiteSpace(textSource.GetCharAt(0))) { return(nextPos); } } else if (nextPos == textLength) { // at the document end, there's never a word start if (mode != CaretPositioningMode.WordStart && mode != CaretPositioningMode.WordStartOrSymbol) { // at the document end, there's only a word border // if the last character is not whitespace if (IsNormal(mode) || !char.IsWhiteSpace(textSource.GetCharAt(textLength - 1))) { return(nextPos); } } } else { char charBefore = textSource.GetCharAt(nextPos - 1); char charAfter = textSource.GetCharAt(nextPos); // Don't stop in the middle of a surrogate pair if (!char.IsSurrogatePair(charBefore, charAfter)) { CharacterClass classBefore = GetCharacterClass(charBefore); CharacterClass classAfter = GetCharacterClass(charAfter); // get correct class for characters outside BMP: if (char.IsLowSurrogate(charBefore) && nextPos >= 2) { classBefore = GetCharacterClass(textSource.GetCharAt(nextPos - 2), charBefore); } if (char.IsHighSurrogate(charAfter) && nextPos + 1 < textLength) { classAfter = GetCharacterClass(charAfter, textSource.GetCharAt(nextPos + 1)); } if (StopBetweenCharacters(mode, classBefore, classAfter)) { return(nextPos); } } } // we'll have to continue searching... offset = nextPos; } }
static bool IsNormal(CaretPositioningMode mode) { return(mode == CaretPositioningMode.Normal || mode == CaretPositioningMode.EveryCodepoint); }
static bool HasImplicitStopAtLineEnd(CaretPositioningMode mode) { return true; }
static TextViewPosition GetNextCaretPosition(TextView textView, TextViewPosition caretPosition, VisualLine visualLine, CaretPositioningMode mode, bool enableVirtualSpace) { int pos = visualLine.GetNextCaretPosition(caretPosition.VisualColumn, LogicalDirection.Forward, mode, enableVirtualSpace); if (pos >= 0) { return visualLine.GetTextViewPosition(pos); } else { // move to start of next line DocumentLine nextDocumentLine = visualLine.LastDocumentLine.NextLine; if (nextDocumentLine != null) { VisualLine nextLine = textView.GetOrConstructVisualLine(nextDocumentLine); pos = nextLine.GetNextCaretPosition(-1, LogicalDirection.Forward, mode, enableVirtualSpace); if (pos < 0) throw ThrowUtil.NoValidCaretPosition(); return nextLine.GetTextViewPosition(pos); } else { // at end of document Debug.Assert(visualLine.LastDocumentLine.Offset + visualLine.LastDocumentLine.TotalLength == textView.Document.TextLength); return new TextViewPosition(textView.Document.GetLocation(textView.Document.TextLength)); } } }
static bool HasStopsInVirtualSpace(CaretPositioningMode mode) { return mode == CaretPositioningMode.Normal; }
static TextViewPosition GetPrevCaretPosition(TextView textView, TextViewPosition caretPosition, VisualLine visualLine, CaretPositioningMode mode, bool enableVirtualSpace) { int pos = visualLine.GetNextCaretPosition(caretPosition.VisualColumn, LogicalDirection.Backward, mode, enableVirtualSpace); if (pos >= 0) { return visualLine.GetTextViewPosition(pos); } else { // move to end of previous line DocumentLine previousDocumentLine = visualLine.FirstDocumentLine.PreviousLine; if (previousDocumentLine != null) { VisualLine previousLine = textView.GetOrConstructVisualLine(previousDocumentLine); pos = previousLine.GetNextCaretPosition(previousLine.VisualLength + 1, LogicalDirection.Backward, mode, enableVirtualSpace); if (pos < 0) throw ThrowUtil.NoValidCaretPosition(); return previousLine.GetTextViewPosition(pos); } else { // at start of document Debug.Assert(visualLine.FirstDocumentLine.Offset == 0); return new TextViewPosition(0, 0); } } }
private int MoveOffset(int offset, CaretPositioningMode mode, int count) { var direction = count < 0 ? LogicalDirection.Backward : LogicalDirection.Forward; count = Math.Abs(count); for (int i = 0; i < count; i++) { int newOffset = TextUtilities.GetNextCaretPosition(doc, offset, direction, mode); if (newOffset == offset || newOffset < 0) break; offset = newOffset; } return offset; }
static bool HasStopsInVirtualSpace(CaretPositioningMode mode) { return mode == CaretPositioningMode.Normal || mode == CaretPositioningMode.EveryCodepoint; }
int GetPrevCaretStop(string text, int offset, CaretPositioningMode mode) { return TextUtilities.GetNextCaretPosition(new StringTextSource(text), offset, LogicalDirection.Backward, mode); }
static bool HasStopsInVirtualSpace(CaretPositioningMode mode) { return(mode == CaretPositioningMode.Normal); }