@@ -12,9 +12,24 @@ namespace PyRevitLabs.PyRevit.Runtime {
1212 /// surface used by the script engines is implemented.
1313 /// </summary>
1414 public class ScriptIO : Stream , IDisposable {
15+ // A buffered output entry carries the error state captured when it was
16+ // enqueued, so normal output drained after an error is not retroactively
17+ // rendered as an error just because the stream later saw a traceback.
18+ private struct PendingEntry {
19+ public readonly string Text ;
20+ public readonly bool IsError ;
21+ public readonly ScriptEngineType Engine ;
22+
23+ public PendingEntry ( string text , bool isError , ScriptEngineType engine ) {
24+ Text = text ;
25+ IsError = isError ;
26+ Engine = engine ;
27+ }
28+ }
29+
1530 private WeakReference < ScriptRuntime > _runtime ;
1631 private WeakReference < ScriptConsole > _gui ;
17- private readonly Queue < string > _pending = new Queue < string > ( ) ;
32+ private readonly Queue < PendingEntry > _pending = new Queue < PendingEntry > ( ) ;
1833 private int _pendingChars ;
1934 private readonly StringBuilder _partial = new StringBuilder ( ) ;
2035 private readonly object _logLock = new object ( ) ;
@@ -183,7 +198,7 @@ public void WriteEntry(string content) {
183198 FinalizePendingEntry ( splitLargeEntries : false ) ;
184199
185200 while ( _pendingChars > MaxPendingChars && _pending . Count > 1 )
186- _pendingChars -= _pending . Dequeue ( ) . Length ;
201+ _pendingChars -= _pending . Dequeue ( ) . Text . Length ;
187202
188203 pendingChars = _pendingChars ;
189204 }
@@ -192,6 +207,11 @@ public void WriteEntry(string content) {
192207 }
193208
194209 public void WriteError ( string error_msg , ScriptEngineType engineType ) {
210+ // Close out any buffered normal output first so it keeps its own
211+ // (non-error) styling when it drains.
212+ lock ( this ) {
213+ FinalizePendingEntry ( keepIncompleteShortcode : false ) ;
214+ }
195215 _errored = true ;
196216 _erroredEngine = engineType ;
197217 foreach ( string message_part in error_msg . SplitIntoChunks ( 1024 ) ) {
@@ -238,7 +258,7 @@ public override void Write(byte[] buffer, int offset, int count) {
238258 FinalizePendingEntry ( ) ;
239259
240260 while ( _pendingChars > MaxPendingChars && _pending . Count > 1 )
241- _pendingChars -= _pending . Dequeue ( ) . Length ;
261+ _pendingChars -= _pending . Dequeue ( ) . Text . Length ;
242262
243263 pendingChars = _pendingChars ;
244264 }
@@ -379,7 +399,7 @@ private void EnqueuePending(string entry) {
379399 if ( entry . Length == 0 )
380400 return ;
381401
382- _pending . Enqueue ( entry ) ;
402+ _pending . Enqueue ( new PendingEntry ( entry , _errored , _erroredEngine ) ) ;
383403 _pendingChars += entry . Length ;
384404 }
385405
@@ -466,7 +486,7 @@ private void FlushUpToBudget() {
466486
467487 private bool FlushOneEntry ( ) {
468488 ScriptConsole output ;
469- string entry ;
489+ PendingEntry entry ;
470490 bool morePending ;
471491
472492 lock ( this ) {
@@ -484,8 +504,8 @@ private bool FlushOneEntry() {
484504 }
485505
486506 entry = _pending . Dequeue ( ) ;
487- _pendingChars -= entry . Length ;
488- _lastEntryChars = entry . Length ;
507+ _pendingChars -= entry . Text . Length ;
508+ _lastEntryChars = entry . Text . Length ;
489509 morePending = _pending . Count > 0 ;
490510 }
491511
@@ -498,13 +518,13 @@ private bool FlushOneEntry() {
498518 return true ;
499519 }
500520
501- private void DrainOutput ( ScriptConsole output , string pending ) {
502- if ( string . IsNullOrEmpty ( pending ) )
521+ private void DrainOutput ( ScriptConsole output , PendingEntry pending ) {
522+ if ( string . IsNullOrEmpty ( pending . Text ) )
503523 return ;
504524
505- var prefixed = PrefixStartupOutput ( pending ) ;
506- if ( _errored )
507- output . AppendError ( prefixed , _erroredEngine ) ;
525+ var prefixed = PrefixStartupOutput ( pending . Text ) ;
526+ if ( pending . IsError )
527+ output . AppendError ( prefixed , pending . Engine ) ;
508528 else
509529 output . AppendHtmlFragment ( prefixed , ScriptConsoleConfigs . DefaultBlock ) ;
510530 }
0 commit comments