using System; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; using System.Text; namespace Spectre.Console { /// /// Factory for creating an ANSI console. /// public sealed class AnsiConsoleFactory { private readonly List _enrichers; /// /// Initializes a new instance of the class. /// public AnsiConsoleFactory() { _enrichers = new List { new AppVeyorProfile(), new BambooProfile(), new BitbucketProfile(), new BitriseProfile(), new ContinuaCIProfile(), new GitHubProfile(), new GitLabProfile(), new GoCDProfile(), new JenkinsProfile(), new MyGetProfile(), new TeamCityProfile(), new TfsProfile(), new TravisProfile(), }; } /// /// Initializes a new instance of the class. /// /// The profile enrichers to use. public AnsiConsoleFactory(IEnumerable enrichers) { _enrichers = new List(enrichers ?? Enumerable.Empty()); } /// /// Creates a new without default profile enrichers. /// /// A new without default profile enrichers. public static AnsiConsoleFactory NoEnrichers() { return new AnsiConsoleFactory(Enumerable.Empty()); } /// /// Creates an ANSI console. /// /// The settings. /// An implementation of . public IAnsiConsole Create(AnsiConsoleSettings settings) { if (settings is null) { throw new ArgumentNullException(nameof(settings)); } var buffer = settings.Out ?? System.Console.Out; // Detect if the terminal support ANSI or not var (supportsAnsi, legacyConsole) = DetectAnsi(settings, buffer); // Use the provided encoding or fall back to UTF-8 var encoding = buffer.IsStandardOut() ? System.Console.OutputEncoding : Encoding.UTF8; // Get the color system var colorSystem = settings.ColorSystem == ColorSystemSupport.Detect ? ColorSystemDetector.Detect(supportsAnsi) : (ColorSystem)settings.ColorSystem; // Get whether or not we consider the terminal interactive var interactive = settings.Interactive == InteractionSupport.Yes; if (settings.Interactive == InteractionSupport.Detect) { interactive = Environment.UserInteractive; } var profile = new Profile("Default", buffer, encoding) { ColorSystem = colorSystem, }; profile.Capabilities.Ansi = supportsAnsi; profile.Capabilities.Links = supportsAnsi && !legacyConsole; profile.Capabilities.Legacy = legacyConsole; profile.Capabilities.Interactive = interactive; // Enrich the profile var variables = GetEnvironmentVariables(settings); var customEnrichers = settings.Enrichers ?? Enumerable.Empty(); foreach (var enricher in _enrichers.Concat(customEnrichers)) { if (enricher.Enabled(variables)) { enricher.Enrich(profile); } } return new AnsiConsoleFacade(profile); } private static IDictionary GetEnvironmentVariables(AnsiConsoleSettings settings) { if (settings.EnvironmentVariables != null) { return new Dictionary(settings.EnvironmentVariables, StringComparer.OrdinalIgnoreCase); } return Environment.GetEnvironmentVariables() .Cast() .Aggregate( new Dictionary(StringComparer.OrdinalIgnoreCase), (dictionary, entry) => { var key = (string)entry.Key; if (!dictionary.TryGetValue(key, out _)) { dictionary.Add(key, entry.Value as string ?? string.Empty); } return dictionary; }, dictionary => dictionary); } private static (bool Ansi, bool Legacy) DetectAnsi(AnsiConsoleSettings settings, System.IO.TextWriter buffer) { var supportsAnsi = settings.Ansi == AnsiSupport.Yes; var legacyConsole = false; if (settings.Ansi == AnsiSupport.Detect) { (supportsAnsi, legacyConsole) = AnsiDetector.Detect(true); // Check whether or not this is a legacy console from the existing instance (if any). // We need to do this because once we upgrade the console to support ENABLE_VIRTUAL_TERMINAL_PROCESSING // on Windows, there is no way of detecting whether or not we're running on a legacy console or not. if (AnsiConsole.Created && !legacyConsole && buffer.IsStandardOut() && AnsiConsole.Profile.Capabilities.Legacy) { legacyConsole = AnsiConsole.Profile.Capabilities.Legacy; } } else { if (buffer.IsStandardOut()) { // Are we running on Windows? if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { // Not the first console we're creating? if (AnsiConsole.Created) { legacyConsole = AnsiConsole.Profile.Capabilities.Legacy; } else { // Try detecting whether or not this (_, legacyConsole) = AnsiDetector.Detect(false); } } } } return (supportsAnsi, legacyConsole); } } }