using System; using System.IO; using System.Text; namespace StardewModdingAPI.Framework.Logging { /// A text writer which allows intercepting output. internal class InterceptingTextWriter : TextWriter { /********* ** Fields *********/ /// Prefixing a message with this character indicates that the console interceptor should write the string without intercepting it. (The character itself is not written.) private readonly char IgnoreChar; /********* ** Accessors *********/ /// 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. /// 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 InterceptingTextWriter(TextWriter output, char ignoreChar) { this.Out = output; this.IgnoreChar = ignoreChar; } /// public override void Write(char[] buffer, int index, int count) { bool ignoreIfNewline = this.IgnoreNextIfNewline; this.IgnoreNextIfNewline = false; if (buffer.Length == 0) this.Out.Write(buffer, index, count); else if (buffer[0] == this.IgnoreChar || char.IsControl(buffer[0])) // ignore control characters like backspace this.Out.Write(buffer, index + 1, count - 1); else if (this.IsEmptyOrNewline(buffer)) { if (!ignoreIfNewline) this.Out.Write(buffer, index, count); } else this.OnMessageIntercepted?.Invoke(new string(buffer, index, count).TrimEnd('\r', '\n')); } /// 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; } } }