Ejemplo n.º 1
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)));
Ejemplo n.º 2
0
    public static Row[] ApplyColorToCharacters(IReadOnlyCollection <FormatSpan> highlights, IReadOnlyList <WrappedLine> lines, SelectionSpan?selection, AnsiColor?selectedTextBackground)
    {
        var selectionStart = new ConsoleCoordinate(int.MaxValue, int.MaxValue); //invalid
        var selectionEnd   = new ConsoleCoordinate(int.MaxValue, int.MaxValue); //invalid

        if (selection.TryGet(out var selectionValue))
        {
            selectionStart = selectionValue.Start;
            selectionEnd   = selectionValue.End;
        }

        bool selectionHighlight = false;

        var highlightsLookup = highlights
                               .ToLookup(h => h.Start)
                               .ToDictionary(h => h.Key, conflictingHighlights => conflictingHighlights.OrderByDescending(h => h.Length).First());
        var        highlightedRows  = new Row[lines.Count];
        FormatSpan?currentHighlight = null;

        for (int lineIndex = 0; lineIndex < lines.Count; lineIndex++)
        {
            WrappedLine line = lines[lineIndex];
            int         lineFullWidthCharacterOffset = 0;
            var         cells = Cell.FromText(line.Content);
            for (int cellIndex = 0; cellIndex < cells.Count; cellIndex++)
            {
                var cell = cells[cellIndex];
                if (cell.IsContinuationOfPreviousCharacter)
                {
                    lineFullWidthCharacterOffset++;
                }

                // syntax highlight wrapped lines
                if (currentHighlight.TryGet(out var previousLineHighlight) &&
                    cellIndex == 0)
                {
                    currentHighlight = HighlightSpan(previousLineHighlight, cells, cellIndex, previousLineHighlight.Start - line.StartIndex);
                }

                // get current syntaxt highlight start
                int characterPosition = line.StartIndex + cellIndex - lineFullWidthCharacterOffset;
                currentHighlight ??= highlightsLookup.TryGetValue(characterPosition, out var lookupHighlight) ? lookupHighlight : null;

                // syntax highlight based on start
                if (currentHighlight.TryGet(out var highlight) &&
                    highlight.Contains(characterPosition))
                {
                    currentHighlight = HighlightSpan(highlight, cells, cellIndex, cellIndex);
                }

                // if there's text selected, invert colors to represent the highlight of the selected text.
                if (selectionStart.Equals(lineIndex, cellIndex - lineFullWidthCharacterOffset)) //start is inclusive
                {
                    selectionHighlight = true;
                }
                if (selectionEnd.Equals(lineIndex, cellIndex - lineFullWidthCharacterOffset)) //end is exclusive
                {
                    selectionHighlight = false;
                }
                if (selectionHighlight)
                {
                    if (selectedTextBackground.TryGet(out var background))
                    {
                        cell.Formatting = cell.Formatting with {
                            Background = background
                        };
                    }
                    else
                    {
                        cell.Formatting = new ConsoleFormat {
                            Inverted = true
                        };
                    }
                }
            }
            highlightedRows[lineIndex] = new Row(cells);
        }
        return(highlightedRows);
    }