// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using System; using System.Management.Automation; namespace Microsoft.PowerShell.MarkdownRender { /// /// Enum to name all the properties of PSMarkdownOptionInfo. /// public enum MarkdownOptionInfoProperty { /// /// Property name Header1. /// Header1, /// /// Property name Header2. /// Header2, /// /// Property name Header3. /// Header3, /// /// Property name Header4. /// Header4, /// /// Property name Header5. /// Header5, /// /// Property name Header6. /// Header6, /// /// Property name Code. /// Code, /// /// Property name Link. /// Link, /// /// Property name Image. /// Image, /// /// Property name EmphasisBold. /// EmphasisBold, /// /// Property name EmphasisItalics. /// EmphasisItalics } /// /// Class to represent color preference options for various Markdown elements. /// public sealed class PSMarkdownOptionInfo { private const char Esc = (char)0x1b; private const string EndSequence = "[0m"; /// /// Gets or sets current VT100 escape sequence for header 1. /// public string Header1 { get; set; } /// /// Gets or sets current VT100 escape sequence for header 2. /// public string Header2 { get; set; } /// /// Gets or sets current VT100 escape sequence for header 3. /// public string Header3 { get; set; } /// /// Gets or sets current VT100 escape sequence for header 4. /// public string Header4 { get; set; } /// /// Gets or sets current VT100 escape sequence for header 5. /// public string Header5 { get; set; } /// /// Gets or sets current VT100 escape sequence for header 6. /// public string Header6 { get; set; } /// /// Gets or sets current VT100 escape sequence for code inline and code blocks. /// public string Code { get; set; } /// /// Gets or sets current VT100 escape sequence for links. /// public string Link { get; set; } /// /// Gets or sets current VT100 escape sequence for images. /// public string Image { get; set; } /// /// Gets or sets current VT100 escape sequence for bold text. /// public string EmphasisBold { get; set; } /// /// Gets or sets current VT100 escape sequence for italics text. /// public string EmphasisItalics { get; set; } /// /// Gets or sets a value indicating whether VT100 escape sequences should be added. Default it true. /// public bool EnableVT100Encoding { get; set; } /// /// Get the property as an rendered escape sequence. /// This is used by formatting system for displaying. /// /// Name of the property to get as escape sequence. /// Specified property name as escape sequence. public string AsEscapeSequence(MarkdownOptionInfoProperty propertyName) { switch (propertyName) { case MarkdownOptionInfoProperty.Header1: return string.Concat(Esc, Header1, Header1, Esc, EndSequence); case MarkdownOptionInfoProperty.Header2: return string.Concat(Esc, Header2, Header2, Esc, EndSequence); case MarkdownOptionInfoProperty.Header3: return string.Concat(Esc, Header3, Header3, Esc, EndSequence); case MarkdownOptionInfoProperty.Header4: return string.Concat(Esc, Header4, Header4, Esc, EndSequence); case MarkdownOptionInfoProperty.Header5: return string.Concat(Esc, Header5, Header5, Esc, EndSequence); case MarkdownOptionInfoProperty.Header6: return string.Concat(Esc, Header6, Header6, Esc, EndSequence); case MarkdownOptionInfoProperty.Code: return string.Concat(Esc, Code, Code, Esc, EndSequence); case MarkdownOptionInfoProperty.Link: return string.Concat(Esc, Link, Link, Esc, EndSequence); case MarkdownOptionInfoProperty.Image: return string.Concat(Esc, Image, Image, Esc, EndSequence); case MarkdownOptionInfoProperty.EmphasisBold: return string.Concat(Esc, EmphasisBold, EmphasisBold, Esc, EndSequence); case MarkdownOptionInfoProperty.EmphasisItalics: return string.Concat(Esc, EmphasisItalics, EmphasisItalics, Esc, EndSequence); default: break; } return null; } /// /// Initializes a new instance of the class and sets dark as the default theme. /// public PSMarkdownOptionInfo() { SetDarkTheme(); EnableVT100Encoding = true; } private const string Header1Dark = "[7m"; private const string Header2Dark = "[4;93m"; private const string Header3Dark = "[4;94m"; private const string Header4Dark = "[4;95m"; private const string Header5Dark = "[4;96m"; private const string Header6Dark = "[4;97m"; private const string CodeDark = "[48;2;155;155;155;38;2;30;30;30m"; private const string CodeMacOS = "[107;95m"; private const string LinkDark = "[4;38;5;117m"; private const string ImageDark = "[33m"; private const string EmphasisBoldDark = "[1m"; private const string EmphasisItalicsDark = "[36m"; private const string Header1Light = "[7m"; private const string Header2Light = "[4;33m"; private const string Header3Light = "[4;34m"; private const string Header4Light = "[4;35m"; private const string Header5Light = "[4;36m"; private const string Header6Light = "[4;30m"; private const string CodeLight = "[48;2;155;155;155;38;2;30;30;30m"; private const string LinkLight = "[4;38;5;117m"; private const string ImageLight = "[33m"; private const string EmphasisBoldLight = "[1m"; private const string EmphasisItalicsLight = "[36m"; /// /// Set all preference for dark theme. /// public void SetDarkTheme() { Header1 = Header1Dark; Header2 = Header2Dark; Header3 = Header3Dark; Header4 = Header4Dark; Header5 = Header5Dark; Header6 = Header6Dark; Link = LinkDark; Image = ImageDark; EmphasisBold = EmphasisBoldDark; EmphasisItalics = EmphasisItalicsDark; SetCodeColor(isDarkTheme: true); } /// /// Set all preference for light theme. /// public void SetLightTheme() { Header1 = Header1Light; Header2 = Header2Light; Header3 = Header3Light; Header4 = Header4Light; Header5 = Header5Light; Header6 = Header6Light; Link = LinkLight; Image = ImageLight; EmphasisBold = EmphasisBoldLight; EmphasisItalics = EmphasisItalicsLight; SetCodeColor(isDarkTheme: false); } private void SetCodeColor(bool isDarkTheme) { // MacOS terminal app does not support extended colors for VT100, so we special case for it. Code = Platform.IsMacOS ? CodeMacOS : isDarkTheme ? CodeDark : CodeLight; } } /// /// Class to represent default VT100 escape sequences. /// public class VT100EscapeSequences { private const char Esc = (char)0x1B; private readonly string endSequence = Esc + "[0m"; // For code blocks, [500@ make sure that the whole line has background color. private const string LongBackgroundCodeBlock = "[500@"; private readonly PSMarkdownOptionInfo options; /// /// Initializes a new instance of the class. /// /// PSMarkdownOptionInfo object to initialize with. public VT100EscapeSequences(PSMarkdownOptionInfo optionInfo) { if (optionInfo == null) { throw new ArgumentNullException(nameof(optionInfo)); } options = optionInfo; } /// /// Class to represent default VT100 escape sequences. /// /// Text of the header to format. /// Formatted Header 1 string. public string FormatHeader1(string headerText) { return FormatHeader(headerText, options.Header1); } /// /// Class to represent default VT100 escape sequences. /// /// Text of the header to format. /// Formatted Header 2 string. public string FormatHeader2(string headerText) { return FormatHeader(headerText, options.Header2); } /// /// Class to represent default VT100 escape sequences. /// /// Text of the header to format. /// Formatted Header 3 string. public string FormatHeader3(string headerText) { return FormatHeader(headerText, options.Header3); } /// /// Class to represent default VT100 escape sequences. /// /// Text of the header to format. /// Formatted Header 4 string. public string FormatHeader4(string headerText) { return FormatHeader(headerText, options.Header4); } /// /// Class to represent default VT100 escape sequences. /// /// Text of the header to format. /// Formatted Header 5 string. public string FormatHeader5(string headerText) { return FormatHeader(headerText, options.Header5); } /// /// Class to represent default VT100 escape sequences. /// /// Text of the header to format. /// Formatted Header 6 string. public string FormatHeader6(string headerText) { return FormatHeader(headerText, options.Header6); } /// /// Class to represent default VT100 escape sequences. /// /// Text of the code block to format. /// True if it is a inline code block, false otherwise. /// Formatted code block string. public string FormatCode(string codeText, bool isInline) { bool isVT100Enabled = options.EnableVT100Encoding; if (isInline) { if (isVT100Enabled) { return string.Concat(Esc, options.Code, codeText, endSequence); } else { return codeText; } } else { if (isVT100Enabled) { return string.Concat(Esc, options.Code, codeText, Esc, LongBackgroundCodeBlock, endSequence); } else { return codeText; } } } /// /// Class to represent default VT100 escape sequences. /// /// Text of the link to format. /// URL of the link. /// True url should be hidden, false otherwise. Default is true. /// Formatted link string. public string FormatLink(string linkText, string url, bool hideUrl = true) { bool isVT100Enabled = options.EnableVT100Encoding; if (hideUrl) { if (isVT100Enabled) { return string.Concat(Esc, options.Link, "\"", linkText, "\"", endSequence); } else { return string.Concat("\"", linkText, "\""); } } else { if (isVT100Enabled) { return string.Concat("\"", linkText, "\" (", Esc, options.Link, url, endSequence, ")"); } else { return string.Concat("\"", linkText, "\" (", url, ")"); } } } /// /// Class to represent default VT100 escape sequences. /// /// Text to format as emphasis. /// True if it is to be formatted as bold, false to format it as italics. /// Formatted emphasis string. public string FormatEmphasis(string emphasisText, bool isBold) { var sequence = isBold ? options.EmphasisBold : options.EmphasisItalics; if (options.EnableVT100Encoding) { return string.Concat(Esc, sequence, emphasisText, endSequence); } else { return emphasisText; } } /// /// Class to represent default VT100 escape sequences. /// /// Text of the image to format. /// Formatted image string. public string FormatImage(string altText) { var text = altText; if (string.IsNullOrEmpty(altText)) { text = "Image"; } if (options.EnableVT100Encoding) { return string.Concat(Esc, options.Image, "[", text, "]", endSequence); } else { return string.Concat("[", text, "]"); } } private string FormatHeader(string headerText, string headerEscapeSequence) { if (options.EnableVT100Encoding) { return string.Concat(Esc, headerEscapeSequence, headerText, endSequence); } else { return headerText; } } } }