/// <summary> /// Writes a text to the console with word wrap. /// </summary> /// <param name="message">the message to write</param> /// <param name="maxLineWidth">the maximum length of a line before line break.</param> /// <param name="writeToNewLine">write the message to a separated line (otherwise continue to output on the same line)</param> /// <param name="indentation">Apply indentation when writing on a new line.</param> /// <param name="prefixForNewLines">The text to put at the beginning of each new line that need to be created because of word wrap.</param> public void Write(string message, int maxLineWidth, bool writeToNewLine, int indentation = 0, string prefixForNewLines = null) { // handle null message if (message == null) { // write a new line if (writeToNewLine && HasWroteToOuput) { WriteLine(); } HasWroteToOuput = true; return; } // check indentation if (maxLineWidth > 0 && indentation >= maxLineWidth - 10) { indentation = maxLineWidth - 10; } indentation = Math.Max(indentation, 0); // write without word wrap if (maxLineWidth <= 0) { // write a new line if (writeToNewLine && HasWroteToOuput) { WriteLine(); _currentLineSpaceTaken = 0; } message = _currentLineSpaceTaken == 0 && indentation > 0 ? message.PadLeft(message.Length + indentation) : message; UnderLyingWriter.Write(message); _currentLineSpaceTaken += message.Length; HasWroteToOuput = true; return; } // for each line of text int lineStartPos, nextLineStartPos; for (lineStartPos = 0; lineStartPos < message.Length; lineStartPos = nextLineStartPos) { int eolPosition = message.IndexOf('\n', lineStartPos); if (eolPosition == -1) { nextLineStartPos = eolPosition = message.Length; } else { nextLineStartPos = eolPosition + 1; if (eolPosition == lineStartPos) { // found a new line WriteLine(); continue; } } // an input line can have indentation; if we split this input line into several lines (because it is too long), // we need to keep the same indentation for each new line bool newInputLineStarting = true; int paragraphIndent = 0; if (eolPosition > lineStartPos) { do { // write a new line if (writeToNewLine && HasWroteToOuput || _currentLineSpaceTaken >= maxLineWidth) { if (newInputLineStarting || string.IsNullOrEmpty(prefixForNewLines)) { WriteLine(); } else { var newLinePrefix = prefixForNewLines.PadRight(Math.Max(0, indentation + paragraphIndent)); WriteLine($"{UnderLyingWriter.NewLine}{newLinePrefix}"); _currentLineSpaceTaken += newLinePrefix.Length; } } int lineLength = eolPosition - lineStartPos; int totalIndent = _currentLineSpaceTaken > 0 ? 0 : indentation + paragraphIndent; int currentConsoleLineSpaceLeft = maxLineWidth - _currentLineSpaceTaken - totalIndent; if (lineLength > currentConsoleLineSpaceLeft) { lineLength = GetLineLengthToKeep(message, lineStartPos, currentConsoleLineSpaceLeft, !writeToNewLine && message.Length <= maxLineWidth - totalIndent); } if (lineLength > 0) { var line = message.Substring(lineStartPos, lineLength); line = _currentLineSpaceTaken > 0 ? line : line.PadLeft(lineLength + indentation + paragraphIndent, ' '); UnderLyingWriter.Write(line); _currentLineSpaceTaken += line.Length; HasWroteToOuput = true; } if (newInputLineStarting && writeToNewLine) { while (lineStartPos + paragraphIndent < message.Length && char.IsWhiteSpace(message[lineStartPos + paragraphIndent])) { paragraphIndent++; } if (indentation + paragraphIndent >= maxLineWidth) { paragraphIndent = maxLineWidth - indentation - 10; } } newInputLineStarting = false; writeToNewLine = true; // trim the whitespaces following a word break lineStartPos += lineLength; while (lineStartPos < eolPosition && message[lineStartPos] != '\n' && char.IsWhiteSpace(message[lineStartPos])) { lineStartPos++; } } while (eolPosition > lineStartPos); } } }
/// <summary> /// Write a new line char into the text writer /// </summary> /// <param name="newLine"></param> public void WriteLine(string newLine = null) { UnderLyingWriter.Write(newLine ?? UnderLyingWriter.NewLine); _currentLineSpaceTaken = 0; HasWroteToOuput = true; }