/// <summary> /// Enumerates the objects in the captured console in a stream fashion. /// The enumeration order is as follows: /// 1. ConsoleLog object. /// 2. For each line: /// a. Line object. /// b. Each Span object. /// c. A null. /// The null is used to indicate termination of a line. /// Note that when ConsoleLog/Line objects are first enumerated, /// their children Line/Span objects are not yet ready. Enumeration /// must proceed for the tree structure to be completed. /// When "populate" is set to $False, this method is GC-efficient in /// that if you discard an object after receiving it, the object /// immediately becomes eligible for garbage collection. /// The effect is that the tree structure is never populated. /// </summary> /// <param name="host">$Host from PowerShell.</param> /// <param name="includeCurrentLine">Whether the current line should be included. /// This can be set to $False if the command is used interactively.</param> /// <param name="behavior">Behavior of trailing space characters.</param> /// <param name="populate">Whether the tree structure is populated.</param> /// <returns>Enumerated objects.</returns> public static IEnumerable <object> StreamCapture(PSHost host, bool includeCurrentLine, TrailingSpaceBehavior behavior, bool populate) { if (host.Name != "ConsoleHost") { throw new NotSupportedException("This method only supports the console host."); } if ((int)behavior < 0 || (int)behavior > 2) { throw new ArgumentOutOfRangeException("behavior"); } PSHostRawUserInterface ui = host.UI.RawUI; int width = ui.BufferSize.Width; int y = ui.CursorPosition.Y + (includeCurrentLine ? 1 : 0); if (width < 1) { throw new ArgumentOutOfRangeException("host.UI.RawUI.BufferSize.Width"); } if (y < 1) { throw new ArgumentOutOfRangeException("host.UI.RawUI.CursorPosition.Y"); } return(StreamCaptureImpl(behavior, ui.GetBufferContents(new Rectangle(0, 0, width - 1, y - 1)), ui.ForegroundColor, ui.BackgroundColor, width, y, populate)); }
private Capturer(TrailingSpaceBehavior tsb, int w, ConsoleColor fg, ConsoleColor bg) { behavior = tsb; width = w; fgDefault = fg; bgDefault = bg; fgCurrent = fg; bgCurrent = bg; space = 0; cjk = 0; sb = new StringBuilder(width + 1); pending = new List <PendingSpan>(width + 1); }
/// <summary> /// Captures the console as a ConsoleLog object. /// </summary> public static ConsoleLog Capture(PSHost host, bool includeCurrentLine, TrailingSpaceBehavior behavior) { ConsoleLog result = null; foreach (object obj in StreamCapture(host, includeCurrentLine, behavior, true)) { if (obj is ConsoleLog) { result = (ConsoleLog)obj; } /* Continue enumeration to populate the structure. */ } return(result); }
public static IEnumerable <string> Capture(PSHost host, bool includeCurrentLine, TrailingSpaceBehavior behavior) { StringBuilder sb = new StringBuilder(); foreach (object obj in Capturer.StreamCapture(host, includeCurrentLine, behavior, false)) { if (ReferenceEquals(obj, null)) { yield return(sb.ToString()); sb.Clear(); } Span span = obj as Span; if (!ReferenceEquals(span, null)) { sb.Append(span.Content); } } }
public static string GetInteractiveHtml(PSHost host, bool includeCurrentLine, TrailingSpaceBehavior behavior) { return(string.Concat(GetInteractiveHtmlImpl( Capturer.StreamCapture(host, includeCurrentLine, behavior, false)))); }
/// <summary> /// Captures the console as a string. /// </summary> public static string Capture(PSHost host, bool includeCurrentLine, TrailingSpaceBehavior behavior) { return(string.Concat(StreamCapture(host, includeCurrentLine, behavior))); }
/// <summary> /// Enumerates the substrings of HTML-formatted ConsoleLog. /// </summary> /// <param name="host">$Host from PowerShell.</param> /// <param name="includeCurrentLine">Whether the current line should be included. /// This can be set to $False if the command is used interactively.</param> /// <param name="behavior">Behavior of trailing space characters.</param> public static IEnumerable <string> StreamCapture(PSHost host, bool includeCurrentLine, TrailingSpaceBehavior behavior) { ConsoleColor fgDefault = ConsoleColor.White, bgDefault = ConsoleColor.Black; ConsoleColor fgLine = ConsoleColor.White, bgLine = ConsoleColor.Black; ConsoleColor fg = ConsoleColor.White, bg = ConsoleColor.Black; bool emptyLine = true; /* The loop directly walks the tree, so we do not need ** the tree to be stored in the returned objects. ** Setting "populate" to $False make it GC-efficient. */ foreach (object obj in Capturer.StreamCapture(host, includeCurrentLine, behavior, false)) { ConsoleLog console = obj as ConsoleLog; if (!ReferenceEquals(console, null)) { yield return("<pre class=\"gl-console gl-console-fg-"); yield return(Helper.ColorToName(fgDefault = console.Foreground)); yield return(" gl-console-bg-"); yield return(Helper.ColorToName(bgLine = bgDefault = console.Background)); yield return("\" data-width=\""); yield return(console.Width.ToString(CultureInfo.InvariantCulture)); yield return("\">\n"); continue; } Line line = obj as Line; if (!ReferenceEquals(line, null)) { yield return("<code class=\"gl-console-line"); if ((fgLine = line.Foreground) != fgDefault) { yield return(" gl-console-fg-"); yield return(Helper.ColorToName(fgLine)); } yield return(" gl-console-bg-"); ConsoleColor bgLine2 = line.Background; if (bgLine != bgLine2) { yield return("change gl-console-bg-"); } yield return((bgLine = bgLine2) != bgDefault ? Helper.ColorToName(bgLine2) : "none"); yield return("\">"); emptyLine = true; continue; } Span span = obj as Span; if (!ReferenceEquals(span, null)) { emptyLine = false; fg = span.Foreground; bg = span.Background; if (fg == fgLine && bg == bgLine) { yield return("<span>"); yield return(Helper.HtmlEncode(span.Content)); yield return("</span>"); } else if (fg == fgLine && bg != bgLine) { yield return("<span class=\"gl-console-bg-"); yield return(Helper.ColorToName(bg)); yield return("\">"); yield return(Helper.HtmlEncode(span.Content)); yield return("</span>"); } else if (fg != fgLine && bg == bgLine) { yield return("<span class=\"gl-console-fg-"); yield return(Helper.ColorToName(fg)); yield return("\">"); yield return(Helper.HtmlEncode(span.Content)); yield return("</span>"); } else { yield return("<span class=\"gl-console-fg-"); yield return(Helper.ColorToName(fg)); yield return(" gl-console-bg-"); yield return(Helper.ColorToName(bg)); yield return("\">"); yield return(Helper.HtmlEncode(span.Content)); yield return("</span>"); } continue; } /* A line has ended. */ yield return(emptyLine ? "<span> </span></code>\n" : "</code>\n"); } yield return("</pre>"); }
private static IEnumerable <object> StreamCaptureImpl(TrailingSpaceBehavior behavior, BufferCell[,] cells, ConsoleColor fg, ConsoleColor bg, int width, int y, bool populate) { List <Line> lines = populate ? new List <Line>(y) : null; Capturer automaton = new Capturer(behavior, width, fg, bg); List <PendingSpan> pending = automaton.pending; yield return(new ConsoleLog(fg, bg, width, populate ? lines.AsReadOnly() : EmptyLineList)); for (int line = 0; line != y; ++line) { for (int column = 0; column != width; ++column) { BufferCell target = cells[line, column]; char ch = target.Character; if (target.BufferCellType == BufferCellType.Complete || ch != '\0') { automaton.AddCharacter(ch, target.ForegroundColor, target.BackgroundColor); } else { automaton.AddCJK(); } } automaton.CompleteCurrentSpan(); Line ln; /* This should not happen, but just being safe. */ if (pending.Count == 0) { ln = new Line(fg, bg, EmptySpanList); if (populate) { lines.Add(ln); } yield return(ln); yield return(null); continue; } ConsoleColor fgLine, bgLine; automaton.ResolveLineColors(out fgLine, out bgLine); automaton.RemoveTrailingSpace(bgLine); List <Span> spans = populate ? new List <Span>(pending.Count) : null; ln = new Line(fgLine, bgLine, populate ? spans.AsReadOnly() : EmptySpanList); if (populate) { lines.Add(ln); } yield return(ln); foreach (PendingSpan sp in pending) { if (sp.Trimmed.Length == 0) { sp.Foreground = fgLine; } Span span = sp.ToSpan(); if (populate) { spans.Add(span); } yield return(span); } yield return(null); pending.Clear(); } }