AvalonStudio.TextEditor.Document.TextUtilities.GetNextCaretPosition C# (CSharp) Method

GetNextCaretPosition() public static method

Gets the next caret position.
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.
public static GetNextCaretPosition ( ITextSource textSource, int offset, LogicalDirection direction, CaretPositioningMode mode ) : int
textSource ITextSource The text source.
offset int The start offset inside the text source.
direction LogicalDirection The search direction (forwards or backwards).
mode CaretPositioningMode The mode for caret positioning.
return int
		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");
			}
			var 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)
			{
				var 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
				{
					var charBefore = textSource.GetCharAt(nextPos - 1);
					var charAfter = textSource.GetCharAt(nextPos);
					// Don't stop in the middle of a surrogate pair
					if (!char.IsSurrogatePair(charBefore, charAfter))
					{
						var classBefore = GetCharacterClass(charBefore);
						var 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;
			}
		}