From 046deb2d56b6d4665280cc5478d9e683ec1d777d Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Mon, 24 Aug 2020 19:25:57 -0400 Subject: simplify console interception flow The console interceptor now uses a marker in the string (instead of a state field) to track whether the message should intercepted. This makes each write more atomic, so it's less affected by multithreading in some cases. --- .../Framework/Logging/InterceptingTextWriter.cs | 55 +++++++++++++++------- 1 file changed, 38 insertions(+), 17 deletions(-) (limited to 'src/SMAPI/Framework/Logging/InterceptingTextWriter.cs') diff --git a/src/SMAPI/Framework/Logging/InterceptingTextWriter.cs b/src/SMAPI/Framework/Logging/InterceptingTextWriter.cs index 9ca61b59..d99f1dd2 100644 --- a/src/SMAPI/Framework/Logging/InterceptingTextWriter.cs +++ b/src/SMAPI/Framework/Logging/InterceptingTextWriter.cs @@ -7,18 +7,22 @@ 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; } - /// The character encoding in which the output is written. + /// public override Encoding Encoding => this.Out.Encoding; - /// Whether to intercept console output. - public bool ShouldIntercept { get; set; } - /// The event raised when a message is written to the console directly. public event Action OnMessageIntercepted; @@ -28,36 +32,53 @@ namespace StardewModdingAPI.Framework.Logging *********/ /// Construct an instance. /// The underlying output writer. - public InterceptingTextWriter(TextWriter output) + /// 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; } - /// Writes a subarray of characters to the text string or stream. - /// The character array to write data from. - /// The character position in the buffer at which to start retrieving data. - /// The number of characters to write. + /// public override void Write(char[] buffer, int index, int count) { - if (this.ShouldIntercept) - this.OnMessageIntercepted?.Invoke(new string(buffer, index, count).TrimEnd('\r', '\n')); - else + if (buffer.Length == 0) this.Out.Write(buffer, index, count); + else if (buffer[0] == this.IgnoreChar) + this.Out.Write(buffer, index + 1, count - 1); + else if (this.IsEmptyOrNewline(buffer)) + this.Out.Write(buffer, index, count); + else + this.OnMessageIntercepted?.Invoke(new string(buffer, index, count).TrimEnd('\r', '\n')); } - /// Writes a character to the text string or stream. - /// The character to write to the text stream. - /// Console log messages from the game should be caught by . This method passes through anything that bypasses that method for some reason, since it's better to show it to users than hide it from everyone. + /// public override void Write(char ch) { this.Out.Write(ch); } - /// Releases the unmanaged resources used by the and optionally releases the managed resources. - /// true to release both managed and unmanaged resources; false to release only unmanaged resources. + /// 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; + } } } -- cgit