Exemplo n.º 1
0
 static bool NextCharacterIsFullWidthAndWillWrap(int width, int currentLineLength, ReadOnlySpan <char> chunk, int i)
 => chunk.Length > i + 1 && UnicodeWidth.GetWidth(chunk[i + 1]) > 1 && currentLineLength + 1 == width;
Exemplo n.º 2
0
    /// <summary>
    /// Wraps editable input, contained in the string builder, to the supplied width.
    /// The caret index (as the input is a 1 dimensional string of text) is converted
    /// to a 2 dimensional coordinate in the wrapped text.
    /// </summary>
    public static WordWrappedText WrapEditableCharacters(ReadOnlyStringBuilder input, int caret, int width)
    {
        Debug.Assert(caret >= 0 && caret <= input.Length);

        if (input.Length == 0)
        {
            return(new WordWrappedText(
                       new[] { WrappedLine.Empty(startIndex: 0) },
                       new ConsoleCoordinate(0, caret)));
        }

        var lines             = new List <WrappedLine>();
        int currentLineLength = 0;
        var line         = new StringBuilder(width);
        int textIndex    = 0;
        int cursorColumn = 0;
        int cursorRow    = 0;

        foreach (ReadOnlyMemory <char> chunkMemory in input.GetChunks())
        {
            var chunk = chunkMemory.Span;
            for (var i = 0; i < chunk.Length; i++)
            {
                char character = chunk[i];
                line.Append(character);
                bool isCursorPastCharacter = caret > textIndex;

                Debug.Assert(character != '\t', "tabs should be replaced by spaces");
                int unicodeWidth = UnicodeWidth.GetWidth(character);
                if (unicodeWidth < 1)
                {
                    Debug.Fail("such character should not be present");
                    continue;
                }
                currentLineLength += unicodeWidth;
                textIndex++;

                if (isCursorPastCharacter && !char.IsControl(character))
                {
                    cursorColumn++;
                }
                if (character == '\n' || currentLineLength == width ||
                    NextCharacterIsFullWidthAndWillWrap(width, currentLineLength, chunk, i))
                {
                    if (isCursorPastCharacter)
                    {
                        cursorRow++;
                        cursorColumn = 0;
                    }
                    lines.Add(new WrappedLine(textIndex - line.Length, line.ToString()));
                    line = new StringBuilder();
                    currentLineLength = 0;
                }
            }
        }

        if (currentLineLength > 0 || input[^ 1] == '\n')
        {
            lines.Add(new WrappedLine(textIndex - line.Length, line.ToString()));
        }

        Debug.Assert(textIndex == input.Length);
        if (cursorRow >= lines.Count)
        {
            Debug.Assert(cursorRow == lines.Count);
            lines.Add(WrappedLine.Empty(startIndex: textIndex));
        }

        return(new WordWrappedText(lines, new ConsoleCoordinate(cursorRow, cursorColumn)));