#nullable disable using System; using System.IO; using System.Text; namespace StardewModdingAPI.Framework.Logging { /// A text writer which allows intercepting output. internal class InterceptingTextWriter : TextWriter { /********* ** Accessors *********/ /// Prefixing a message with this character indicates that the console interceptor should write the string without intercepting it. (The character itself is not written.) public const char IgnoreChar = '\u200B'; /// The underlying console output. public TextWriter Out { get; } /// public override Encoding Encoding => this.Out.Encoding; /// The event raised when a message is written to the console directly. public event Action OnMessageIntercepted; /// Whether the text writer should ignore the next input if it's a newline. /// This is used when log output is suppressed from the console, since Console.WriteLine writes the trailing newline as a separate call. public bool IgnoreNextIfNewline { get; set; } /********* ** Public methods *********/ /// Construct an instance. /// The underlying output writer. public InterceptingTextWriter(TextWriter output) { this.Out = output; } /// public override void Write(char[] buffer, int index, int count) { // track newline skip bool ignoreIfNewline = this.IgnoreNextIfNewline; this.IgnoreNextIfNewline = false; // get first character if valid if (count == 0 || index < 0 || index >= buffer.Length) { this.Out.Write(buffer, index, count); return; } char firstChar = buffer[index]; // handle output if (firstChar == InterceptingTextWriter.IgnoreChar) this.Out.Write(buffer, index + 1, count - 1); else if (char.IsControl(firstChar) && firstChar is not ('\r' or '\n')) this.Out.Write(buffer, index, count); else if (this.IsEmptyOrNewline(buffer)) { if (!ignoreIfNewline) this.Out.Write(buffer, index, count); } else this.OnMessageIntercepted?.Invoke(new string(buffer, index, count)); } /// public override void Write(char ch) { this.Out.Write(ch); } /// protected override void Dispose(bool disposing) { this.OnMessageIntercepted = null; } /********* ** Private methods *********/ /// Get whether a buffer represents a line break. /// The buffer to check. private bool IsEmptyOrNewline(char[] buffer) { foreach (char ch in buffer) { if (ch != '\n' && ch != '\r') return false; } return true; } } }