diff --git a/Cli/Cli.csproj b/Cli/Cli.csproj index a7b9158..3aae8f6 100644 --- a/Cli/Cli.csproj +++ b/Cli/Cli.csproj @@ -1,43 +1,24 @@  + + Exe net8.0;net7.0;net6.0 enable lwac Trivial.Web - Trivial - Kingcean Tuan - Nanchang Jinchen Software Co., Ltd. - 7.1.0 - 7.1.0.0 - 7.1.0.0 CLI for local web app. - Copyright (c) 2022 Kingcean Tuan. - MIT https://github.com/nuscien/winkit false true snupkg - https://github.com/nuscien/winkit - git - true logo.png cli lwa - README.md - ..\Materials\WinKit.ico - 10.0 - True - ..\Trivial.snk - - ..\bin\Debug\ - ..\bin\$(Configuration)\$(TargetFramework)\lwac.xml - - - - ..\bin\Release\ + + ..\bin\$(Configuration)\ ..\bin\$(Configuration)\$(TargetFramework)\lwac.xml @@ -51,16 +32,6 @@ - - - - - - - - - - diff --git a/Common.props b/Common.props new file mode 100644 index 0000000..952b896 --- /dev/null +++ b/Common.props @@ -0,0 +1,40 @@ + + + Trivial + Kingcean Tuan + Nanchang Jinchen Software Co., Ltd. + 7.2.0 + 7.2.0.0 + 7.2.0.0 + Copyright (c) 2018 Kingcean Tuan. + MIT + https://github.com/nuscien/trivial + git + true + README.md + 12.0 + True + ..\Trivial.snk + + + + NETOLDVER + + + + + + + + + + + + + + + + + + + diff --git a/Common/Common.csproj b/Common/Common.csproj index 7c572e1..82206e2 100644 --- a/Common/Common.csproj +++ b/Common/Common.csproj @@ -1,4 +1,7 @@  + + + net7.0-windows10.0.17763.0;net7.0-windows10.0.19041.0;net7.0-windows10.0.22000.0;net6.0-windows10.0.17763.0;net6.0-windows10.0.19041.0;net6.0-windows10.0.22000.0 10.0.17763.0 @@ -7,38 +10,15 @@ Trivial.WindowsKit win10-x86;win10-x64;win10-arm64 true - Trivial - Kingcean Tuan - Nanchang Jinchen Software Co., Ltd. - 7.1.0 - 7.1.0.0 - 7.1.0.0 Some advanced visual controls and utilities for Windows app. - Copyright (c) 2022 Kingcean Tuan. - MIT - https://github.com/nuscien/winkit - true true snupkg - https://github.com/nuscien/winkit - git - true logo.png ui tiles lwa - README.md - ..\Materials\WinKit.ico - 10.0 - True - ..\Trivial.snk - - - - ..\bin\Debug\ - ..\bin\$(Configuration)\$(TargetFramework)\Trivial.WindowsKit.xml - - ..\bin\Release\ + + ..\bin\$(Configuration)\ ..\bin\$(Configuration)\$(TargetFramework)\Trivial.WindowsKit.xml @@ -54,11 +34,6 @@ - - - - - @@ -81,8 +56,6 @@ - - diff --git a/Console/CommandLine/AnsiGenerator.cs b/Console/CommandLine/AnsiGenerator.cs new file mode 100644 index 0000000..50e08b5 --- /dev/null +++ b/Console/CommandLine/AnsiGenerator.cs @@ -0,0 +1,259 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Trivial.CommandLine; + +/// +/// Code generator of ANSI escape sequences. +/// +internal static class AnsiCodeGenerator +{ + // https://docs.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences + + /// + /// The sign of escape. + /// + public const string Esc = "\u001b"; + + /// + /// Gets the ANSI escape sequences to set the foreground color. + /// + /// The color. + /// The specific ANSI escape sequences. + public static string Foreground(ConsoleColor? color) + => color.HasValue ? color.Value switch + { + ConsoleColor.Black => $"{Esc}[30m", + ConsoleColor.White => $"{Esc}[97m", + ConsoleColor.DarkRed => $"{Esc}[31m", + ConsoleColor.DarkGreen => $"{Esc}[32m", + ConsoleColor.DarkYellow => $"{Esc}[33m", + ConsoleColor.DarkBlue => $"{Esc}[34m", + ConsoleColor.DarkMagenta => $"{Esc}[35m", + ConsoleColor.DarkCyan => $"{Esc}[36m", + ConsoleColor.DarkGray => $"{Esc}[90m", + ConsoleColor.Red => $"{Esc}[91m", + ConsoleColor.Green => $"{Esc}[92m", + ConsoleColor.Yellow => $"{Esc}[93m", + ConsoleColor.Blue => $"{Esc}[94m", + ConsoleColor.Magenta => $"{Esc}[95m", + ConsoleColor.Cyan => $"{Esc}[96m", + ConsoleColor.Gray => $"{Esc}[37m", + _ => string.Empty + } : string.Empty; + + /// + /// Gets the ANSI escape sequences to set the foreground color. + /// + /// The red. + /// The green. + /// The blue. + /// The specific ANSI escape sequences. + public static string Foreground(byte r, byte g, byte b) + => $"{Esc}[38;2;{r};{g};{b}m"; + + /// + /// Gets the ANSI escape sequences to set the foreground color. + /// + /// The color. + /// The specific ANSI escape sequences. + public static string Foreground(System.Drawing.Color color) + => $"{Esc}[38;2;{color.R};{color.G};{color.B}m"; + + /// + /// Gets the ANSI escape sequences to set the foreground color. + /// + /// true if resets color; otherwise, false. + /// The specific ANSI escape sequences. + public static string Foreground(bool reset) + => reset ? $"{Esc}[39m" : string.Empty; + + /// + /// Gets the ANSI escape sequences to set the background color. + /// + /// The color. + /// The specific ANSI escape sequences. + public static string Background(ConsoleColor? color) + => color.HasValue ? color.Value switch + { + ConsoleColor.Black => $"{Esc}[40m", + ConsoleColor.White => $"{Esc}[107m", + ConsoleColor.DarkRed => $"{Esc}[41m", + ConsoleColor.DarkGreen => $"{Esc}[42m", + ConsoleColor.DarkYellow => $"{Esc}[43m", + ConsoleColor.DarkBlue => $"{Esc}[44m", + ConsoleColor.DarkMagenta => $"{Esc}[45m", + ConsoleColor.DarkCyan => $"{Esc}[46m", + ConsoleColor.DarkGray => $"{Esc}[100m", + ConsoleColor.Red => $"{Esc}[101m", + ConsoleColor.Green => $"{Esc}[102m", + ConsoleColor.Yellow => $"{Esc}[103m", + ConsoleColor.Blue => $"{Esc}[104m", + ConsoleColor.Magenta => $"{Esc}[105m", + ConsoleColor.Cyan => $"{Esc}[106m", + ConsoleColor.Gray => $"{Esc}[47m", + _ => string.Empty + } : string.Empty; + + /// + /// Gets the ANSI escape sequences to set the background color. + /// + /// The red. + /// The green. + /// The blue. + /// The specific ANSI escape sequences. + public static string Background(byte r, byte g, byte b) + => $"{Esc}[48;2;{r};{g};{b}m"; + + /// + /// Gets the ANSI escape sequences to set the background color. + /// + /// The color. + /// The specific ANSI escape sequences. + public static string Background(System.Drawing.Color color) + => $"{Esc}[48;2;{color.R};{color.G};{color.B}m"; + + /// + /// Gets the ANSI escape sequences to set the foreground color. + /// + /// true if resets color; otherwise, false. + /// The specific ANSI escape sequences. + public static string Background(bool reset) + => reset ? $"{Esc}[49m" : string.Empty; + + public static string AttributesOff() => $"{Esc}[0m"; + public static string Blink(bool enable) => enable ? $"{Esc}[5m" : $"{Esc}[25m"; + public static string Bold(bool enable) => enable ? $"{Esc}[1m" : $"{Esc}[22m"; + public static string TextHidden() => $"{Esc}[8m"; + public static string Reverse(bool enable) => enable ? $"{Esc}[7m" : $"{Esc}[27m"; + public static string Italic(bool enable) => enable ? $"{Esc}[3m" : $"{Esc}[23m"; + public static string Underline(bool enable) => enable ? $"{Esc}[4m" : $"{Esc}[24m"; + public static string Strikeout(bool enable) => enable ? $"{Esc}[9m" : $"{Esc}[29m"; + + public static string MoveCursorX(int columns) + { + if (columns == 0) return string.Empty; + return columns > 0 ? $"{Esc}[{columns}C" : $"{Esc}[{-columns}D"; + } + + public static string MoveCursorY(int lines) + { + if (lines == 0) return string.Empty; + return lines > 0 ? $"{Esc}[{lines}B" : $"{Esc}[{-lines}A"; + } + + public static string MoveCursorBy(int x, int y) + => $"{MoveCursorX(x)}{MoveCursorY(y)}"; + + public static string MoveCursorNextLine(int line = 1) + => $"{Esc}[{line}E"; + + public static string MoveCursorTo(int left, int top = -1) + => top >= 0 + ? $"{Esc}[{top + 1};{(left >= 0 ? (left + 1).ToString("g") : string.Empty)}H" + : $"{Esc}[{(left >= 0 ? (left + 1).ToString("g") : string.Empty)}G"; + + public static string CursorVisibility(bool show) + => show ? $"{Esc}[?25h" : $"{Esc}[?25l"; + + public static string ScrollBy(int step) + { + if (step == 0) return string.Empty; + var r = step > 0 ? $"{Esc}[T" : $"{Esc}[S"; + var i = Math.Abs(step); + if (i == 1) return r; + var s = new StringBuilder(r); + for (var j = 1; j < i; j++) + { + s.Append(r); + } + + return s.ToString(); + } + + /// + /// Code to clear lines. + /// + public static string Clear(StyleConsole.RelativeAreas area) + => area switch + { + StyleConsole.RelativeAreas.Line => $"{Esc}[2K", + StyleConsole.RelativeAreas.ToBeginningOfLine => $"{Esc}[1K", + StyleConsole.RelativeAreas.ToEndOfLine => $"{Esc}[K", + StyleConsole.RelativeAreas.EntireScreen => $"{Esc}[2J", + StyleConsole.RelativeAreas.ToBeginningOfScreen => $"{Esc}[1J", + StyleConsole.RelativeAreas.ToEndOfScreen => $"{Esc}[J", + StyleConsole.RelativeAreas.EntireBuffer => $"{Esc}[3J", + _ => string.Empty + }; + + public static string SaveCursorPosition() => $"{Esc}7"; + + public static string RestoreCursorPosition() => $"{Esc}8"; + + public static string Remove(int count = 1) + { + if (count == 0) return string.Empty; + return count > 0 ? $"{Esc}[{count}X" : $"{Esc}[{-count}P"; + } + + public static ConsoleColor ToConsoleColor(System.Drawing.Color color) + => ToConsoleColor(color.R, color.G, color.B); + + public static ConsoleColor ToConsoleColor(byte r, byte g, byte b) + { + if (r >= 220 && g >= 220 && b >= 220) + return ConsoleColor.White; + if (r <= 48 && g <= 48 && b <= 48) + return ConsoleColor.Black; + var isLight = r >= 192 || g >= 192 || b >= 192; + var rank = new Maths.RankResult3(r, g, b, CompareColorChannel); + var s = $"{rank.RankFor1}{rank.RankFor2}{rank.RankFor3}"; + if (s.Contains('3')) + { + var single = rank.Number1 - rank.Number2 >= rank.Number2 - rank.Number3; + switch (s) + { + case "123": + s = single ? "122" : "112"; + break; + case "132": + s = single ? "122" : "121"; + break; + case "312": + s = single ? "212" : "211"; + break; + case "213": + s = single ? "212" : "112"; + break; + case "321": + s = single ? "221" : "211"; + break; + case "231": + s = single ? "221" : "121"; + break; + } + } + + return s switch + { + "122" => isLight ? ConsoleColor.Red : ConsoleColor.DarkRed, + "212" => isLight ? ConsoleColor.Green : ConsoleColor.DarkBlue, + "221" => isLight ? ConsoleColor.Blue : ConsoleColor.DarkBlue, + "112" => isLight ? ConsoleColor.Yellow : ConsoleColor.DarkYellow, + "121" => isLight ? ConsoleColor.Magenta : ConsoleColor.DarkMagenta, + "211" => isLight ? ConsoleColor.Cyan : ConsoleColor.DarkCyan, + _ => isLight ? ConsoleColor.Gray : ConsoleColor.DarkGray + }; + } + + private static int CompareColorChannel(int a, int b) + { + var diff = a - b; + if (diff > -48 && diff < 48) return 0; + return diff; + } +} diff --git a/Console/CommandLine/Default.cs b/Console/CommandLine/Default.cs new file mode 100644 index 0000000..49f3ace --- /dev/null +++ b/Console/CommandLine/Default.cs @@ -0,0 +1,1724 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Drawing; +using System.IO; +using System.Linq; +using System.Security; +using System.Text; +using System.Threading.Tasks; + +using Trivial.Collection; +using Trivial.Security; +using Trivial.Tasks; +using Trivial.Text; + +namespace Trivial.CommandLine; + +/// +/// The command line interface. +/// +public static class DefaultConsole +{ + /// + /// Writes the specified string value to the standard output stream. + /// Note it may not flush immediately. + /// + /// The text content. + public static void Write(ConsoleText content) + => StyleConsole.Default.Write(content); + + /// + /// Writes the specified string value to the standard output stream. + /// Note it may not flush immediately. + /// + /// The text content 1. + /// The text content 2. + /// The additional text content collection. + public static void Write(ConsoleText content1, ConsoleText content2, params ConsoleText[] additionalContext) + => StyleConsole.Default.Write(content1, content2, additionalContext); + + /// + /// Writes the specified string value to the standard output stream. + /// Note it may not flush immediately. + /// + /// The text content collection. + public static void Write(IEnumerable content) + => StyleConsole.Default.Write(content); + + /// + /// Writes the specified string value to the standard output stream. + /// Note it may not flush immediately. + /// + /// A composite format string to output. + /// An object array that contains zero or more objects to format. + /// format is invalid. -or- The index of a format item is less than zero, or greater than or equal to the length of the args array. + public static void Write(string s, params object[] args) + => StyleConsole.Default.Write(s, args); + + /// + /// Writes the specified string value to the standard output stream. + /// Note it may not flush immediately. + /// + /// The content style. + /// A composite format string to output. + /// An object array that contains zero or more objects to format. + /// format is invalid. -or- The index of a format item is less than zero, or greater than or equal to the length of the args array. + public static void Write(ConsoleTextStyle style, string s, params object[] args) + => StyleConsole.Default.Write(style, s, args); + + /// + /// Writes the specified string value to the standard output stream. + /// Note it may not flush immediately. + /// + /// The foreground color. + /// A composite format string to output. + /// An object array that contains zero or more objects to format. + /// format is invalid. -or- The index of a format item is less than zero, or greater than or equal to the length of the args array. + public static void Write(ConsoleColor foreground, string s, params object[] args) + => StyleConsole.Default.Write(foreground, s, args); + + /// + /// Writes the specified string value to the standard output stream. + /// Note it may not flush immediately. + /// + /// The foreground color. + /// The background color. + /// A composite format string to output. + /// An object array that contains zero or more objects to format. + /// format is invalid. -or- The index of a format item is less than zero, or greater than or equal to the length of the args array. + public static void Write(ConsoleColor? foreground, ConsoleColor? background, string s, params object[] args) + => StyleConsole.Default.Write(foreground, background, s, args); + + /// + /// Writes the specified string value to the standard output stream. + /// Note it may not flush immediately. + /// + /// The foreground color. + /// A composite format string to output. + /// An object array that contains zero or more objects to format. + /// format is invalid. -or- The index of a format item is less than zero, or greater than or equal to the length of the args array. + public static void Write(Color foreground, string s, params object[] args) + => StyleConsole.Default.Write(foreground, s, args); + + /// + /// Writes the specified string value to the standard output stream. + /// Note it may not flush immediately. + /// + /// The foreground color. + /// The background color. + /// A composite format string to output. + /// An object array that contains zero or more objects to format. + /// format is invalid. -or- The index of a format item is less than zero, or greater than or equal to the length of the args array. + public static void Write(Color foreground, Color background, string s, params object[] args) + => StyleConsole.Default.Write(foreground, background, s, args); + + /// + /// Writes the specified string value to the standard output stream. + /// Note it may not flush immediately. + /// + /// The style. + /// A composite format string to output. + /// An object array that contains zero or more objects to format. + /// format is invalid. -or- The index of a format item is less than zero, or greater than or equal to the length of the args array. + public static void Write(IConsoleTextPrettier style, string s, params object[] args) + => StyleConsole.Default.Write(style, s, args); + + /// + /// Writes the specified string value to the standard output stream. + /// Note it may not flush immediately. + /// + /// A composite format string to output. + public static void Write(StringBuilder s) + => StyleConsole.Default.Write(s); + + /// + /// Writes the specified string value to the standard output stream. + /// Note it may not flush immediately. + /// + /// The content style. + /// A composite format string to output. + public static void Write(ConsoleTextStyle style, StringBuilder s) + => StyleConsole.Default.Write(style, s); + + /// + /// Writes the specified string value to the standard output stream. + /// Note it may not flush immediately. + /// + /// The foreground color. + /// A composite format string to output. + public static void Write(ConsoleColor foreground, StringBuilder s) + => StyleConsole.Default.Write(foreground, s); + + /// + /// Writes the specified string value to the standard output stream. + /// Note it may not flush immediately. + /// + /// The foreground color. + /// The background color. + /// A composite format string to output. + public static void Write(ConsoleColor? foreground, ConsoleColor? background, StringBuilder s) + => StyleConsole.Default.Write(foreground, background, s); + + /// + /// Writes the specified string value to the standard output stream. + /// Note it may not flush immediately. + /// + /// The foreground color. + /// A composite format string to output. + public static void Write(Color foreground, StringBuilder s) + => StyleConsole.Default.Write(foreground, s); + + /// + /// Writes the specified string value to the standard output stream. + /// Note it may not flush immediately. + /// + /// The foreground color. + /// The background color. + /// A composite format string to output. + public static void Write(Color foreground, Color background, StringBuilder s) + => StyleConsole.Default.Write(foreground, background, s); + + /// + /// Writes the specified string value to the standard output stream. + /// Note it may not flush immediately. + /// + /// The style. + /// A composite format string to output. + public static void Write(IConsoleTextPrettier style, StringBuilder s) + => StyleConsole.Default.Write(style, s); + + /// + /// Writes the specified string value to the standard output stream. + /// Note it may not flush immediately. + /// + /// A composite format string to output. + public static void Write(SecureString s) + => StyleConsole.Default.Write(s.ToUnsecureString()); + + /// + /// Writes the specified string value to the standard output stream. + /// Note it may not flush immediately. + /// + /// The content style. + /// A composite format string to output. + public static void Write(ConsoleTextStyle style, SecureString s) + => StyleConsole.Default.Write(style, s.ToUnsecureString()); + + /// + /// Writes the specified string value to the standard output stream. + /// Note it may not flush immediately. + /// + /// The foreground color. + /// A composite format string to output. + public static void Write(ConsoleColor foreground, SecureString s) + => StyleConsole.Default.Write(foreground, s.ToUnsecureString()); + + /// + /// Writes the specified string value to the standard output stream. + /// Note it may not flush immediately. + /// + /// The foreground color. + /// The background color. + /// A composite format string to output. + public static void Write(ConsoleColor? foreground, ConsoleColor? background, SecureString s) + => StyleConsole.Default.Write(foreground, background, s.ToUnsecureString()); + + /// + /// Writes the specified number to the standard output stream. + /// Note it may not flush immediately. + /// + /// A number to output. + public static void Write(int number) + => StyleConsole.Default.Write(number); + + /// + /// Writes the specified number to the standard output stream. + /// Note it may not flush immediately. + /// + /// A number to output. + /// A standard or custom numeric format string. + /// format is invalid or not supported. + public static void Write(int number, string format) + => StyleConsole.Default.Write(number, format); + + /// + /// Writes the specified number to the standard output stream. + /// Note it may not flush immediately. + /// + /// The content style. + /// A number to output. + public static void Write(ConsoleTextStyle style, int number) + => StyleConsole.Default.Write(style, number); + + /// + /// Writes the specified number to the standard output stream. + /// Note it may not flush immediately. + /// + /// The content style. + /// A number to output. + /// A standard or custom numeric format string. + /// format is invalid or not supported. + public static void Write(ConsoleTextStyle style, int number, string format) + => StyleConsole.Default.Write(style, number, format); + + /// + /// Writes the specified number to the standard output stream. + /// Note it may not flush immediately. + /// + /// The foreground color. + /// A number to output. + public static void Write(ConsoleColor foreground, int number) + => StyleConsole.Default.Write(foreground, number); + + /// + /// Writes the specified number to the standard output stream. + /// Note it may not flush immediately. + /// + /// The foreground color. + /// A number to output. + /// A standard or custom numeric format string. + /// format is invalid or not supported. + public static void Write(ConsoleColor foreground, int number, string format) + => StyleConsole.Default.Write(foreground, number, format); + + /// + /// Writes the specified number to the standard output stream. + /// Note it may not flush immediately. + /// + /// The foreground color. + /// The background color. + /// A number to output. + public static void Write(ConsoleColor? foreground, ConsoleColor? background, int number) + => StyleConsole.Default.Write(foreground, background, number); + + /// + /// Writes the specified number to the standard output stream. + /// Note it may not flush immediately. + /// + /// The foreground color. + /// The background color. + /// A number to output. + /// A standard or custom numeric format string. + /// format is invalid or not supported. + public static void Write(ConsoleColor? foreground, ConsoleColor? background, int number, string format) + => StyleConsole.Default.Write(foreground, background, number, format); + + /// + /// Writes the specified number to the standard output stream. + /// Note it may not flush immediately. + /// + /// A number to output. + public static void Write(long number) + => StyleConsole.Default.Write(number); + + /// + /// Writes the specified number to the standard output stream. + /// Note it may not flush immediately. + /// + /// The content style. + /// A number to output. + public static void Write(ConsoleTextStyle style, long number) + => StyleConsole.Default.Write(style, number); + + /// + /// Writes the specified number to the standard output stream. + /// Note it may not flush immediately. + /// + /// The foreground color. + /// A number to output. + public static void Write(ConsoleColor foreground, long number) + => StyleConsole.Default.Write(foreground, number); + + /// + /// Writes the specified number to the standard output stream. + /// Note it may not flush immediately. + /// + /// The foreground color. + /// The background color. + /// A number to output. + public static void Write(ConsoleColor? foreground, ConsoleColor? background, long number) + => StyleConsole.Default.Write(foreground, background, number); + + /// + /// Writes the specified number to the standard output stream. + /// Note it may not flush immediately. + /// + /// A number to output. + public static void Write(ulong number) + => StyleConsole.Default.Write(number); + + /// + /// Writes the specified number to the standard output stream. + /// Note it may not flush immediately. + /// + /// The content style. + /// A number to output. + public static void Write(ConsoleTextStyle style, ulong number) + => StyleConsole.Default.Write(style, number); + + /// + /// Writes the specified number to the standard output stream. + /// Note it may not flush immediately. + /// + /// The foreground color. + /// A number to output. + public static void Write(ConsoleColor foreground, ulong number) + => StyleConsole.Default.Write(foreground, number); + + /// + /// Writes the specified number to the standard output stream. + /// Note it may not flush immediately. + /// + /// The foreground color. + /// The background color. + /// A number to output. + public static void Write(ConsoleColor? foreground, ConsoleColor? background, ulong number) + => StyleConsole.Default.Write(foreground, background, number); + + /// + /// Writes the specified number to the standard output stream. + /// Note it may not flush immediately. + /// + /// A number to output. + public static void Write(float number) + => StyleConsole.Default.Write(number); + + /// + /// Writes the specified number to the standard output stream. + /// Note it may not flush immediately. + /// + /// The content style. + /// A number to output. + public static void Write(ConsoleTextStyle style, float number) + => StyleConsole.Default.Write(style, number); + + /// + /// Writes the specified number to the standard output stream. + /// Note it may not flush immediately. + /// + /// The foreground color. + /// A number to output. + public static void Write(ConsoleColor foreground, float number) + => StyleConsole.Default.Write(foreground, number); + + /// + /// Writes the specified number to the standard output stream. + /// Note it may not flush immediately. + /// + /// The foreground color. + /// The background color. + /// A number to output. + public static void Write(ConsoleColor? foreground, ConsoleColor? background, float number) + => StyleConsole.Default.Write(foreground, background, number); + + /// + /// Writes the specified number to the standard output stream. + /// Note it may not flush immediately. + /// + /// A number to output. + public static void Write(decimal number) + => StyleConsole.Default.Write(number); + + /// + /// Writes the specified number to the standard output stream. + /// Note it may not flush immediately. + /// + /// The content style. + /// A number to output. + public static void Write(ConsoleTextStyle style, decimal number) + => StyleConsole.Default.Write(style, number); + + /// + /// Writes the specified number to the standard output stream. + /// Note it may not flush immediately. + /// + /// The foreground color. + /// A number to output. + public static void Write(ConsoleColor foreground, decimal number) + => StyleConsole.Default.Write(foreground, number); + + /// + /// Writes the specified number to the standard output stream. + /// Note it may not flush immediately. + /// + /// The foreground color. + /// The background color. + /// A number to output. + public static void Write(ConsoleColor? foreground, ConsoleColor? background, decimal number) + => StyleConsole.Default.Write(foreground, background, number); + + /// + /// Writes the specified number to the standard output stream. + /// Note it may not flush immediately. + /// + /// A number to output. + public static void Write(double number) + => StyleConsole.Default.Write(number); + + /// + /// Writes the specified number to the standard output stream. + /// Note it may not flush immediately. + /// + /// A number to output. + /// A standard or custom numeric format string. + /// format is invalid or not supported. + public static void Write(double number, string format) + => StyleConsole.Default.Write(number, format); + + /// + /// Writes the specified number to the standard output stream. + /// Note it may not flush immediately. + /// + /// The content style. + /// A number to output. + public static void Write(ConsoleTextStyle style, double number) + => StyleConsole.Default.Write(style, number); + + /// + /// Writes the specified number to the standard output stream. + /// Note it may not flush immediately. + /// + /// The content style. + /// A number to output. + /// A standard or custom numeric format string. + /// format is invalid or not supported. + public static void Write(ConsoleTextStyle style, double number, string format) + => StyleConsole.Default.Write(style, number, format); + + /// + /// Writes the specified number to the standard output stream. + /// Note it may not flush immediately. + /// + /// The foreground color. + /// A number to output. + public static void Write(ConsoleColor foreground, double number) + => StyleConsole.Default.Write(foreground, number); + + /// + /// Writes the specified number to the standard output stream. + /// Note it may not flush immediately. + /// + /// The foreground color. + /// A number to output. + /// A standard or custom numeric format string. + /// format is invalid or not supported. + public static void Write(ConsoleColor foreground, double number, string format) + => StyleConsole.Default.Write(foreground, number, format); + + /// + /// Writes the specified number to the standard output stream. + /// Note it may not flush immediately. + /// + /// The foreground color. + /// The background color. + /// A number to output. + public static void Write(ConsoleColor? foreground, ConsoleColor? background, double number) + => StyleConsole.Default.Write(foreground, background, number); + + /// + /// Writes the specified number to the standard output stream. + /// Note it may not flush immediately. + /// + /// The foreground color. + /// The background color. + /// A number to output. + /// A standard or custom numeric format string. + /// format is invalid or not supported. + public static void Write(ConsoleColor? foreground, ConsoleColor? background, double number, string format) + => StyleConsole.Default.Write(foreground, background, number, format); + + /// + /// Writes the specified characters to the standard output stream. + /// Note it may not flush immediately. + /// + /// The value to write. + /// The starting position in value. + /// The number of characters to write. + public static void Write(char[] value, int start = 0, int? count = null) + => StyleConsole.Default.Write(value, start, count); + + /// + /// Writes the specified characters to the standard output stream. + /// Note it may not flush immediately. + /// + /// The content style. + /// The value to write. + /// The starting position in value. + /// The number of characters to write. + public static void Write(ConsoleTextStyle style, char[] value, int start = 0, int? count = null) + => StyleConsole.Default.Write(style, value, start, count); + + /// + /// Writes the specified characters to the standard output stream. + /// Note it may not flush immediately. + /// + /// The foreground color. + /// The value to write. + /// The starting position in value. + /// The number of characters to write. + public static void Write(ConsoleColor foreground, char[] value, int start = 0, int? count = null) + => StyleConsole.Default.Write(foreground, value, start, count); + + /// + /// Writes the specified characters to the standard output stream. + /// Note it may not flush immediately. + /// + /// The foreground color. + /// The background color. + /// The value to write. + /// The starting position in value. + /// The number of characters to write. + public static void Write(ConsoleColor? foreground, ConsoleColor? background, char[] value, int start = 0, int? count = null) + => StyleConsole.Default.Write(foreground, background, value, start, count); + + /// + /// Writes the specified characters to the standard output stream. + /// Note it may not flush immediately. + /// + /// The content style. + /// The value to write. + /// The starting position in value. + /// The number of characters to write. + public static void Write(IConsoleTextPrettier style, char[] value, int start = 0, int? count = null) + => StyleConsole.Default.Write(style, value, start, count); + + /// + /// Writes the specified characters to the standard output stream. + /// Note it may not flush immediately. + /// + /// The value to write. + /// The number of times to append value. + public static void Write(char value, int repeatCount = 1) + => StyleConsole.Default.Write(value, repeatCount); + + /// + /// Writes the specified characters to the standard output stream. + /// Note it may not flush immediately. + /// + /// The content style. + /// The value to write. + /// The number of times to append value. + public static void Write(ConsoleTextStyle style, char value, int repeatCount = 1) + => StyleConsole.Default.Write(style, value, repeatCount); + + /// + /// Writes the specified characters to the standard output stream. + /// Note it may not flush immediately. + /// + /// The foreground color. + /// The value to write. + /// The number of times to append value. + public static void Write(ConsoleColor foreground, char value, int repeatCount = 1) + => StyleConsole.Default.Write(foreground, value, repeatCount); + + /// + /// Writes the specified string value to the standard output stream. + /// Note it may not flush immediately. + /// + /// The style. + /// The value to write. + /// The number of times to append value. + public static void Write(IConsoleTextPrettier style, char value, int repeatCount = 1) + => StyleConsole.Default.Write(style, value, repeatCount); + + /// + /// Writes the specified data to the standard output stream. + /// Note it may not flush immediately. + /// + /// A representation model. + public static void Write(IConsoleTextCreator model) + => StyleConsole.Default.Write(model); + + /// + /// Writes the specified data to the standard output stream. + /// Note it may not flush immediately. + /// + /// The type of data model. + /// The style. + /// A data model. + public static void Write(IConsoleTextCreator style, T data) + => StyleConsole.Default.Write(style, data); + + /// + /// Writes the specified data to the standard output stream. + /// Note it may not flush immediately. + /// + /// The type of data model. + /// The additional options. + /// The style. + /// A data model. + /// The additional options. + public static void Write(IConsoleTextCreator style, TData data, TOptions options) + => StyleConsole.Default.Write(style, data, options); + + /// + /// Writes the specified characters to the standard output stream. + /// Note it may not flush immediately. + /// + /// The foreground color. + /// The background color. + /// The value to write. + /// The number of times to append value. + public static void Write(ConsoleColor? foreground, ConsoleColor? background, char value, int repeatCount = 1) + => StyleConsole.Default.Write(foreground, background, value, repeatCount); + + /// + /// Writes the specified string value, followed by the current line terminator, to the standard output stream. + /// It will flush immediately. + /// + /// The text content. + public static void WriteLine(ConsoleText content = null) + => StyleConsole.Default.WriteLine(content); + + /// + /// Writes the specified string value, followed by the current line terminator, to the standard output stream. + /// It will flush immediately. + /// + /// The text content 1. + /// The text content 2. + /// The additional text content collection. + public static void WriteLine(ConsoleText content1, ConsoleText content2, params ConsoleText[] additionalContext) + => StyleConsole.Default.WriteLine(content1, content2, additionalContext); + + /// + /// Writes the specified string value, followed by the current line terminator, to the standard output stream. + /// It will flush immediately. + /// + /// The text content collection. + public static void WriteLine(IEnumerable content) + => StyleConsole.Default.WriteLine(content); + + /// + /// Writes the specified string value, followed by the current line terminator, to the standard output stream. + /// It will flush immediately. + /// + /// A composite format string to output. + /// An object array that contains zero or more objects to format. + /// format is invalid. -or- The index of a format item is less than zero, or greater than or equal to the length of the args array. + public static void WriteLine(string s, params object[] args) + => StyleConsole.Default.WriteLine(s, args); + + /// + /// Writes the specified string value, followed by the current line terminator, to the standard output stream. + /// + /// The content style. + /// A composite format string to output. + /// An object array that contains zero or more objects to format. + /// format is invalid. -or- The index of a format item is less than zero, or greater than or equal to the length of the args array. + public static void WriteLine(ConsoleTextStyle style, string s, params object[] args) + => StyleConsole.Default.WriteLine(style, s, args); + + /// + /// Writes the specified string value, followed by the current line terminator, to the standard output stream. + /// It will flush immediately. + /// + /// The foreground color. + /// A composite format string to output. + /// An object array that contains zero or more objects to format. + /// format is invalid. -or- The index of a format item is less than zero, or greater than or equal to the length of the args array. + public static void WriteLine(ConsoleColor foreground, string s, params object[] args) + => StyleConsole.Default.WriteLine(foreground, s, args); + + /// + /// Writes the specified string value, followed by the current line terminator, to the standard output stream. + /// It will flush immediately. + /// + /// The foreground color. + /// The background color. + /// A composite format string to output. + /// An object array that contains zero or more objects to format. + /// format is invalid. -or- The index of a format item is less than zero, or greater than or equal to the length of the args array. + public static void WriteLine(ConsoleColor? foreground, ConsoleColor? background, string s, params object[] args) + => StyleConsole.Default.WriteLine(foreground, background, s, args); + + /// + /// Writes the specified string value, followed by the current line terminator, to the standard output stream. + /// It will flush immediately. + /// + /// The foreground color. + /// A composite format string to output. + /// An object array that contains zero or more objects to format. + /// format is invalid. -or- The index of a format item is less than zero, or greater than or equal to the length of the args array. + public static void WriteLine(Color foreground, string s, params object[] args) + => StyleConsole.Default.WriteLine(foreground, s, args); + + /// + /// Writes the specified string value, followed by the current line terminator, to the standard output stream. + /// It will flush immediately. + /// + /// The foreground color. + /// The background color. + /// A composite format string to output. + /// An object array that contains zero or more objects to format. + /// format is invalid. -or- The index of a format item is less than zero, or greater than or equal to the length of the args array. + public static void WriteLine(Color foreground, Color background, string s, params object[] args) + => StyleConsole.Default.WriteLine(foreground, background, s, args); + + /// + /// Writes a JSON object, followed by the current line terminator, to the standard output stream. + /// It will flush immediately. + /// + /// The style. + /// A composite format string to output. + /// An object array that contains zero or more objects to format. + /// format is invalid. -or- The index of a format item is less than zero, or greater than or equal to the length of the args array. + public static void WriteLine(IConsoleTextPrettier style, string s, params object[] args) + => StyleConsole.Default.WriteLine(style, s, args); + + /// + /// Writes the specified string value, followed by the current line terminator, to the standard output stream. + /// It will flush immediately. + /// + /// A composite format string to output. + public static void WriteLine(StringBuilder s) + => StyleConsole.Default.WriteLine(s); + + /// + /// Writes the specified string value, followed by the current line terminator, to the standard output stream. + /// + /// The content style. + /// A composite format string to output. + public static void WriteLine(ConsoleTextStyle style, StringBuilder s) + => StyleConsole.Default.WriteLine(style, s); + + /// + /// Writes the specified string value, followed by the current line terminator, to the standard output stream. + /// It will flush immediately. + /// + /// The foreground color. + /// A composite format string to output. + public static void WriteLine(ConsoleColor foreground, StringBuilder s) + => StyleConsole.Default.WriteLine(foreground, s); + + /// + /// Writes the specified string value, followed by the current line terminator, to the standard output stream. + /// It will flush immediately. + /// + /// The foreground color. + /// The background color. + /// A composite format string to output. + public static void WriteLine(ConsoleColor? foreground, ConsoleColor? background, StringBuilder s) + => StyleConsole.Default.WriteLine(foreground, background, s); + + /// + /// Writes the specified string value, followed by the current line terminator, to the standard output stream. + /// It will flush immediately. + /// + /// The foreground color. + /// A composite format string to output. + public static void WriteLine(Color foreground, StringBuilder s) + => StyleConsole.Default.WriteLine(foreground, s); + + /// + /// Writes the specified string value, followed by the current line terminator, to the standard output stream. + /// It will flush immediately. + /// + /// The foreground color. + /// The background color. + /// A composite format string to output. + public static void WriteLine(Color foreground, Color background, StringBuilder s) + => StyleConsole.Default.WriteLine(foreground, background, s); + + /// + /// Writes a JSON object, followed by the current line terminator, to the standard output stream. + /// It will flush immediately. + /// + /// The style. + /// A composite format string to output. + public static void WriteLine(IConsoleTextPrettier style, StringBuilder s) + => StyleConsole.Default.WriteLine(style, s?.ToString()); + + /// + /// Writes the specified string value, followed by the current line terminator, to the standard output stream. + /// It will flush immediately. + /// + /// A composite format string to output. + public static void WriteLine(SecureString s) + => StyleConsole.Default.WriteLine(s?.ToUnsecureString()); + + /// + /// Writes the specified string value, followed by the current line terminator, to the standard output stream. + /// + /// The content style. + /// A composite format string to output. + public static void WriteLine(ConsoleTextStyle style, SecureString s) + => StyleConsole.Default.WriteLine(style, s?.ToUnsecureString()); + + /// + /// Writes the specified string value, followed by the current line terminator, to the standard output stream. + /// It will flush immediately. + /// + /// The foreground color. + /// A composite format string to output. + public static void WriteLine(ConsoleColor foreground, SecureString s) + => StyleConsole.Default.WriteLine(foreground, s?.ToUnsecureString()); + + /// + /// Writes the specified string value, followed by the current line terminator, to the standard output stream. + /// It will flush immediately. + /// + /// The foreground color. + /// The background color. + /// A composite format string to output. + public static void WriteLine(ConsoleColor? foreground, ConsoleColor? background, SecureString s) + => StyleConsole.Default.WriteLine(foreground, background, s.ToUnsecureString()); + + /// + /// Writes the specified number, followed by the current line terminator, to the standard output stream. + /// It will flush immediately. + /// + /// A number to output. + public static void WriteLine(int number) + => StyleConsole.Default.WriteLine(number); + + /// + /// Writes the specified number, followed by the current line terminator, to the standard output stream. + /// It will flush immediately. + /// + /// A number to output. + /// A standard or custom numeric format string. + /// format is invalid or not supported. + public static void WriteLine(int number, string format) + => StyleConsole.Default.WriteLine(number, format); + + /// + /// Writes the specified number, followed by the current line terminator, to the standard output stream. + /// It will flush immediately. + /// + /// The content style. + /// A number to output. + public static void WriteLine(ConsoleTextStyle style, int number) + => StyleConsole.Default.WriteLine(style, number); + + /// + /// Writes the specified number, followed by the current line terminator, to the standard output stream. + /// It will flush immediately. + /// + /// The content style. + /// A number to output. + /// A standard or custom numeric format string. + /// format is invalid or not supported. + public static void WriteLine(ConsoleTextStyle style, int number, string format) + => StyleConsole.Default.WriteLine(style, number, format); + + /// + /// Writes the specified number, followed by the current line terminator, to the standard output stream. + /// It will flush immediately. + /// + /// The foreground color. + /// A number to output. + public static void WriteLine(ConsoleColor foreground, int number) + => StyleConsole.Default.WriteLine(foreground, number); + + /// + /// Writes the specified number, followed by the current line terminator, to the standard output stream. + /// It will flush immediately. + /// + /// The foreground color. + /// A number to output. + /// A standard or custom numeric format string. + /// format is invalid or not supported. + public static void WriteLine(ConsoleColor foreground, int number, string format) + => StyleConsole.Default.WriteLine(foreground, number, format); + + /// + /// Writes the specified number, followed by the current line terminator, to the standard output stream. + /// It will flush immediately. + /// + /// The foreground color. + /// The background color. + /// A number to output. + public static void WriteLine(ConsoleColor? foreground, ConsoleColor? background, int number) + => StyleConsole.Default.WriteLine(foreground, background, number); + + /// + /// Writes the specified number, followed by the current line terminator, to the standard output stream. + /// It will flush immediately. + /// + /// The foreground color. + /// The background color. + /// A number to output. + /// A standard or custom numeric format string. + /// format is invalid or not supported. + public static void WriteLine(ConsoleColor? foreground, ConsoleColor? background, int number, string format) + => StyleConsole.Default.WriteLine(foreground, background, number, format); + + /// + /// Writes the specified number, followed by the current line terminator, to the standard output stream. + /// It will flush immediately. + /// + /// A number to output. + public static void WriteLine(long number) + => StyleConsole.Default.WriteLine(number); + + /// + /// Writes the specified number, followed by the current line terminator, to the standard output stream. + /// It will flush immediately. + /// + /// The content style. + /// A number to output. + public static void WriteLine(ConsoleTextStyle style, long number) + => StyleConsole.Default.WriteLine(style, number); + + /// + /// Writes the specified number, followed by the current line terminator, to the standard output stream. + /// It will flush immediately. + /// + /// The foreground color. + /// A number to output. + public static void WriteLine(ConsoleColor foreground, long number) + => StyleConsole.Default.WriteLine(foreground, number); + + /// + /// Writes the specified number, followed by the current line terminator, to the standard output stream. + /// It will flush immediately. + /// + /// The foreground color. + /// The background color. + /// A number to output. + public static void WriteLine(ConsoleColor? foreground, ConsoleColor? background, long number) + => StyleConsole.Default.WriteLine(foreground, background, number); + + /// + /// Writes the specified number, followed by the current line terminator, to the standard output stream. + /// It will flush immediately. + /// + /// A number to output. + public static void WriteLine(ulong number) + => StyleConsole.Default.WriteLine(number); + + /// + /// Writes the specified number, followed by the current line terminator, to the standard output stream. + /// It will flush immediately. + /// + /// The content style. + /// A number to output. + public static void WriteLine(ConsoleTextStyle style, ulong number) + => StyleConsole.Default.WriteLine(style, number); + + /// + /// Writes the specified number, followed by the current line terminator, to the standard output stream. + /// It will flush immediately. + /// + /// The foreground color. + /// A number to output. + public static void WriteLine(ConsoleColor foreground, ulong number) + => StyleConsole.Default.WriteLine(foreground, number); + + /// + /// Writes the specified number, followed by the current line terminator, to the standard output stream. + /// It will flush immediately. + /// + /// The foreground color. + /// The background color. + /// A number to output. + public static void WriteLine(ConsoleColor? foreground, ConsoleColor? background, ulong number) + => StyleConsole.Default.WriteLine(foreground, background, number); + + /// + /// Writes the specified number, followed by the current line terminator, to the standard output stream. + /// It will flush immediately. + /// + /// A number to output. + public static void WriteLine(float number) + => StyleConsole.Default.WriteLine(number); + + /// + /// Writes the specified number, followed by the current line terminator, to the standard output stream. + /// It will flush immediately. + /// + /// The content style. + /// A number to output. + public static void WriteLine(ConsoleTextStyle style, float number) + => StyleConsole.Default.WriteLine(style, number); + + /// + /// Writes the specified number, followed by the current line terminator, to the standard output stream. + /// It will flush immediately. + /// + /// The foreground color. + /// A number to output. + public static void WriteLine(ConsoleColor foreground, float number) + => StyleConsole.Default.WriteLine(foreground, number); + + /// + /// Writes the specified number, followed by the current line terminator, to the standard output stream. + /// It will flush immediately. + /// + /// The foreground color. + /// The background color. + /// A number to output. + public static void WriteLine(ConsoleColor? foreground, ConsoleColor? background, float number) + => StyleConsole.Default.WriteLine(foreground, background, number); + + /// + /// Writes the specified number, followed by the current line terminator, to the standard output stream. + /// It will flush immediately. + /// + /// A number to output. + public static void WriteLine(decimal number) + => StyleConsole.Default.WriteLine(number); + + /// + /// Writes the specified number, followed by the current line terminator, to the standard output stream. + /// It will flush immediately. + /// + /// The content style. + /// A number to output. + public static void WriteLine(ConsoleTextStyle style, decimal number) + => StyleConsole.Default.WriteLine(style, number); + + /// + /// Writes the specified number, followed by the current line terminator, to the standard output stream. + /// It will flush immediately. + /// + /// The foreground color. + /// A number to output. + public static void WriteLine(ConsoleColor foreground, decimal number) + => StyleConsole.Default.WriteLine(foreground, number); + + /// + /// Writes the specified number, followed by the current line terminator, to the standard output stream. + /// It will flush immediately. + /// + /// The foreground color. + /// The background color. + /// A number to output. + public static void WriteLine(ConsoleColor? foreground, ConsoleColor? background, decimal number) + => StyleConsole.Default.WriteLine(foreground, background, number); + + /// + /// Writes the specified number, followed by the current line terminator, to the standard output stream. + /// It will flush immediately. + /// + /// A number to output. + public static void WriteLine(double number) + => StyleConsole.Default.WriteLine(number); + + /// + /// Writes the specified number, followed by the current line terminator, to the standard output stream. + /// It will flush immediately. + /// + /// A number to output. + /// A standard or custom numeric format string. + /// format is invalid or not supported. + public static void WriteLine(double number, string format) + => StyleConsole.Default.WriteLine(number, format); + + /// + /// Writes the specified number, followed by the current line terminator, to the standard output stream. + /// It will flush immediately. + /// + /// The content style. + /// A number to output. + public static void WriteLine(ConsoleTextStyle style, double number) + => StyleConsole.Default.WriteLine(style, number); + + /// + /// Writes the specified number, followed by the current line terminator, to the standard output stream. + /// It will flush immediately. + /// + /// The content style. + /// A number to output. + /// A standard or custom numeric format string. + /// format is invalid or not supported. + public static void WriteLine(ConsoleTextStyle style, double number, string format) + => StyleConsole.Default.WriteLine(style, number, format); + + /// + /// Writes the specified number, followed by the current line terminator, to the standard output stream. + /// It will flush immediately. + /// + /// The foreground color. + /// A number to output. + public static void WriteLine(ConsoleColor foreground, double number) + => StyleConsole.Default.WriteLine(foreground, number); + + /// + /// Writes the specified number, followed by the current line terminator, to the standard output stream. + /// It will flush immediately. + /// + /// The foreground color. + /// A number to output. + /// A standard or custom numeric format string. + /// format is invalid or not supported. + public static void WriteLine(ConsoleColor foreground, double number, string format) + => StyleConsole.Default.WriteLine(foreground, number, format); + + /// + /// Writes the specified number, followed by the current line terminator, to the standard output stream. + /// It will flush immediately. + /// + /// The foreground color. + /// The background color. + /// A number to output. + public static void WriteLine(ConsoleColor? foreground, ConsoleColor? background, double number) + => StyleConsole.Default.WriteLine(foreground, background, number); + + /// + /// Writes the specified number, followed by the current line terminator, to the standard output stream. + /// It will flush immediately. + /// + /// The foreground color. + /// The background color. + /// A number to output. + /// A standard or custom numeric format string. + /// format is invalid or not supported. + public static void WriteLine(ConsoleColor? foreground, ConsoleColor? background, double number, string format) + => StyleConsole.Default.WriteLine(foreground, background, number, format); + + /// + /// Writes the specified characters, followed by the current line terminator, to the standard output stream. + /// It will flush immediately. + /// + /// The value to write. + /// The starting position in value. + /// The number of characters to write. + public static void WriteLine(char[] value, int start = 0, int? count = null) + => StyleConsole.Default.WriteLine(value, start, count); + + /// + /// Writes the specified characters, followed by the current line terminator, to the standard output stream. + /// It will flush immediately. + /// + /// The content style. + /// The value to write. + /// The starting position in value. + /// The number of characters to write. + public static void WriteLine(ConsoleTextStyle style, char[] value, int start = 0, int? count = null) + => StyleConsole.Default.WriteLine(style, value, start, count); + + /// + /// Writes the specified characters, followed by the current line terminator, to the standard output stream. + /// It will flush immediately. + /// + /// The foreground color. + /// The value to write. + /// The starting position in value. + /// The number of characters to write. + public static void WriteLine(ConsoleColor foreground, char[] value, int start = 0, int? count = null) + => StyleConsole.Default.WriteLine(foreground, value, start, count); + + /// + /// Writes the specified characters, followed by the current line terminator, to the standard output stream. + /// It will flush immediately. + /// + /// The foreground color. + /// The background color. + /// The value to write. + /// The starting position in value. + /// The number of characters to write. + public static void WriteLine(ConsoleColor? foreground, ConsoleColor? background, char[] value, int start = 0, int? count = null) + => StyleConsole.Default.WriteLine(foreground, background, value, start, count); + + /// + /// Writes the specified characters, followed by the current line terminator, to the standard output stream. + /// It will flush immediately. + /// + /// The content style. + /// The value to write. + /// The starting position in value. + /// The number of characters to write. + public static void WriteLine(IConsoleTextPrettier style, char[] value, int start = 0, int? count = null) + => StyleConsole.Default.WriteLine(style, value, start, count); + + /// + /// Writes the specified characters, followed by the current line terminator, to the standard output stream. + /// It will flush immediately. + /// + /// The value to write. + /// The number of times to append value. + public static void WriteLine(char value, int repeatCount = 1) + => StyleConsole.Default.WriteLine(value, repeatCount); + + /// + /// Writes the specified characters, followed by the current line terminator, to the standard output stream. + /// It will flush immediately. + /// + /// The content style. + /// The value to write. + /// The number of times to append value. + public static void WriteLine(ConsoleTextStyle style, char value, int repeatCount = 1) + => StyleConsole.Default.WriteLine(style, value, repeatCount); + + /// + /// Writes the specified characters, followed by the current line terminator, to the standard output stream. + /// It will flush immediately. + /// + /// The foreground color. + /// The value to write. + /// The number of times to append value. + public static void WriteLine(ConsoleColor foreground, char value, int repeatCount = 1) + => StyleConsole.Default.WriteLine(foreground, value, repeatCount); + + /// + /// Writes a JSON object, followed by the current line terminator, to the standard output stream. + /// It will flush immediately. + /// + /// The style. + /// The value to write. + /// The number of times to append value. + public static void WriteLine(IConsoleTextPrettier style, char value, int repeatCount = 1) + => StyleConsole.Default.WriteLine(style, value, repeatCount); + + /// + /// Writes the specified characters, followed by the current line terminator, to the standard output stream. + /// It will flush immediately. + /// + /// The foreground color. + /// The background color. + /// The value to write. + /// The number of times to append value. + public static void WriteLine(ConsoleColor? foreground, ConsoleColor? background, char value, int repeatCount = 1) + => StyleConsole.Default.WriteLine(foreground, background, value, repeatCount); + + /// + /// Writes an exception, followed by the current line terminator, to the standard output stream. + /// It will flush immediately. + /// + /// The exception. + /// true if output stack trace; otherwise, false. + public static void WriteLine(Exception ex, bool stackTrace = false) + => StyleConsole.Default.WriteLine(new ConsoleTextStyle(ConsoleColor.Red), null as ConsoleTextStyle, ex, stackTrace); + + /// + /// Writes an exception, followed by the current line terminator, to the standard output stream. + /// It will flush immediately. + /// + /// The exception. + /// The style of header. + /// The style of details. + /// true if output stack trace; otherwise, false. + public static void WriteLine(ConsoleTextStyle captionStyle, ConsoleTextStyle messageStyle, Exception ex, bool stackTrace = false) + => StyleConsole.Default.WriteLine(captionStyle, messageStyle, ex, stackTrace); + + /// + /// Writes an exception, followed by the current line terminator, to the standard output stream. + /// It will flush immediately. + /// + /// The error information. + public static void WriteLine(Data.ErrorMessageResult ex) + => StyleConsole.Default.WriteLine(new ConsoleTextStyle(ConsoleColor.Red), null as ConsoleTextStyle, ex); + + /// + /// Writes an exception, followed by the current line terminator, to the standard output stream. + /// It will flush immediately. + /// + /// The error information. + /// The style of header. + /// The style of details. + public static void WriteLine(ConsoleTextStyle captionStyle, ConsoleTextStyle messageStyle, Data.ErrorMessageResult ex) + => StyleConsole.Default.WriteLine(captionStyle, messageStyle, ex); + + /// + /// Writes a JSON object, followed by the current line terminator, to the standard output stream. + /// It will flush immediately. + /// + /// The JSON instance. + public static void WriteLine(IJsonDataNode json) + => StyleConsole.Default.WriteLine(new JsonConsoleStyle().CreateTextCollection(json, 0)); + + /// + /// Writes a JSON object, followed by the current line terminator, to the standard output stream. + /// It will flush immediately. + /// + /// The style. + /// The JSON instance. + public static void WriteLine(JsonConsoleStyle style, IJsonDataNode json) + => StyleConsole.Default.WriteLine((style ?? new JsonConsoleStyle()).CreateTextCollection(json, 0)); + + /// + /// Writes a JSON object, followed by the current line terminator, to the standard output stream. + /// It will flush immediately. + /// + /// The JSON instance. + public static void WriteLine(System.Text.Json.Nodes.JsonObject json) + => StyleConsole.Default.WriteLine(new JsonConsoleStyle().CreateTextCollection(json == null ? null : (JsonObjectNode)json, 0)); + + /// + /// Writes a JSON object, followed by the current line terminator, to the standard output stream. + /// It will flush immediately. + /// + /// The style. + /// The JSON instance. + public static void WriteLine(JsonConsoleStyle style, System.Text.Json.Nodes.JsonObject json) + => StyleConsole.Default.WriteLine((style ?? new JsonConsoleStyle()).CreateTextCollection(json == null ? null : (JsonObjectNode)json, 0)); + + /// + /// Writes a JSON object, followed by the current line terminator, to the standard output stream. + /// It will flush immediately. + /// + /// The JSON instance. + public static void WriteLine(IJsonObjectHost json) + => StyleConsole.Default.WriteLine(new JsonConsoleStyle().CreateTextCollection(json?.ToJson(), 0)); + + /// + /// Writes a JSON object, followed by the current line terminator, to the standard output stream. + /// It will flush immediately. + /// + /// The style. + /// The JSON instance. + public static void WriteLine(JsonConsoleStyle style, IJsonObjectHost json) + => StyleConsole.Default.WriteLine((style ?? new JsonConsoleStyle()).CreateTextCollection(json?.ToJson(), 0)); + + /// + /// Writes a JSON object, followed by the current line terminator, to the standard output stream. + /// It will flush immediately. + /// + /// The JSON instance. + public static void WriteLine(System.Text.Json.Nodes.JsonArray json) + => StyleConsole.Default.WriteLine(new JsonConsoleStyle().CreateTextCollection(json == null ? null : (JsonArrayNode)json, 0)); + + /// + /// Writes a JSON object, followed by the current line terminator, to the standard output stream. + /// It will flush immediately. + /// + /// The style. + /// The JSON instance. + public static void WriteLine(JsonConsoleStyle style, System.Text.Json.Nodes.JsonArray json) + => StyleConsole.Default.WriteLine((style ?? new JsonConsoleStyle()).CreateTextCollection(json == null ? null : (JsonArrayNode)json, 0)); + + /// + /// Writes the specified data, followed by the current line terminator, to the standard output stream. + /// It will flush immediately. + /// + /// A representation model. + public static void WriteLine(IConsoleTextCreator model) + => StyleConsole.Default.WriteLine(model); + + /// + /// Writes the specified data, followed by the current line terminator, to the standard output stream. + /// It will flush immediately. + /// + /// The type of data model. + /// The style. + /// A data model. + public static void WriteLine(IConsoleTextCreator style, T data) + => StyleConsole.Default.WriteLine(style, data); + + /// + /// Writes the specified data, followed by the current line terminator, to the standard output stream. + /// It will flush immediately. + /// + /// The type of data model. + /// The additional options. + /// The style. + /// A data model. + /// The additional options. + public static void WriteLine(IConsoleTextCreator style, TData data, TOptions options) + => StyleConsole.Default.WriteLine(style, data, options); + + /// + /// Writes a progress component, followed by the current line terminator, to the standard output stream. + /// + /// The options. + /// The progress result. + public static OneProgress WriteLine(ConsoleProgressStyle style) + => StyleConsole.Default.WriteLine(style, null); + + /// + /// Writes a progress component, followed by the current line terminator, to the standard output stream. + /// + /// The caption; or null if no caption. It will be better if it is less than 20 characters. + /// The progress size. + /// The progress kind. + /// The progress result. + public static OneProgress WriteLine(ConsoleProgressStyle.Sizes progressSize, string caption, ConsoleProgressStyle.Kinds kind = ConsoleProgressStyle.Kinds.Full) + => StyleConsole.Default.WriteLine(progressSize, caption, kind); + + /// + /// Writes a progress component, followed by the current line terminator, to the standard output stream. + /// + /// The progress size. + /// The progress kind. + /// The progress result. + public static OneProgress WriteLine(ConsoleProgressStyle.Sizes progressSize, ConsoleProgressStyle.Kinds kind = ConsoleProgressStyle.Kinds.Full) + => StyleConsole.Default.WriteLine(progressSize, kind); + + /// + /// Writes a progress component, followed by the current line terminator, to the standard output stream. + /// + /// The caption; or null if no caption. It will be better if it is less than 20 characters. + /// The style. + /// The progress result. + public static OneProgress WriteLine(ConsoleProgressStyle style, string caption) + => StyleConsole.Default.WriteLine(style, caption); + + /// + /// Writes the specific lines to the standard output stream. + /// + /// The count of line. + public static void WriteLines(int count) + => StyleConsole.Default.WriteLines(count); + + /// + /// Writes the current line terminator for each item, to the standard output stream. + /// + /// The text content collection. + public static void WriteLines(IEnumerable content) + => StyleConsole.Default.WriteLines(content); + + /// + /// Writes the current line terminator for each item, to the standard output stream. + /// + /// The text content. + /// The additional text content collection. + public static void WriteLines(ConsoleText content, params ConsoleText[] additionalContext) + => StyleConsole.Default.WriteLines(content, additionalContext); + + /// + /// Writes the current line terminator for each item, to the standard output stream. + /// + /// The string collection to write. Each one in a line. + public static void WriteLines(IEnumerable col) + => StyleConsole.Default.WriteLines(col); + + /// + /// Writes the current line terminator for each item, to the standard output stream. + /// + /// The content style. + /// The string collection to write. Each one in a line. + public static void WriteLines(ConsoleTextStyle style, IEnumerable col) + => StyleConsole.Default.WriteLines(style, col); + + /// + /// Writes the current line terminator for each item, to the standard output stream. + /// + /// The foreground color of the console. + /// The string collection to write. Each one in a line. + public static void WriteLines(ConsoleColor foreground, IEnumerable col) + => StyleConsole.Default.WriteLines(foreground, col); + + /// + /// Writes the current line terminator for each item, to the standard output stream. + /// + /// The foreground color of the console. + /// The background color. + /// The string collection to write. Each one in a line. + public static void WriteLines(ConsoleColor foreground, ConsoleColor background, IEnumerable col) + => StyleConsole.Default.WriteLines(foreground, background, col); + + /// + /// Writes the current line terminator for each item, to the standard output stream. + /// + /// The string collection to write. Each one in a line. + /// A transform function to apply to each source element; the second parameter of the function represents the index of the source element. + public static void WriteLines(IEnumerable col, Func selector) + { + if (col == null) return; + if (selector == null) selector = (ele, i) => ele?.ToString(); + StyleConsole.Default.WriteLines(col.Select(selector)); + } + + /// + /// Writes the current line terminator for each item, to the standard output stream. + /// + /// The foreground color of the console. + /// The string collection to write. Each one in a line. + /// A transform function to apply to each source element; the second parameter of the function represents the index of the source element. + public static void WriteLines(ConsoleColor foreground, IEnumerable col, Func selector) + { + if (col == null) return; + if (selector == null) selector = (ele, i) => ele?.ToString(); + StyleConsole.Default.WriteLines(foreground, col.Select(selector)); + } + + /// + /// Writes the current line terminator for each item, to the standard output stream. + /// + /// The string collection to write. Each one in a line. + /// A transform function to apply to each element. + public static void WriteLines(IEnumerable col, Func selector) + { + if (col == null) return; + if (selector == null) selector = ele => ele?.ToString(); + StyleConsole.Default.WriteLines(col.Select(selector)); + } + + /// + /// Writes the current line terminator for each item, to the standard output stream. + /// + /// The foreground color of the console. + /// The string collection to write. Each one in a line. + /// A transform function to apply to each element. + public static void WriteLines(ConsoleColor foreground, IEnumerable col, Func selector) + { + if (col == null) return; + if (selector == null) selector = ele => ele?.ToString(); + StyleConsole.Default.WriteLines(foreground, col.Select(selector)); + } + + /// + /// Writes a collection of item for selecting. + /// + /// The collection data. + /// The selection display options. + /// The result of selection. + public static SelectionResult Select(SelectionData collection, SelectionConsoleOptions options = null) + => StyleConsole.Default.Select(collection, options); + + /// + /// Writes a collection of item for selecting. + /// + /// The collection data. + /// The converter. + /// The selection display options. + /// The result of selection. + public static SelectionResult Select(IEnumerable collection, Func> convert, SelectionConsoleOptions options = null) + => StyleConsole.Default.Select(collection, convert, options); + + /// + /// Writes a collection of item for selecting. + /// + /// The collection data. + /// The selection display options. + /// The result of selection. + public static SelectionResult Select(IEnumerable> collection, SelectionConsoleOptions options = null) + => StyleConsole.Default.Select(collection, options); + + /// + /// Writes a collection of item for selecting. + /// + /// The parent foler path. + /// The selection display options. + /// The search string to match against the names of directories and files. This parameter can contain a combination of valid literal path and wildcard (* and ?) characters, but it doesn't support regular expressions. + /// The result of selection. + /// searchPattern contains one or more invalid characters defined by the System.IO.Path.GetInvalidPathChars method. + /// The specified path is invalid (for example, it is on an unmapped drive). + /// The caller does not have the required permission. + public static SelectionResult Select(DirectoryInfo path, SelectionConsoleOptions options = null, string searchPattern = null) + => StyleConsole.Default.Select(path, options, searchPattern); + + /// + /// Writes a collection of item for selecting. + /// + /// The parent foler path. + /// A function to test each element for a condition. + /// The selection display options. + /// The result of selection. + /// The specified path is invalid (for example, it is on an unmapped drive). + /// The caller does not have the required permission. + public static SelectionResult Select(DirectoryInfo path, Func predicate, SelectionConsoleOptions options = null) + => StyleConsole.Default.Select(path, predicate, options); + + /// + /// Writes a collection of item for selecting. + /// + /// The type of data. + /// The collection data. + /// The selection display options. + /// The result of selection. + public static SelectionResult Select(SelectionData collection, SelectionConsoleOptions options = null) + => StyleConsole.Default.Select(collection, options); + + /// + /// Writes a collection of item for selecting. + /// + /// The collection data. + /// The selection display options. + /// The result of selection. + public static SelectionResult Select(IEnumerable collection, SelectionConsoleOptions options = null) + => StyleConsole.Default.Select(collection, options); + + /// + /// Flushes all data. + /// + public static void Flush() + => StyleConsole.Default.Flush(); + + /// + /// Clears output cache. + /// + public static void ClearOutputCache() + => StyleConsole.Default.ClearOutputCache(); + + /// + /// Enters a backspace to console to remove the last charactor. + /// + /// The count of the charactor to remove from end. + /// true if just only move cursor back and keep output; otherwise, false. + public static void Backspace(int count = 1, bool doNotRemoveOutput = false) + => StyleConsole.Default.Backspace(count, doNotRemoveOutput); + + /// + /// Enters backspaces to console to remove the charactors to the beginning of the line. + /// + public static void BackspaceToBeginning() + => StyleConsole.Default.BackspaceToBeginning(); + + /// + /// Reads the next line of characters from the standard input stream. + /// + /// The next line of characters from the input stream, or null if no more lines are available. + /// An I/O error occurred. + /// There is insufficient memory to allocate a buffer for the returned string. + /// The number of characters in the next line of characters is greater than max value of 32-bit integer. + public static string ReadLine() + => StyleConsole.Default.ReadLine(); + + /// + /// Obtains the next character or function key pressed by the user. The pressed key is optionally displayed in the console window. + /// + /// Determines whether to display the pressed key in the console window. true to not display the pressed key; otherwise, false. + /// The next line of characters from the input stream, or null if no more lines are available. + /// An I/O error occurred. + /// The input stream is redirected from the one other than the console. + public static ConsoleKeyInfo ReadKey(bool intercept = false) + => StyleConsole.Default.ReadKey(intercept); + + /// + /// Obtains the password pressed by the user. + /// + /// + /// The password. + /// + public static SecureString ReadPassword() + => StyleConsole.Default.ReadPassword(null, null); + + /// + /// Obtains the password pressed by the user. + /// + /// The optional charactor to output to replace the original one, such as *. + /// true if do not follow the line terminator after typing the password; otherwise, false. + /// + /// The password. + /// + public static SecureString ReadPassword(char replaceChar, bool inline = false) + => StyleConsole.Default.ReadPassword(null, replaceChar, inline); + + /// + /// Obtains the password pressed by the user. + /// + /// The replace charactor color. + /// The optional charactor to output to replace the original one, such as *. + /// true if do not follow the line terminator after typing the password; otherwise, false. + /// + /// The password. + /// + public static SecureString ReadPassword(ConsoleColor? foreground, char? replaceChar, bool inline = false) + => StyleConsole.Default.ReadPassword(foreground, replaceChar, inline); + + /// + /// Moves cursor by a specific relative position. + /// + /// The horizontal translation size. + /// The vertical translation size. + public static void MoveCursorBy(int x, int y = 0) + => StyleConsole.Default.MoveCursorBy(x, y); + + /// + /// Moves cursor at a specific position in buffer. + /// + /// Column, the left from the edge of buffer. + /// Row, the top from the edge of buffer. + public static void MoveCursorTo(int x, int y) + => StyleConsole.Default.MoveCursorTo(x, y); + + /// + /// Removes the specific area. + /// + /// The area to remove. + public static void Clear(StyleConsole.RelativeAreas area) + => StyleConsole.Default.Clear(area); +} diff --git a/Console/CommandLine/HighlightConsoleStyle.cs b/Console/CommandLine/HighlightConsoleStyle.cs new file mode 100644 index 0000000..bdb5e36 --- /dev/null +++ b/Console/CommandLine/HighlightConsoleStyle.cs @@ -0,0 +1,114 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Text.Json.Serialization; +using System.Threading.Tasks; + +using Trivial.Collection; +using Trivial.Text; + +namespace Trivial.CommandLine; + +/// +/// The highlight console text style. +/// +public class HighlightConsoleStyle : IConsoleTextPrettier +{ + /// + /// Initialzies a new instance of the HighlightConsoleStyle class. + /// + /// The normal style. + /// The highlight style. + /// The query string. + public HighlightConsoleStyle(ConsoleTextStyle normal, ConsoleTextStyle highlight, IEnumerable q) + { + Normal = normal; + Highlight = highlight; + if (q != null) Query.AddRange(q.Where(ele => !string.IsNullOrEmpty(ele))); + } + + /// + /// Initialzies a new instance of the HighlightConsoleStyle class. + /// + /// The normal style. + /// The highlight style. + /// The query string. + /// One of the enumeration values that specifies the rules for the search. + public HighlightConsoleStyle(ConsoleTextStyle normal, ConsoleTextStyle highlight, string q, StringComparison? comparisonType = null) + { + Normal = normal; + Highlight = highlight; + if (!string.IsNullOrEmpty(q)) Query.Add(q); + if (comparisonType.HasValue) ComparisonType = comparisonType.Value; + } + + /// + /// Gets or sets the fallback foreground color. + /// + [JsonPropertyName("normal")] + public ConsoleTextStyle Normal { get; } = new(); + + /// + /// Gets or sets the fallback background color. + /// + [JsonPropertyName("highlight")] + public ConsoleTextStyle Highlight { get; } = new(); + + /// + /// Gets or sets a value indicating whether the text is strikeout. + /// + [JsonPropertyName("q")] + public List Query { get; } = new(); + + /// + /// Gets or sets one of the enumeration values that specifies the rules for the search. + /// + [JsonPropertyName("compare")] + [JsonConverter(typeof(JsonIntegerEnumCompatibleConverter))] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public StringComparison ComparisonType { get; set; } + + /// + /// The search starting position to search. + /// + [JsonPropertyName("start")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public int StartPosition { get; set; } + +#pragma warning disable IDE0057 + /// + /// Creates the console text collection based on this style. + /// + /// The text. + /// A collection of console text. + IEnumerable IConsoleTextPrettier.CreateTextCollection(string s) + { + var col = new List(); + var q = Query.Where(ele => !string.IsNullOrEmpty(ele)).ToList(); + var pos = StartPosition > 0 ? StartPosition : 0; + while (true) + { + if (pos >= s.Length) break; + var i = -1; + var hl = string.Empty; + foreach (var item in q) + { + var j = s.IndexOf(item, pos, ComparisonType); + if (j < 0 || (i >= 0 && j > i) || (i == j && item.Length < hl.Length)) continue; + i = j; + hl = item; + } + + if (i < 0) break; + col.Add(s.Substring(pos, i - pos), Normal); + col.Add(hl, Highlight); + pos += hl.Length; + } + + if (pos < s.Length) col.Add(s.Substring(pos), Normal); + return col; + } +#pragma warning restore IDE0057 +} diff --git a/Console/CommandLine/InfoRender.cs b/Console/CommandLine/InfoRender.cs new file mode 100644 index 0000000..746abfc --- /dev/null +++ b/Console/CommandLine/InfoRender.cs @@ -0,0 +1,360 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.IO; +using System.Text; +using System.Text.Json; +using System.Threading.Tasks; +using System.Security; + +using Trivial.Text; + +namespace Trivial.CommandLine; + +/// +/// The extensions for console renderer. +/// +public static partial class ConsoleRenderExtensions +{ + /// + /// Writes an exception, followed by the current line terminator, to the standard output stream. + /// It will flush immediately. + /// + /// The command line interface proxy. + /// The exception. + /// true if output stack trace; otherwise, false. + public static void WriteLine(this StyleConsole cli, Exception ex, bool stackTrace = false) + => WriteLine(cli, new ConsoleTextStyle + { + ForegroundConsoleColor = ConsoleColor.Red + }, null, ex, stackTrace); + + /// + /// Writes an exception, followed by the current line terminator, to the standard output stream. + /// It will flush immediately. + /// + /// The command line interface proxy. + /// The style of header. + /// The style of details. + /// The exception. + /// true if output stack trace; otherwise, false. + public static void WriteLine(this StyleConsole cli, ConsoleTextStyle captionStyle, ConsoleTextStyle messageStyle, Exception ex, bool stackTrace = false) + { + if (ex == null) return; + cli ??= StyleConsole.Default; + var header = new ConsoleText(Resource.Error, captionStyle); + if (!string.IsNullOrWhiteSpace(ex.Message)) header.Content.Append(ex.Message); + var message = new ConsoleText(Environment.NewLine, messageStyle); + if (!string.IsNullOrWhiteSpace(ex.HelpLink)) + message.Content.AppendLine(ex.HelpLink); + message.Content.Append(ex.GetType().FullName); + if (ex.InnerException != null) + { + message.Content.Append($" > {ex.InnerException.GetType().FullName}"); + if (ex.InnerException is AggregateException aggEx && aggEx.InnerExceptions != null) + { + foreach (var iEx in aggEx.InnerExceptions) + { + message.Content.AppendLine(); + message.Content.Append($"- {iEx.GetType().FullName}\t{iEx.Message}"); + } + } + else + { + if (!string.IsNullOrWhiteSpace(ex.InnerException.Message)) + { + message.Content.AppendLine(); + message.Content.Append(ex.InnerException.Message); + } + } + } + + if (stackTrace && !string.IsNullOrWhiteSpace(ex.StackTrace)) + { + message.Content.AppendLine(); + message.Content.AppendLine("Stack trace"); + message.Content.Append(ex.StackTrace); + } + + cli.WriteLine(header, message); + } + /// + /// Writes an exception, followed by the current line terminator, to the standard output stream. + /// It will flush immediately. + /// + /// The command line interface proxy. + /// The error information. + public static void WriteLine(this StyleConsole cli, Data.ErrorMessageResult ex) + => WriteLine(cli, new ConsoleTextStyle + { + ForegroundConsoleColor = ConsoleColor.Red + }, null, ex); + + /// + /// Writes an exception, followed by the current line terminator, to the standard output stream. + /// It will flush immediately. + /// + /// The command line interface proxy. + /// The style of header. + /// The style of details. + /// The error information. + public static void WriteLine(this StyleConsole cli, ConsoleTextStyle captionStyle, ConsoleTextStyle messageStyle, Data.ErrorMessageResult ex) + { + if (ex == null) return; + cli ??= StyleConsole.Default; + var header = new ConsoleText(Resource.Error, captionStyle); + if (!string.IsNullOrWhiteSpace(ex.Message)) header.Content.Append(ex.Message); + var message = new ConsoleText(Environment.NewLine, messageStyle); + if (!string.IsNullOrWhiteSpace(ex.ErrorCode)) + message.Content.Append($" [ErrCode] {ex.ErrorCode}"); + if (ex.Details != null) + { + foreach (var line in ex.Details) + { + message.Content.AppendLine(); + message.Content.Append($"- {line}"); + } + } + + cli.WriteLine(header, message); + } + + /// + /// Writes a JSON object, followed by the current line terminator, to the standard output stream. + /// It will flush immediately. + /// + /// The command line interface proxy. + /// The JSON instance. + public static void WriteLine(this StyleConsole cli, IJsonDataNode json) + => (cli ?? StyleConsole.Default).WriteLine(new JsonConsoleStyle().CreateTextCollection(json, 0)); + + /// + /// Writes a JSON object, followed by the current line terminator, to the standard output stream. + /// It will flush immediately. + /// + /// The command line interface proxy. + /// The style. + /// The JSON instance. + public static void WriteLine(this StyleConsole cli, JsonConsoleStyle style, IJsonDataNode json) + => (cli ?? StyleConsole.Default).WriteLine((style ?? new JsonConsoleStyle()).CreateTextCollection(json, 0)); + + /// + /// Writes a JSON object, followed by the current line terminator, to the standard output stream. + /// It will flush immediately. + /// + /// The command line interface proxy. + /// The JSON instance. + public static void WriteLine(this StyleConsole cli, System.Text.Json.Nodes.JsonObject json) + => (cli ?? StyleConsole.Default).WriteLine(new JsonConsoleStyle().CreateTextCollection(json == null ? null : (JsonObjectNode)json, 0)); + + /// + /// Writes a JSON object, followed by the current line terminator, to the standard output stream. + /// It will flush immediately. + /// + /// The command line interface proxy. + /// The style. + /// The JSON instance. + public static void WriteLine(this StyleConsole cli, JsonConsoleStyle style, System.Text.Json.Nodes.JsonObject json) + => (cli ?? StyleConsole.Default).WriteLine((style ?? new JsonConsoleStyle()).CreateTextCollection(json == null ? null : (JsonObjectNode)json, 0)); + + /// + /// Writes a JSON object, followed by the current line terminator, to the standard output stream. + /// It will flush immediately. + /// + /// The command line interface proxy. + /// The JSON instance. + public static void WriteLine(this StyleConsole cli, System.Text.Json.Nodes.JsonArray json) + => (cli ?? StyleConsole.Default).WriteLine(new JsonConsoleStyle().CreateTextCollection(json == null ? null : (JsonArrayNode)json, 0)); + + /// + /// Writes a JSON object, followed by the current line terminator, to the standard output stream. + /// It will flush immediately. + /// + /// The command line interface proxy. + /// The style. + /// The JSON instance. + public static void WriteLine(this StyleConsole cli, JsonConsoleStyle style, System.Text.Json.Nodes.JsonArray json) + => (cli ?? StyleConsole.Default).WriteLine((style ?? new JsonConsoleStyle()).CreateTextCollection(json == null ? null : (JsonArrayNode)json, 0)); + + /// + /// Adds an empty line. + /// + /// The console text collection. + public static void AddEmptyLine(this IList list) + => list?.Add(new ConsoleText(Environment.NewLine)); + + /// + /// Writes a sentense to allow pressing a key to continue. + /// + /// The command line interface proxy. + /// The style. + public static void PressAnyKeyToContinue(StyleConsole cli, ConsoleTextStyle style = null) + { + cli ??= StyleConsole.Default; + cli.WriteLine(style, Resource.PressAnyKeyToCont); + try + { + cli.ReadKey(true); + } + catch (InvalidOperationException) + { + } + catch (IOException) + { + } + catch (NotSupportedException) + { + } + + try + { + cli.ReadLine(); + } + catch (InvalidOperationException) + { + } + catch (IOException) + { + } + catch (NotSupportedException) + { + } + catch (ArgumentException) + { + } + + return; + } + + /// + /// Removes rest content value. + /// + /// The console text instance to limit length. + /// The length at most. + public static void RemoveRest(this ConsoleText s, int length) + { + if (s == null) return; + var sb = s.Content; + if (sb.Length <= length / 2) return; + var count = 0; + var n = new StringBuilder(); + foreach (var c in sb.ToString()) + { + var needStop = false; + switch (c) + { + case '\t': + count += 4; + break; + case '\r': + case '\n': + case '\0': + needStop = true; + break; + case '\b': + break; + default: + count += GetLetterWidth(c); + break; + } + + if (needStop || count > length) break; + n.Append(c); + } + + sb.Clear(); +#if NETFRAMEWORK + sb.Append(n.ToString()); +#else + sb.Append(n); +#endif + } + + /// + /// Tries to gets the row position of the cursor within the buffer area. + /// + /// The command line interface. + /// The row position of the cursor within the buffer area; or null if failed. + public static int? TryGetCursorTop(this StyleConsole cli) + { + try + { + return cli.CursorTop; + } + catch (IOException) + { + } + catch (InvalidOperationException) + { + } + catch (NotSupportedException) + { + } + catch (SecurityException) + { + } + + return null; + } + + /// + /// Tries to gets the column position of the cursor within the buffer area; or null if failed. + /// + /// The command line interface. + /// The column position of the cursor within the buffer area; or null if failed. + public static int? TryGetCursorLeft(this StyleConsole cli) + { + try + { + return (cli ?? StyleConsole.Default).CursorLeft; + } + catch (IOException) + { + } + catch (InvalidOperationException) + { + } + catch (NotSupportedException) + { + } + catch (SecurityException) + { + } + catch (System.Runtime.InteropServices.ExternalException) + { + } + catch (ArgumentException) + { + } + + return null; + } + + private static int GetBufferSafeWidth(StyleConsole cli) + { + try + { + return (cli ?? StyleConsole.Default).BufferWidth - 1; + } + catch (IOException) + { + } + catch (InvalidOperationException) + { + } + catch (NotSupportedException) + { + } + catch (SecurityException) + { + } + catch (System.Runtime.InteropServices.ExternalException) + { + } + catch (ArgumentException) + { + } + + return 70; + } +} diff --git a/Console/CommandLine/JsonConsoleStyle.cs b/Console/CommandLine/JsonConsoleStyle.cs new file mode 100644 index 0000000..dbc6ded --- /dev/null +++ b/Console/CommandLine/JsonConsoleStyle.cs @@ -0,0 +1,326 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Text.Json; +using System.Text.Json.Serialization; +using System.Threading.Tasks; + +using Trivial.Text; + +namespace Trivial.CommandLine; + +/// +/// The style for JSON output. +/// +public class JsonConsoleStyle : ICloneable +{ + /// + /// Gets or sets the foreground color of property key. + /// + [JsonPropertyName("property")] + [JsonConverter(typeof(JsonNumberConverter))] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public Color? PropertyForegroundRgbColor { get; set; } = Color.FromArgb(0xCE, 0x91, 0x78); + + /// + /// Gets or sets the foreground color of string value. + /// + [JsonPropertyName("string")] + [JsonConverter(typeof(JsonNumberConverter))] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public Color? StringForegroundRgbColor { get; set; } = Color.FromArgb(0xCE, 0x91, 0x78); + + /// + /// Gets or sets the foreground color of language keyword. + /// + [JsonPropertyName("keyword")] + [JsonConverter(typeof(JsonNumberConverter))] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public Color? KeywordForegroundRgbColor { get; set; } = Color.FromArgb(0x56, 0x9C, 0xD6); + + /// + /// Gets or sets the foreground color of number. + /// + [JsonPropertyName("number")] + [JsonConverter(typeof(JsonNumberConverter))] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public Color? NumberForegroundRgbColor { get; set; } = Color.FromArgb(0xB5, 0xCE, 0xA8); + + /// + /// Gets or sets the foreground color of punctuation. + /// + [JsonPropertyName("punctuation")] + [JsonConverter(typeof(JsonNumberConverter))] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public Color? PunctuationForegroundRgbColor { get; set; } = Color.FromArgb(0xDC, 0xDC, 0xDC); + + /// + /// Gets or sets the background color. + /// + [JsonPropertyName("back")] + [JsonConverter(typeof(JsonNumberConverter))] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public Color? BackgroundRgbColor { get; set; } + + /// + /// Gets or sets the foreground color of property key. + /// + [JsonPropertyName("property2")] + [JsonConverter(typeof(JsonIntegerEnumCompatibleConverter))] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public ConsoleColor? PropertyForegroundConsoleColor { get; set; } = ConsoleColor.Gray; + + /// + /// Gets or sets the foreground color of string value. + /// + [JsonPropertyName("string2")] + [JsonConverter(typeof(JsonIntegerEnumCompatibleConverter))] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public ConsoleColor? StringForegroundConsoleColor { get; set; } = ConsoleColor.Green; + + /// + /// Gets or sets the foreground color of keyword. + /// + [JsonPropertyName("keyword2")] + [JsonConverter(typeof(JsonIntegerEnumCompatibleConverter))] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public ConsoleColor? KeywordForegroundConsoleColor { get; set; } = ConsoleColor.Cyan; + + /// + /// Gets or sets the foreground color of number. + /// + [JsonPropertyName("number2")] + [JsonConverter(typeof(JsonIntegerEnumCompatibleConverter))] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public ConsoleColor? NumberForegroundConsoleColor { get; set; } = ConsoleColor.Yellow; + + /// + /// Gets or sets the foreground color of punctuation. + /// + [JsonPropertyName("punctuation2")] + [JsonConverter(typeof(JsonIntegerEnumCompatibleConverter))] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public ConsoleColor? PunctuationForegroundConsoleColor { get; set; } = ConsoleColor.Gray; + + /// + /// Gets or sets the background color. + /// + [JsonPropertyName("back2")] + [JsonConverter(typeof(JsonIntegerEnumCompatibleConverter))] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public ConsoleColor? BackgroundConsoleColor { get; set; } + + /// + /// Creates a console text by this style. + /// + /// The value. + /// A console text instance. + public ConsoleText CreateText(bool value) + => CreateByKeyword(value ? JsonBooleanNode.TrueString : JsonBooleanNode.FalseString); + + /// + /// Creates a console text by this style. + /// + /// The value. + /// A console text instance. + public ConsoleText CreateText(int value) + => new( + value.ToString("g"), + NumberForegroundRgbColor, + NumberForegroundConsoleColor, + BackgroundRgbColor, + BackgroundConsoleColor); + + /// + /// Creates a console text by this style. + /// + /// The value. + /// A console text instance. + public ConsoleText CreateText(string value) + { + return value == null ? CreateByKeyword(JsonValues.Null.ToString()) : new( + JsonStringNode.ToJson(value), + StringForegroundRgbColor, + StringForegroundConsoleColor, + BackgroundRgbColor, + BackgroundConsoleColor); + } + + /// + /// Clones an object. + /// + /// The object copied from this instance. + public virtual JsonConsoleStyle Clone() + => MemberwiseClone() as JsonConsoleStyle; + + /// + /// Clones an object. + /// + /// The object copied from this instance. + object ICloneable.Clone() + => MemberwiseClone(); + + /// + /// Creates a console text by this style. + /// + /// The JSON instance. + /// The current indent level. + /// A console text instance. + internal List CreateTextCollection(IJsonDataNode json, int indentLevel = 0) + { + var cmd = new List(); + if (json == null) + { + cmd.Add(CreateByKeyword(JsonValues.Null.ToString())); + return cmd; + } + + switch (json.ValueKind) + { + case JsonValueKind.Undefined: + case JsonValueKind.Null: + cmd.Add(CreateText(null)); + break; + case JsonValueKind.String: + cmd.Add(new(json.ToString(), + StringForegroundRgbColor, + StringForegroundConsoleColor, + BackgroundRgbColor, + BackgroundConsoleColor)); + break; + case JsonValueKind.Number: + cmd.Add(new(json.ToString(), + NumberForegroundRgbColor, + NumberForegroundConsoleColor, + BackgroundRgbColor, + BackgroundConsoleColor)); + break; + case JsonValueKind.True: + cmd.Add(CreateText(true)); + break; + case JsonValueKind.False: + cmd.Add(CreateText(false)); + break; + case JsonValueKind.Object: + cmd.AddRange(CreateTextCollection(json as JsonObjectNode, indentLevel)); + break; + case JsonValueKind.Array: + cmd.AddRange(CreateTextCollection(json as JsonArrayNode, indentLevel)); + break; + default: + break; + } + + return cmd; + } + + /// + /// Creates a console text by this style. + /// + /// The JSON instance. + /// The current indent level. + /// A console text instance. + private List CreateTextCollection(JsonObjectNode json, int indentLevel) + { + var cmd = new List(); + if (json == null) + { + cmd.Add(CreateByKeyword(JsonValues.Null.ToString())); + return cmd; + } + + var spaces = CreateByWhitespace(Environment.NewLine + new string(' ', (indentLevel + 1) * 2)); + cmd.Add(CreateByPunctuation("{")); + foreach (var prop in json) + { + cmd.Add(spaces); + cmd.Add(new( + JsonStringNode.ToJson(prop.Key), + PropertyForegroundRgbColor, + PropertyForegroundConsoleColor, + BackgroundRgbColor, + BackgroundConsoleColor)); + cmd.Add(CreateByPunctuation(": ")); + cmd.AddRange(CreateTextCollection(prop.Value, indentLevel + 1)); + cmd.Add(CreateByPunctuation(",")); + } + + if (cmd.Count > 1) cmd.RemoveAt(cmd.Count - 1); + cmd.Add(CreateByWhitespace(Environment.NewLine)); + cmd.Add(CreateByWhitespace(new string(' ', indentLevel * 2))); + cmd.Add(CreateByPunctuation("}")); + return cmd; + } + + /// + /// Creates a console text by this style. + /// + /// The JSON instance. + /// The current indent level. + /// A console text instance. + private List CreateTextCollection(JsonArrayNode json, int indentLevel) + { + var cmd = new List(); + if (json == null) + { + cmd.Add(CreateByKeyword(JsonValues.Null.ToString())); + return cmd; + } + + var spaces = CreateByWhitespace(Environment.NewLine + new string(' ', (indentLevel + 1) * 2)); + cmd.Add(CreateByPunctuation("[")); + foreach (var prop in json) + { + cmd.Add(spaces); + cmd.AddRange(CreateTextCollection(prop, indentLevel + 1)); + cmd.Add(CreateByPunctuation(",")); + } + + if (cmd.Count > 1) cmd.RemoveAt(cmd.Count - 1); + cmd.Add(CreateByWhitespace(Environment.NewLine)); + cmd.Add(CreateByWhitespace(new string(' ', indentLevel * 2))); + cmd.Add(CreateByPunctuation("]")); + return cmd; + } + + /// + /// Creates a console text by this style. + /// + /// The value. + /// A console text instance. + private ConsoleText CreateByKeyword(string value) + => new( + value, + KeywordForegroundRgbColor, + KeywordForegroundConsoleColor, + BackgroundRgbColor, + BackgroundConsoleColor); + + /// + /// Creates a console text by this style. + /// + /// The value. + /// A console text instance. + private ConsoleText CreateByPunctuation(string value) + => new( + value, + PunctuationForegroundRgbColor, + PunctuationForegroundConsoleColor, + BackgroundRgbColor, + BackgroundConsoleColor); + + /// + /// Creates a console text by this style. + /// + /// The value. + /// A console text instance. + private ConsoleText CreateByWhitespace(string value) + => new( + value, + null, + null, + BackgroundRgbColor, + BackgroundConsoleColor); +} diff --git a/Console/CommandLine/LinearGradientConsoleStyle.cs b/Console/CommandLine/LinearGradientConsoleStyle.cs new file mode 100644 index 0000000..e6960aa --- /dev/null +++ b/Console/CommandLine/LinearGradientConsoleStyle.cs @@ -0,0 +1,172 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +using Trivial.Collection; +using Trivial.Text; + +namespace Trivial.CommandLine; + +/// +/// The linear gradient console text style. +/// +public class LinearGradientConsoleStyle : IConsoleTextPrettier +{ + private readonly Color? fromFore; + private readonly Color? toFore; + private readonly Color? fromBack; + private readonly Color? toBack; + + /// + /// Initialzies a new instance of the LinearGradientConsoleStyle class. + /// + /// The fallback foreground color. + /// The from foreground color. + /// The to foreground color. + public LinearGradientConsoleStyle(ConsoleColor? fallbackForegroundColor, Color fromForegroundColor, Color toForegroundColor) + { + FallbackForegroundColor = fallbackForegroundColor; + fromFore = fromForegroundColor; + toFore = toForegroundColor; + } + + /// + /// Initialzies a new instance of the LinearGradientConsoleStyle class. + /// + /// The fallback foreground color. + /// The from foreground color. + /// The to foreground color. + /// The fallback background color. + /// The from background color. + /// The to background color. + public LinearGradientConsoleStyle(ConsoleColor? fallbackForegroundColor, Color fromForegroundColor, Color toForegroundColor, ConsoleColor? fallbackBackgroundColor, Color fromBackgroundColor, Color toBackgroundColor) + : this(fallbackForegroundColor, fromForegroundColor, toForegroundColor) + { + FallbackBackgroundColor = fallbackBackgroundColor; + fromBack = fromBackgroundColor; + toBack = toBackgroundColor; + } + + /// + /// Gets or sets the fallback foreground color. + /// + public ConsoleColor? FallbackForegroundColor { get; set; } + + /// + /// Gets or sets the fallback background color. + /// + public ConsoleColor? FallbackBackgroundColor { get; set; } + + /// + /// Gets or sets a value indicating whether the text is blink. + /// + public bool Blink { get; set; } + + /// + /// Gets or sets a value indicating whether the text is bold. + /// + public bool Bold { get; set; } + + /// + /// Gets or sets a value indicating whether the text is italic. + /// + public bool Italic { get; set; } + + /// + /// Gets or sets a value indicating whether the text is underlined. + /// + public bool Underline { get; set; } + + /// + /// Gets or sets a value indicating whether the text is strikeout. + /// + public bool Strikeout { get; set; } + + /// + /// Creates the console text collection based on this style. + /// + /// The text. + /// A collection of console text. + IEnumerable IConsoleTextPrettier.CreateTextCollection(string s) + { + var col = new List(); + if (string.IsNullOrEmpty(s)) return col; + col.Add(s[0], 1, new ConsoleTextStyle(fromFore, FallbackForegroundColor, fromBack, FallbackBackgroundColor) + { + Blink = Blink, + Bold = Bold, + Italic = Italic, + Underline = Underline, + Strikeout = Strikeout + }); + if (s.Length == 1) + { + if (fromFore.HasValue && toFore.HasValue) + col[0].Style.ForegroundRgbColor = Color.FromArgb((fromFore.Value.R + toFore.Value.R) / 2, (fromFore.Value.G + toFore.Value.G) / 2, (fromFore.Value.B + toFore.Value.B) / 2); + else if (!fromFore.HasValue) + col[0].Style.ForegroundRgbColor = toFore; + if (fromBack.HasValue && toBack.HasValue) + col[0].Style.BackgroundRgbColor = Color.FromArgb((fromBack.Value.R + toBack.Value.R) / 2, (fromBack.Value.G + toBack.Value.G) / 2, (fromBack.Value.B + toBack.Value.B) / 2); + else if (!fromBack.HasValue) + col[0].Style.BackgroundRgbColor = toBack; + return col; + } + + var steps = s.Length - 1; + var hasFore = fromFore.HasValue || toFore.HasValue; + var hasBack = fromBack.HasValue || toBack.HasValue; + var foreDelta = fromFore.HasValue && toFore.HasValue + ? ((toFore.Value.R - fromFore.Value.R) * 1.0 / steps, (toFore.Value.G - fromFore.Value.B) * 1.0 / steps, (toFore.Value.B - fromFore.Value.B) * 1.0 / steps) + : (0.0, 0.0, 0.0); + var backDelta = fromBack.HasValue && toBack.HasValue + ? ((toBack.Value.R - fromBack.Value.R) * 1.0 / steps, (toBack.Value.G - fromBack.Value.B) * 1.0 / steps, (toBack.Value.B - fromBack.Value.B) * 1.0 / steps) + : (0.0, 0.0, 0.0); + double foreR = fromFore?.R ?? toFore?.R ?? 0; + double foreG = fromFore?.G ?? toFore?.G ?? 0; + double foreB = fromFore?.B ?? toFore?.B ?? 0; + double backR = fromBack?.R ?? toBack?.R ?? 0; + double backG = fromBack?.G ?? toBack?.G ?? 0; + double backB = fromBack?.B ?? toBack?.B ?? 0; + for (var i = 1; i < steps; i++) + { + Color? fore = hasFore ? Color.FromArgb( + PlusChannel(ref foreR, foreDelta.Item1), + PlusChannel(ref foreG, foreDelta.Item2), + PlusChannel(ref foreB, foreDelta.Item3)) : null; + Color? back = hasBack ? Color.FromArgb( + PlusChannel(ref backR, backDelta.Item1), + PlusChannel(ref backG, backDelta.Item2), + PlusChannel(ref backB, backDelta.Item3)) : null; + col.Add(s[i], 1, new ConsoleTextStyle(fore, FallbackForegroundColor, back, FallbackBackgroundColor) + { + Blink = Blink, + Bold = Bold, + Italic = Italic, + Underline = Underline, + Strikeout = Strikeout + }); + } + + col.Add(s[s.Length - 1], 1, new ConsoleTextStyle(toFore, FallbackForegroundColor, toBack, FallbackBackgroundColor) + { + Blink = Blink, + Bold = Bold, + Italic = Italic, + Underline = Underline, + Strikeout = Strikeout + }); + return col; + } + + private static int PlusChannel(ref double c, double delta) + { + c = Math.Round(c + delta); + var r = (int)c; + if (r < 0) return 0; + else if (r > 255) return 255; + return r; + } +} diff --git a/Console/CommandLine/ProgressRender.cs b/Console/CommandLine/ProgressRender.cs new file mode 100644 index 0000000..8f59a60 --- /dev/null +++ b/Console/CommandLine/ProgressRender.cs @@ -0,0 +1,222 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.IO; +using System.Text; +using System.Threading.Tasks; +using System.Security; + +using Trivial.Collection; +using Trivial.Tasks; + +namespace Trivial.CommandLine; + +/// +/// The extensions for console renderer. +/// +public static partial class ConsoleRenderExtensions +{ + /// + /// Writes a progress component, followed by the current line terminator, to the standard output stream. + /// + /// The command line interface proxy. + /// The options. + /// The progress result. + public static OneProgress WriteLine(this StyleConsole cli, ConsoleProgressStyle style) + => WriteLine(cli, style, null); + + /// + /// Writes a progress component, followed by the current line terminator, to the standard output stream. + /// + /// The command line interface proxy. + /// The caption; or null if no caption. It will be better if it is less than 20 characters. + /// The progress size. + /// The progress kind. + /// The progress result. + public static OneProgress WriteLine(this StyleConsole cli, ConsoleProgressStyle.Sizes progressSize, string caption, ConsoleProgressStyle.Kinds kind = ConsoleProgressStyle.Kinds.Full) + => WriteLine(cli, progressSize != ConsoleProgressStyle.Sizes.None ? new ConsoleProgressStyle + { + Size = progressSize, + Kind = kind + } : null, caption); + + /// + /// Writes a progress component, followed by the current line terminator, to the standard output stream. + /// + /// The command line interface proxy. + /// The progress size. + /// The progress kind. + /// The progress result. + public static OneProgress WriteLine(this StyleConsole cli, ConsoleProgressStyle.Sizes progressSize, ConsoleProgressStyle.Kinds kind = ConsoleProgressStyle.Kinds.Full) + => WriteLine(cli, progressSize != ConsoleProgressStyle.Sizes.None ? new ConsoleProgressStyle + { + Size = progressSize, + Kind = kind + } : null, null); + + /// + /// Writes a progress component, followed by the current line terminator, to the standard output stream. + /// + /// The command line interface proxy. + /// The caption; or null if no caption. It will be better if it is less than 20 characters. + /// The style. + /// The progress result. + public static OneProgress WriteLine(this StyleConsole cli, ConsoleProgressStyle style, string caption) + { + if (cli == null) cli = StyleConsole.Default; + if (cli.Mode == StyleConsole.Modes.Text && cli.Handler == null) + { + var progress2 = new OneProgress(); + if (string.IsNullOrWhiteSpace(caption)) + { + cli.WriteLine(Resource.Loading); + progress2.ProgressChanged += (sender, ev) => + { + if (progress2.IsFailed || progress2.IsNotSupported) + cli.WriteLine($"{ev:#0%} {Resource.Error}"); + else if (progress2.IsCompleted) + cli.WriteLine($"√"); + }; + } + else + { + cli.WriteLine($"{caption} \t{Resource.Loading}"); + progress2.ProgressChanged += (sender, ev) => + { + if (progress2.IsFailed || progress2.IsNotSupported) + cli.WriteLine($"{caption} \t{ev:#0%} {Resource.Error}"); + else if (progress2.IsCompleted) + cli.WriteLine($"{caption} \t√"); + }; + } + + return progress2; + } + + if (style == null) style = new ConsoleProgressStyle(); + var status = RenderData(cli, style, caption, null, null); + var progress = new OneProgress(); + var top = TryGetCursorTop(cli) ?? -1; + progress.ProgressChanged += (sender, ev) => + { + var top2 = TryGetCursorTop(cli) ?? -1; + var left2 = TryGetCursorLeft(cli) ?? 0; + cli.Flush(); + if (cli.Mode == StyleConsole.Modes.Cmd && top >= 0 && top2 > top) + cli.MoveCursorBy(0, top - top2 - 1); + else + cli.MoveCursorBy(0, -1); + status = RenderData(cli, style, caption, progress, status); + if (cli.Mode == StyleConsole.Modes.Cmd && top >= 0 && top2 > top) + cli.MoveCursorTo(left2, top2); + }; + return progress; + } + + private static string RenderData(StyleConsole cli, ConsoleProgressStyle style, string caption, OneProgress value, string status) + { + var maxWidth = GetBufferSafeWidth(cli); + var width = style.Size switch + { + ConsoleProgressStyle.Sizes.None => 0, + ConsoleProgressStyle.Sizes.Short => maxWidth > 70 ? 20 : 10, + ConsoleProgressStyle.Sizes.Wide => maxWidth > 88 ? 60 : 40, + ConsoleProgressStyle.Sizes.Full => maxWidth - 5, + _ => maxWidth > 70 ? (maxWidth > 88 ? 40 : 30) : 20 + }; + var barChar = style.Kind switch + { + ConsoleProgressStyle.Kinds.AngleBracket => '>', + ConsoleProgressStyle.Kinds.Plus => '+', + ConsoleProgressStyle.Kinds.Sharp => '#', + ConsoleProgressStyle.Kinds.X => 'x', + ConsoleProgressStyle.Kinds.O => 'o', + _ => ' ', + }; + var pendingChar = style.Kind switch + { + ConsoleProgressStyle.Kinds.AngleBracket => '=', + ConsoleProgressStyle.Kinds.Plus => '-', + ConsoleProgressStyle.Kinds.Sharp => '-', + ConsoleProgressStyle.Kinds.X => '.', + ConsoleProgressStyle.Kinds.O => '.', + _ => ' ', + }; + var col = new List(); + if (!string.IsNullOrWhiteSpace(caption)) + { + var sb = new StringBuilder(); + var j = style.IgnoreCaptionSeparator ? 0 : 1; + foreach (var c in caption) + { + var c2 = c; + switch (c) + { + case '\t': + case '\r': + case '\n': + j++; + c2 = ' '; + break; + case '\0': + case '\b': + continue; + default: + j += GetLetterWidth(c); + break; + } + + sb.Append(c2); + } + + if (!style.IgnoreCaptionSeparator) sb.Append(' '); + col.Add(sb, new ConsoleTextStyle(style.CaptionRgbColor, style.CaptionConsoleColor, style.BackgroundRgbColor, style.BackgroundConsoleColor)); + if (style.Size == ConsoleProgressStyle.Sizes.Full) + width -= j; + } + + var v = value?.Value ?? -1; + if (v > 1) v = 1; + if (double.IsNaN(v)) + { + cli.WriteLine(col); + return null; + } + + var w = (int)Math.Round(width * v); + if (w < 0) w = 0; + var isError = value?.IsFailed == true || value?.IsNotSupported == true; + var isSucc = !isError && value?.IsSuccessful == true; + var newStatus = $"{(isError ? "e" : (isSucc ? "s" : "p"))}{w}/{maxWidth}"; + if (status == newStatus) + { + cli.Flush(); + cli.MoveCursorBy(0, 1); + return status; + } + + if (barChar == ' ') + { + col.Add(barChar, w, new ConsoleTextStyle(null, null, isError ? style.ErrorRgbColor : style.BarRgbColor, isError ? style.ErrorConsoleColor : style.BarConsoleColor)); + col.Add(pendingChar, width - w, new ConsoleTextStyle(null, null, style.PendingRgbColor, style.PendingConsoleColor)); + } + else + { + col.Add(barChar, w, new ConsoleTextStyle(isError ? style.ErrorRgbColor : style.BarRgbColor, isError ? style.ErrorConsoleColor : style.BarConsoleColor, style.BackgroundRgbColor, style.BackgroundConsoleColor)); + col.Add(pendingChar, width - w, new ConsoleTextStyle(style.PendingRgbColor, style.PendingConsoleColor, style.BackgroundRgbColor, style.BackgroundConsoleColor)); + } + + if (v >= 0) + { + var s = v.ToString("#0%"); + if (s.Length > 3) s = isSucc ? " √" : "99%"; + col.Add(" " + s, new ConsoleTextStyle(style.ValueRgbColor, style.ValueConsoleColor, style.BackgroundRgbColor, style.BackgroundConsoleColor)); + } + + cli.Flush(); + cli.Clear(StyleConsole.RelativeAreas.Line); + cli.BackspaceToBeginning(); + cli.WriteLine(col); + return status; + } +} diff --git a/Console/CommandLine/ProgressStyle.cs b/Console/CommandLine/ProgressStyle.cs new file mode 100644 index 0000000..1586a73 --- /dev/null +++ b/Console/CommandLine/ProgressStyle.cs @@ -0,0 +1,198 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Text; +using System.Text.Json.Serialization; +using System.Threading.Tasks; + +using Trivial.Text; + +namespace Trivial.CommandLine; + +/// +/// The style of progress line console component. +/// +public class ConsoleProgressStyle +{ + /// + /// Progress sizes (width). + /// + public enum Sizes : byte + { + /// + /// Normal size. + /// + Normal = 0, + + /// + /// Short size. + /// + Short = 1, + + /// + /// Wide size. + /// + Wide = 2, + + /// + /// The progress and its related text will stretch horizontal in the console. + /// + Full = 3, + + /// + /// No progress bar but only a value. + /// + None = 4 + } + + /// + /// The output text kinds filling in progress. + /// + public enum Kinds : byte + { + /// + /// Whitespace (rectangle). + /// + Full = 0, + + /// + /// Left angle bracket (less sign). + /// + AngleBracket = 1, + + /// + /// Plus sign. + /// + Plus = 2, + + /// + /// Sharp. + /// + Sharp = 3, + + /// + /// Character x. + /// + X = 4, + + /// + /// Character o. + /// + O = 5 + } + + /// + /// Gets or sets the background color of the component. + /// + [JsonPropertyName("back2")] + [JsonConverter(typeof(JsonIntegerEnumCompatibleConverter))] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public ConsoleColor? BackgroundConsoleColor { get; set; } + + /// + /// Gets or sets the background color of the component. + /// + [JsonPropertyName("back")] + [JsonConverter(typeof(JsonNumberConverter))] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public Color? BackgroundRgbColor { get; set; } + + /// + /// Gets or sets the progress background color. + /// + [JsonPropertyName("pending2")] + [JsonConverter(typeof(JsonIntegerEnumCompatibleConverter))] + public ConsoleColor PendingConsoleColor { get; set; } = ConsoleColor.DarkGray; + + /// + /// Gets or sets the progress background color. + /// + [JsonPropertyName("pending")] + [JsonConverter(typeof(JsonNumberConverter))] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public Color? PendingRgbColor { get; set; } = Color.FromArgb(68, 68, 68); + + /// + /// Gets or sets the progress bar color. + /// + [JsonPropertyName("bar2")] + [JsonConverter(typeof(JsonIntegerEnumCompatibleConverter))] + public ConsoleColor BarConsoleColor { get; set; } = ConsoleColor.Green; + + /// + /// Gets or sets the progress bar color. + /// + [JsonPropertyName("bar")] + [JsonConverter(typeof(JsonNumberConverter))] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public Color? BarRgbColor { get; set; } = Color.FromArgb(48, 192, 128); + + /// + /// Gets or sets the error color. + /// + [JsonPropertyName("error2")] + [JsonConverter(typeof(JsonIntegerEnumCompatibleConverter))] + public ConsoleColor ErrorConsoleColor { get; set; } = ConsoleColor.Red; + + /// + /// Gets or sets the error color. + /// + [JsonPropertyName("error")] + [JsonConverter(typeof(JsonNumberConverter))] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public Color? ErrorRgbColor { get; set; } = Color.FromArgb(212, 48, 48); + + /// + /// Gets or sets the foreground color of caption. + /// + [JsonPropertyName("caption2")] + [JsonConverter(typeof(JsonIntegerEnumCompatibleConverter))] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public ConsoleColor? CaptionConsoleColor { get; set; } + + /// + /// Gets or sets the foreground color of caption. + /// + [JsonPropertyName("caption")] + [JsonConverter(typeof(JsonNumberConverter))] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public Color? CaptionRgbColor { get; set; } + + /// + /// Gets or sets the foreground color of value. + /// + [JsonPropertyName("value2")] + [JsonConverter(typeof(JsonIntegerEnumCompatibleConverter))] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public ConsoleColor? ValueConsoleColor { get; set; } = ConsoleColor.Gray; + + /// + /// Gets or sets the foreground color of value. + /// + [JsonPropertyName("value")] + [JsonConverter(typeof(JsonNumberConverter))] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public Color? ValueRgbColor { get; set; } + + /// + /// Gets or sets the progress size (width). + /// + [JsonPropertyName("size")] + [JsonConverter(typeof(JsonIntegerEnumCompatibleConverter))] + public Sizes Size { get; set; } + + /// + /// Gets or sets the progress style. + /// + [JsonPropertyName("kind")] + [JsonConverter(typeof(JsonIntegerEnumCompatibleConverter))] + public Kinds Kind { get; set; } + + /// + /// Gets or sets a value indicating whether remove the white space between caption and progress. + /// + [JsonPropertyName("slim")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public bool IgnoreCaptionSeparator { get; set; } +} diff --git a/Console/CommandLine/RepeatedColorConsoleStyle.cs b/Console/CommandLine/RepeatedColorConsoleStyle.cs new file mode 100644 index 0000000..ec30ddc --- /dev/null +++ b/Console/CommandLine/RepeatedColorConsoleStyle.cs @@ -0,0 +1,191 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +using Trivial.Collection; +using Trivial.Text; + +namespace Trivial.CommandLine; + +/// +/// The linear gradient console text style. +/// +public class RepeatedColorConsoleStyle : IConsoleTextPrettier +{ + /// + /// Initialzies a new instance of the RepeatedColorConsoleStyle class. + /// + public RepeatedColorConsoleStyle() + { + } + + /// + /// Initialzies a new instance of the RepeatedColorConsoleStyle class. + /// + /// The fallback foreground color. + /// The foreground colors. + public RepeatedColorConsoleStyle(ConsoleColor fallbackForegroundColor, IEnumerable foregroundColors) + { + ForegroundConsoleColors.Add(fallbackForegroundColor); + if (foregroundColors != null) ForegroundRgbColors.AddRange(foregroundColors); + } + + /// + /// Initialzies a new instance of the RepeatedColorConsoleStyle class. + /// + /// The foreground console colors. + /// The foreground RGB colors. + public RepeatedColorConsoleStyle(IEnumerable foregroundConsoleColors, IEnumerable foregroundRgbColors) + { + if (foregroundConsoleColors != null) ForegroundConsoleColors.AddRange(foregroundConsoleColors); + if (foregroundRgbColors != null) ForegroundRgbColors.AddRange(foregroundRgbColors); + } + + /// + /// Initialzies a new instance of the RepeatedColorConsoleStyle class. + /// + /// The foreground console colors. + /// The foreground RGB colors. + /// The background console colors. + /// The background RGB colors. + public RepeatedColorConsoleStyle(IEnumerable foregroundConsoleColors, IEnumerable foregroundRgbColors, ConsoleColor? backgroundConsoleColor, Color? backgroundRgbColor) + { + if (foregroundConsoleColors != null) ForegroundConsoleColors.AddRange(foregroundConsoleColors); + if (foregroundRgbColors != null) ForegroundRgbColors.AddRange(foregroundRgbColors); + if (backgroundConsoleColor.HasValue) BackgroundConsoleColors.Add(backgroundConsoleColor.Value); + if (backgroundRgbColor.HasValue) BackgroundRgbColors.Add(backgroundRgbColor.Value); + } + + /// + /// Initialzies a new instance of the RepeatedColorConsoleStyle class. + /// + /// The foreground console colors. + /// The foreground RGB colors. + /// The background console colors. + /// The background RGB colors. + public RepeatedColorConsoleStyle(IEnumerable foregroundConsoleColors, IEnumerable foregroundRgbColors, IEnumerable backgroundConsoleColors, IEnumerable backgroundRgbColors) + { + if (foregroundConsoleColors != null) ForegroundConsoleColors.AddRange(foregroundConsoleColors); + if (foregroundRgbColors != null) ForegroundRgbColors.AddRange(foregroundRgbColors); + if (backgroundConsoleColors != null) BackgroundConsoleColors.AddRange(backgroundConsoleColors); + if (backgroundRgbColors != null) BackgroundRgbColors.AddRange(backgroundRgbColors); + } + + /// + /// Initialzies a new instance of the RepeatedColorConsoleStyle class. + /// + /// The foreground console color. + /// The foreground RGB color. + /// The background console colors. + /// The background RGB colors. + public RepeatedColorConsoleStyle(ConsoleColor foregroundConsoleColor, Color foregroundRgbColor, IEnumerable backgroundConsoleColors, IEnumerable backgroundRgbColors) + { + ForegroundConsoleColors.Add(foregroundConsoleColor); + ForegroundRgbColors.Add(foregroundRgbColor); + if (backgroundConsoleColors != null) BackgroundConsoleColors.AddRange(backgroundConsoleColors); + if (backgroundRgbColors != null) BackgroundRgbColors.AddRange(backgroundRgbColors); + } + + /// + /// Gets or sets the foreground console colors. + /// + public List ForegroundConsoleColors { get; } = new(); + + /// + /// Gets or sets the foreground RGB colors. + /// + public List ForegroundRgbColors { get; } = new(); + + /// + /// Gets or sets the background console colors. + /// + public List BackgroundConsoleColors { get; } = new(); + + /// + /// Gets or sets the background RGB colors. + /// + public List BackgroundRgbColors { get; } = new(); + + /// + /// Gets or sets a value indicating whether the text is blink. + /// + public bool Blink { get; set; } + + /// + /// Gets or sets a value indicating whether the text is bold. + /// + public bool Bold { get; set; } + + /// + /// Gets or sets a value indicating whether the text is italic. + /// + public bool Italic { get; set; } + + /// + /// Gets or sets a value indicating whether the text is underlined. + /// + public bool Underline { get; set; } + + /// + /// Gets or sets a value indicating whether the text is strikeout. + /// + public bool Strikeout { get; set; } + + /// + /// Creates the console text collection based on this style. + /// + /// The text. + /// A collection of console text. + IEnumerable IConsoleTextPrettier.CreateTextCollection(string s) + { + var col = new List(); + if (string.IsNullOrEmpty(s)) return col; + if (ForegroundConsoleColors.Count < 2 && ForegroundRgbColors.Count < 2 && BackgroundConsoleColors.Count < 2 && BackgroundRgbColors.Count < 2) + { + col.Add(s, new ConsoleTextStyle( + ForegroundRgbColors.FirstOrDefault(), + ForegroundConsoleColors.FirstOrDefault(), + BackgroundRgbColors.FirstOrDefault(), + BackgroundConsoleColors.FirstOrDefault()) + { + Blink = Blink, + Bold = Bold, + Italic = Italic, + Underline = Underline, + Strikeout = Strikeout + }); + return col; + } + + var i = 0; + foreach (var c in s) + { + Color? foreRgb = ForegroundRgbColors.Count > 0 + ? ForegroundRgbColors[i % ForegroundRgbColors.Count] + : null; + ConsoleColor? foreConsole = ForegroundConsoleColors.Count > 0 + ? ForegroundConsoleColors[i % ForegroundConsoleColors.Count] + : null; + Color? backRgb = BackgroundRgbColors.Count > 0 + ? BackgroundRgbColors[i % BackgroundRgbColors.Count] + : null; + ConsoleColor? backConsole = BackgroundConsoleColors.Count > 0 + ? BackgroundConsoleColors[i % BackgroundConsoleColors.Count] + : null; + i++; + col.Add(c, 1, new ConsoleTextStyle(foreRgb, foreConsole, backRgb, backConsole) + { + Blink = Blink, + Bold = Bold, + Italic = Italic, + Underline = Underline, + Strikeout = Strikeout + }); + } + + return col; + } +} diff --git a/Console/CommandLine/Resource.Designer.cs b/Console/CommandLine/Resource.Designer.cs new file mode 100644 index 0000000..ef0a6fc --- /dev/null +++ b/Console/CommandLine/Resource.Designer.cs @@ -0,0 +1,126 @@ +//------------------------------------------------------------------------------ +// +// 此代码由工具生成。 +// 运行时版本:4.0.30319.42000 +// +// 对此文件的更改可能会导致不正确的行为,并且如果 +// 重新生成代码,这些更改将会丢失。 +// +//------------------------------------------------------------------------------ + +namespace Trivial.CommandLine { + using System; + + + /// + /// 一个强类型的资源类,用于查找本地化的字符串等。 + /// + // 此类是由 StronglyTypedResourceBuilder + // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。 + // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen + // (以 /str 作为命令选项),或重新生成 VS 项目。 + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resource { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resource() { + } + + /// + /// 返回此类使用的缓存的 ResourceManager 实例。 + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Trivial.CommandLine.Resource", typeof(Resource).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// 重写当前线程的 CurrentUICulture 属性,对 + /// 使用此强类型资源类的所有资源查找执行重写。 + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// 查找类似 End the current conversation. 的本地化字符串。 + /// + internal static string EndConversation { + get { + return ResourceManager.GetString("EndConversation", resourceCulture); + } + } + + /// + /// 查找类似 Error! 的本地化字符串。 + /// + internal static string Error { + get { + return ResourceManager.GetString("Error", resourceCulture); + } + } + + /// + /// 查找类似 Get help. 的本地化字符串。 + /// + internal static string GetHelp { + get { + return ResourceManager.GetString("GetHelp", resourceCulture); + } + } + + /// + /// 查找类似 Loading... 的本地化字符串。 + /// + internal static string Loading { + get { + return ResourceManager.GetString("Loading", resourceCulture); + } + } + + /// + /// 查找类似 Press any key to continue... 的本地化字符串。 + /// + internal static string PressAnyKeyToCont { + get { + return ResourceManager.GetString("PressAnyKeyToCont", resourceCulture); + } + } + + /// + /// 查找类似 Tips: [↑][↓][←][→] Select; [ENTER] OK. 的本地化字符串。 + /// + internal static string SelectionTips { + get { + return ResourceManager.GetString("SelectionTips", resourceCulture); + } + } + + /// + /// 查找类似 Select: 的本地化字符串。 + /// + internal static string ToSelect { + get { + return ResourceManager.GetString("ToSelect", resourceCulture); + } + } + } +} diff --git a/Console/CommandLine/Resource.de.resx b/Console/CommandLine/Resource.de.resx new file mode 100644 index 0000000..f57cd5a --- /dev/null +++ b/Console/CommandLine/Resource.de.resx @@ -0,0 +1,141 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Beenden Sie die aktuelle Sitzung. + + + Fehler! + + + Holen Sie sich Hilfe. + + + Laden... + + + Drücken Sie eine beliebige Taste, um fortzufahren... + + + Tipps: [↑][↓][←][→] Wählen; [ENTER] OKAY. + + + Wählen: + + \ No newline at end of file diff --git a/Console/CommandLine/Resource.en.resx b/Console/CommandLine/Resource.en.resx new file mode 100644 index 0000000..1a42488 --- /dev/null +++ b/Console/CommandLine/Resource.en.resx @@ -0,0 +1,141 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + End the current conversation. + + + Error! + + + Get help. + + + Loading... + + + Press any key to continue... + + + Tips: [↑][↓][←][→] Select; [ENTER] OK. + + + Select: + + \ No newline at end of file diff --git a/Console/CommandLine/Resource.es.resx b/Console/CommandLine/Resource.es.resx new file mode 100644 index 0000000..bb3cec0 --- /dev/null +++ b/Console/CommandLine/Resource.es.resx @@ -0,0 +1,141 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Termine la conversación actual. + + + Error! + + + Ayuda. + + + Cargando... + + + Pulsa cualquier tecla para continuar... + + + Tips: [↑][↓][←][→] Seleccione; [ENTER] OK. + + + Seleccione: + + \ No newline at end of file diff --git a/Console/CommandLine/Resource.fr.resx b/Console/CommandLine/Resource.fr.resx new file mode 100644 index 0000000..1d914f8 --- /dev/null +++ b/Console/CommandLine/Resource.fr.resx @@ -0,0 +1,141 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Mettez fin à la conversation en cours. + + + Erreur! + + + Obtenez de l’aide. + + + Chargement... + + + Appuyez sur n’importe quelle touche pour continuer... + + + Conseils: [↑][↓][←][→]=Sélectionnez; [ENTRÉE]=D’accord. + + + Choisir: + + \ No newline at end of file diff --git a/Console/CommandLine/Resource.ja.resx b/Console/CommandLine/Resource.ja.resx new file mode 100644 index 0000000..4bc2539 --- /dev/null +++ b/Console/CommandLine/Resource.ja.resx @@ -0,0 +1,141 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 会話の終了。 + + + エラー! + + + 情報を取得します。 + + + 読み込み中…… + + + 任意のキーを押して続行します…… + + + 操作提示:[↑][↓][←][→]=選択;[ENTER]=確定。 + + + 選択してください: + + \ No newline at end of file diff --git a/Console/CommandLine/Resource.ko.resx b/Console/CommandLine/Resource.ko.resx new file mode 100644 index 0000000..9b827c7 --- /dev/null +++ b/Console/CommandLine/Resource.ko.resx @@ -0,0 +1,141 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 현재 대화를 종료합니다. + + + 오류! + + + 정보를 가져옵니다. + + + 로드... + + + 계속하려면 키를 누릅니다. + + + 팁: [↑][↓][←][→]=선택;[ENTER]=오케이. + + + 선택: + + \ No newline at end of file diff --git a/Console/CommandLine/Resource.pt.resx b/Console/CommandLine/Resource.pt.resx new file mode 100644 index 0000000..760ee1c --- /dev/null +++ b/Console/CommandLine/Resource.pt.resx @@ -0,0 +1,141 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Termine a conversa atual. + + + Erro! + + + Pedir ajuda. + + + Carregamento... + + + Pressione qualquer tecla para continuar... + + + Dicas: [↑][↓][←][→] Selecione; [ENTER] OK. + + + Selecione: + + \ No newline at end of file diff --git a/Console/CommandLine/Resource.resx b/Console/CommandLine/Resource.resx new file mode 100644 index 0000000..1a42488 --- /dev/null +++ b/Console/CommandLine/Resource.resx @@ -0,0 +1,141 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + End the current conversation. + + + Error! + + + Get help. + + + Loading... + + + Press any key to continue... + + + Tips: [↑][↓][←][→] Select; [ENTER] OK. + + + Select: + + \ No newline at end of file diff --git a/Console/CommandLine/Resource.ru.resx b/Console/CommandLine/Resource.ru.resx new file mode 100644 index 0000000..a75dd63 --- /dev/null +++ b/Console/CommandLine/Resource.ru.resx @@ -0,0 +1,141 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Завершите текущую беседу. + + + Ошибка! + + + Справка. + + + Погрузка... + + + Нажмите любую клавишу, чтобы продолжить... + + + Советы: [↑][↓][←][→] Выбирать; [ENTER] Ок. + + + Выбирать: + + \ No newline at end of file diff --git a/Console/CommandLine/Resource.zh-Hans.resx b/Console/CommandLine/Resource.zh-Hans.resx new file mode 100644 index 0000000..78d5995 --- /dev/null +++ b/Console/CommandLine/Resource.zh-Hans.resx @@ -0,0 +1,141 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 退出当前会话。 + + + 错误! + + + 获取帮助。 + + + 加载中…… + + + 按任意键继续…… + + + 请先按方向键进行选择,然后按回车键确定。 + + + 请选择: + + \ No newline at end of file diff --git a/Console/CommandLine/Resource.zh-Hant.resx b/Console/CommandLine/Resource.zh-Hant.resx new file mode 100644 index 0000000..92909c0 --- /dev/null +++ b/Console/CommandLine/Resource.zh-Hant.resx @@ -0,0 +1,141 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 退出當前會話。 + + + 錯誤! + + + 獲取説明。 + + + 載入中…… + + + 按任意鍵繼續...... + + + 操作提示:[↑][↓][←][→]=選擇;[ENTER]=確定。 + + + 請選擇: + + \ No newline at end of file diff --git a/Console/CommandLine/SelectionOptions.cs b/Console/CommandLine/SelectionOptions.cs new file mode 100644 index 0000000..730e7d5 --- /dev/null +++ b/Console/CommandLine/SelectionOptions.cs @@ -0,0 +1,291 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Text.Json.Serialization; +using System.Threading.Tasks; + +using Trivial.Text; + +namespace Trivial.CommandLine; + +/// +/// The selection options. +/// +public class SelectionConsoleOptions : ICloneable +{ + /// + /// Gets or sets the minimum length for each item. + /// + [JsonPropertyName("minlen")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public int? MinLength { get; set; } + + /// + /// Gets or sets the maximum length for each item. + /// + [JsonPropertyName("maxlen")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public int? MaxLength { get; set; } + + /// + /// Gets or sets the maximum column count to display. + /// + [JsonPropertyName("columns")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public int? Column { get; set; } + + /// + /// Gets or sets maximum row count per page. + /// null for disable paging. + /// + [JsonPropertyName("rows")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public int? MaxRow { get; set; } + + /// + /// Gets or sets the tips. + /// null for disable tips. + /// + [JsonPropertyName("tip")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public string Tips { get; set; } = Resource.SelectionTips; + + /// + /// Gets or sets the paging tips. + /// Or null to disable tips. + /// + [JsonPropertyName("pagetip")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public string PagingTips { get; set; } = "← [PgUp] | {from} - {end} / {total} | [PgDn] →"; + + /// + /// Gets or sets the question message for keyboard selecting. + /// Or null to disable additional question line. + /// + [JsonPropertyName("q")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public string Question { get; set; } = Resource.ToSelect; + + /// + /// Gets or sets the question message for manual typing. + /// Or null to disable manual mode. + /// + [JsonPropertyName("manualq")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public string ManualQuestion { get; set; } + + /// + /// Gets or sets the question message displayed when it is not supported. + /// Or null to disable manual mode. + /// + [JsonPropertyName("notsupportq")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public string QuestionWhenNotSupported { get; set; } + + /// + /// Gets or sets the foreground color for item. + /// + [JsonPropertyName("fore")] + [JsonConverter(typeof(JsonIntegerEnumCompatibleConverter))] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public ConsoleColor? ForegroundColor { get; set; } + + /// + /// Gets or sets the background color for item. + /// + [JsonPropertyName("back")] + [JsonConverter(typeof(JsonIntegerEnumCompatibleConverter))] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public ConsoleColor? BackgroundColor { get; set; } + + /// + /// Gets or sets the foreground color for item selected. + /// + [JsonPropertyName("selfore2")] + [JsonConverter(typeof(JsonIntegerEnumCompatibleConverter))] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public ConsoleColor? SelectedForegroundConsoleColor { get; set; } = ConsoleColor.Black; + + /// + /// Gets or sets the foreground color for item selected. + /// + [JsonPropertyName("selfore")] + [JsonConverter(typeof(JsonNumberConverter))] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public Color? SelectedForegroundRgbColor { get; set; } + + /// + /// Gets or sets the foreground color for item selected. + /// + [JsonPropertyName("selback2")] + [JsonConverter(typeof(JsonIntegerEnumCompatibleConverter))] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public ConsoleColor? SelectedBackgroundConsoleColor { get; set; } = ConsoleColor.Cyan; + + /// + /// Gets or sets the foreground color for item selected. + /// + [JsonPropertyName("selback")] + [JsonConverter(typeof(JsonNumberConverter))] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public Color? SelectedBackgroundRgbColor { get; set; } = Color.FromArgb(0x55, 0xCC, 0xEE); + + /// + /// Gets or sets the foreground color for question. + /// + [JsonPropertyName("qfore2")] + [JsonConverter(typeof(JsonIntegerEnumCompatibleConverter))] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public ConsoleColor? QuestionForegroundConsoleColor { get; set; } + + /// + /// Gets or sets the foreground color for question. + /// + [JsonPropertyName("qfore")] + [JsonConverter(typeof(JsonNumberConverter))] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public Color? QuestionForegroundRgbColor { get; set; } + + /// + /// Gets or sets the background color for question. + /// + [JsonPropertyName("qback2")] + [JsonConverter(typeof(JsonIntegerEnumCompatibleConverter))] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public ConsoleColor? QuestionBackgroundConsoleColor { get; set; } + + /// + /// Gets or sets the background color for question. + /// + [JsonPropertyName("qback")] + [JsonConverter(typeof(JsonNumberConverter))] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public Color? QuestionBackgroundRgbColor { get; set; } + + /// + /// Gets or sets the foreground color for tips. + /// + [JsonPropertyName("tipfore2")] + [JsonConverter(typeof(JsonIntegerEnumCompatibleConverter))] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public ConsoleColor? TipsForegroundConsoleColor { get; set; } = ConsoleColor.Yellow; + + /// + /// Gets or sets the foreground color for tips. + /// + [JsonPropertyName("tipfore")] + [JsonConverter(typeof(JsonNumberConverter))] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public Color? TipsForegroundRgbColor { get; set; } = Color.FromArgb(0xF9, 0xEE, 0x88); + + /// + /// Gets or sets the background color for tips. + /// + [JsonPropertyName("tipback2")] + [JsonConverter(typeof(JsonIntegerEnumCompatibleConverter))] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public ConsoleColor? TipsBackgroundConsoleColor { get; set; } + + /// + /// Gets or sets the background color for tips. + /// + [JsonPropertyName("tipback")] + [JsonConverter(typeof(JsonNumberConverter))] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public Color? TipsBackgroundRgbColor { get; set; } + + /// + /// Gets or sets the foreground color for paing tips. + /// + [JsonPropertyName("pagefore2")] + [JsonConverter(typeof(JsonIntegerEnumCompatibleConverter))] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public ConsoleColor? PagingForegroundConsoleColor { get; set; } + + /// + /// Gets or sets the foreground color for paing tips. + /// + [JsonPropertyName("pagefore")] + [JsonConverter(typeof(JsonNumberConverter))] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public Color? PagingForegroundRgbColor { get; set; } + + /// + /// Gets or sets the background color for paging tips. + /// + [JsonPropertyName("pageback2")] + [JsonConverter(typeof(JsonIntegerEnumCompatibleConverter))] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public ConsoleColor? PagingBackgroundConsoleColor { get; set; } + + /// + /// Gets or sets the background color for paging tips. + /// + [JsonPropertyName("pageback")] + [JsonConverter(typeof(JsonNumberConverter))] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public Color? PagingBackgroundRgbColor { get; set; } + + /// + /// Gets or sets the foreground color for default value. + /// + [JsonPropertyName("itemfore2")] + [JsonConverter(typeof(JsonIntegerEnumCompatibleConverter))] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public ConsoleColor? ItemForegroundConsoleColor { get; set; } + + /// + /// Gets or sets the foreground color for default value. + /// + [JsonPropertyName("itemfore")] + [JsonConverter(typeof(JsonNumberConverter))] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public Color? ItemForegroundRgbColor { get; set; } + + /// + /// Gets or sets the background color for default value. + /// + [JsonPropertyName("itemback2")] + [JsonConverter(typeof(JsonIntegerEnumCompatibleConverter))] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public ConsoleColor? ItemBackgroundConsoleColor { get; set; } + + /// + /// Gets or sets the background color for default value. + /// + [JsonPropertyName("itemback")] + [JsonConverter(typeof(JsonNumberConverter))] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public Color? ItemBackgroundRgbColor { get; set; } + + /// + /// Gets or sets the prefix for the item. + /// + [JsonPropertyName("prefix")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public string Prefix { get; set; } + + /// + /// Gets or sets the prefix for the item selected. + /// + [JsonPropertyName("selprefix")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public string SelectedPrefix { get; set; } + + /// + /// Clones an object. + /// + /// The object copied from this instance. + public virtual SelectionConsoleOptions Clone() + => MemberwiseClone() as SelectionConsoleOptions; + + /// + /// Clones an object. + /// + /// The object copied from this instance. + object ICloneable.Clone() + => MemberwiseClone(); +} diff --git a/Console/CommandLine/SelectionRender.cs b/Console/CommandLine/SelectionRender.cs new file mode 100644 index 0000000..38336d5 --- /dev/null +++ b/Console/CommandLine/SelectionRender.cs @@ -0,0 +1,867 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.IO; +using System.Text; +using System.Threading.Tasks; +using System.Security; + +using Trivial.Collection; + +namespace Trivial.CommandLine; + +/// +/// The extensions for console renderer. +/// +public static partial class ConsoleRenderExtensions +{ + /// + /// Writes a collection of item for selecting. + /// + /// The command line interface proxy. + /// The collection data. + /// The selection display options. + /// The result of selection. + public static SelectionResult Select(this StyleConsole cli, SelectionData collection, SelectionConsoleOptions options = null) + => Select(cli, collection, options); + + /// + /// Writes a collection of item for selecting. + /// + /// The command line interface proxy. + /// The collection data. + /// The converter. + /// The selection display options. + /// The result of selection. + public static SelectionResult Select(this StyleConsole cli, IEnumerable collection, Func> convert, SelectionConsoleOptions options = null) + { + var c = new SelectionData(); + c.AddRange(collection.Select(convert)); + return Select(cli, c, options); + } + + /// + /// Writes a collection of item for selecting. + /// + /// The command line interface proxy. + /// The collection data. + /// The selection display options. + /// The result of selection. + public static SelectionResult Select(this StyleConsole cli, IEnumerable> collection, SelectionConsoleOptions options = null) + { + var c = new SelectionData(); + c.AddRange(collection); + return Select(cli, c, options); + } + + /// + /// Writes a collection of item for selecting. + /// + /// The command line interface proxy. + /// The parent foler path. + /// The selection display options. + /// The search string to match against the names of directories and files. This parameter can contain a combination of valid literal path and wildcard (* and ?) characters, but it doesn't support regular expressions. + /// The result of selection. + /// searchPattern contains one or more invalid characters defined by the System.IO.Path.GetInvalidPathChars method. + /// The specified path is invalid (for example, it is on an unmapped drive). + /// The caller does not have the required permission. + public static SelectionResult Select(this StyleConsole cli, DirectoryInfo path, SelectionConsoleOptions options = null, string searchPattern = null) + { + var c = new SelectionData(); + var col = string.IsNullOrEmpty(searchPattern) ? path.GetFileSystemInfos() : path.GetFileSystemInfos(searchPattern); + foreach (var f in col) + { + c.Add(f.Name, f); + } + + return Select(cli, c, options); + } + + /// + /// Writes a collection of item for selecting. + /// + /// The command line interface proxy. + /// The parent foler path. + /// true if only display files; otherwise, false. + /// The selection display options. + /// The search string to match against the names of directories and files. This parameter can contain a combination of valid literal path and wildcard (* and ?) characters, but it doesn't support regular expressions. + /// The result of selection. + /// searchPattern contains one or more invalid characters defined by the System.IO.Path.GetInvalidPathChars method. + /// The specified path is invalid (for example, it is on an unmapped drive). + /// The caller does not have the required permission. + public static SelectionResult Select(this StyleConsole cli, DirectoryInfo path, bool onlyFiles, SelectionConsoleOptions options = null, string searchPattern = null) + { + if (!onlyFiles) return Select(cli, path, options, searchPattern); + var c = new SelectionData(); + var col = string.IsNullOrEmpty(searchPattern) ? path.GetFiles() : path.GetFiles(searchPattern); + foreach (var f in col) + { + c.Add(f.Name, f); + } + + return Select(cli, c, options); + } + + /// + /// Writes a collection of item for selecting. + /// + /// The command line interface proxy. + /// The parent foler path. + /// A function to test each element for a condition. + /// The selection display options. + /// The result of selection. + /// The specified path is invalid (for example, it is on an unmapped drive). + /// The caller does not have the required permission. + public static SelectionResult Select(this StyleConsole cli, DirectoryInfo path, Func predicate, SelectionConsoleOptions options = null) + { + var c = new SelectionData(); + IEnumerable col = path.GetFileSystemInfos(); + if (predicate != null) col = col.Where(predicate); + foreach (var f in col) + { + c.Add(f.Name, f); + } + + return Select(cli, c, options); + } + + /// + /// Writes a collection of item for selecting. + /// + /// The type of data. + /// The command line interface proxy. + /// The collection data. + /// The selection display options. + /// The result of selection. + public static SelectionResult Select(this StyleConsole cli, SelectionData collection, SelectionConsoleOptions options = null) + { + if (collection == null) return null; + if (cli == null) cli = StyleConsole.Default; + if (options == null) options = new(); + else options = options.Clone(); + cli.Flush(); + if (cli.Handler == null && cli.Mode == StyleConsole.Modes.Text) + return SelectForText(cli, collection, options); + return Select(cli, collection, options, 0); + } + + /// + /// Writes a collection of item for selecting. + /// + /// The command line interface proxy. + /// The collection data. + /// The selection display options. + /// The result of selection. + public static SelectionResult Select(this StyleConsole cli, IEnumerable collection, SelectionConsoleOptions options = null) + { + if (collection == null) return null; + if (cli == null) cli = StyleConsole.Default; + if (options == null) options = new(); + else options = options.Clone(); + cli.Flush(); + var c = new SelectionData(); + c.AddRange(collection); + if (cli.Handler == null && cli.Mode == StyleConsole.Modes.Text) + return SelectForText(cli, c, options); + return Select(cli, c, options, 0); + } + + /// + /// Writes a collection of item for selecting. + /// + /// The type of data. + /// The command line interface proxy. + /// The collection data. + /// The selection display options. + /// The index of item selected. + /// The result of selection. + private static SelectionResult Select(StyleConsole cli, SelectionData collection, SelectionConsoleOptions options, int select) + { + var temp = (0, 0, 0, 0, false, false, 0, 0); + var oldSelect = select; + while (true) + { + var list = collection.ToList(); + void resetSelect() + { + if (oldSelect < 0 || oldSelect >= list.Count) return; + var h = temp.Item3 + (temp.Item5 ? 2 : 1) + (temp.Item6 ? 1 : 0); + var oldItem = list[oldSelect]; + var y2 = Math.DivRem(oldSelect % temp.Item7, temp.Item4, out var x2) - h; + x2 *= temp.Item8; + cli.MoveCursorBy(x2, y2); + RenderData(cli, oldItem, options, false, temp.Item8); + cli.MoveCursorBy(-x2 - temp.Item8, -y2); + }; + + if (temp.Item3 > 0 && select >= temp.Item1 && select < (temp.Item1 + temp.Item2)) + { + cli.BackspaceToBeginning(); + var h = temp.Item3 + (temp.Item5 ? 2 : 1) + (temp.Item6 ? 1 : 0); + if (oldSelect != select) resetSelect(); + if (select < 0 || select >= list.Count) select = 0; + var item = list[select]; + var y = Math.DivRem(select % temp.Item7, temp.Item4, out var x) - h; + x *= temp.Item8; + cli.MoveCursorBy(x, y); + RenderData(cli, item, options, true, temp.Item8); + cli.MoveCursorBy(-x - temp.Item8, -y); + RenderSelectResult(cli, item?.Title, options); + } + else + { + if (temp.Item3 > 0) + { + cli.BackspaceToBeginning(); + var h = temp.Item3 + (temp.Item5 ? 2 : 1) + (temp.Item6 ? 1 : 0); + for (var i = 0; i < h; i++) + { + cli.MoveCursorBy(0, -1); + cli.Clear(StyleConsole.RelativeAreas.Line); + } + } + + temp = RenderData(cli, list, options, select); + } + + oldSelect = select; + ConsoleKeyInfo key; + try + { + key = cli.ReadKey(); + } + catch (InvalidOperationException) + { + return SelectByManualTyping(cli, collection, options); + } + catch (IOException) + { + return SelectByManualTyping(cli, collection, options); + } + catch (SecurityException) + { + return SelectByManualTyping(cli, collection, options); + } + catch (NotSupportedException) + { + return SelectByManualTyping(cli, collection, options); + } + + switch (key.Key) + { + case ConsoleKey.Enter: + case ConsoleKey.Select: + case ConsoleKey.Spacebar: + if (select < 0 || select >= list.Count) + { + select = temp.Item1; + if (select < 0 || select >= list.Count) + select = 0; + break; + } + + var sel = list[select]; + RenderSelectResult(cli, sel?.Title, options); + cli.WriteLine(); + return new SelectionResult(sel.Title, select, sel.Data, sel.Title); + case ConsoleKey.Backspace: + case ConsoleKey.Delete: + case ConsoleKey.Clear: + cli.WriteImmediately(' '); + cli.BackspaceToBeginning(); + resetSelect(); + return SelectByManualTyping(cli, collection, options); + case ConsoleKey.Escape: + case ConsoleKey.Pause: + cli.WriteImmediately(' '); + cli.BackspaceToBeginning(); + resetSelect(); + RenderSelectResult(cli, null, options); + cli.WriteLine(); + return new SelectionResult(string.Empty, SelectionResultTypes.Canceled); + case ConsoleKey.Help: + case ConsoleKey.F1: + { + cli.BackspaceToBeginning(); + resetSelect(); + RenderSelectResult(cli, "?", options); + cli.WriteLine(); + var item = collection.Get('?', out select); + return item == null + ? new SelectionResult("?", SelectionResultTypes.Selected) + : new SelectionResult("?", select, item.Data, item.Title); + } + case ConsoleKey.F4: + if (temp.Item3 > 0) + { + cli.BackspaceToBeginning(); + var h = temp.Item3 + (temp.Item5 ? 2 : 1) + (temp.Item6 ? 1 : 0); + for (var i = 0; i < h; i++) + { + cli.MoveCursorBy(0, -1); + cli.Clear(StyleConsole.RelativeAreas.Line); + } + } + + return SelectByManualTyping(cli, collection, options, true); + case ConsoleKey.F5: + if (key.Modifiers.HasFlag(ConsoleModifiers.Control)) + select = 0; + break; + case ConsoleKey.F6: + cli.BackspaceToBeginning(); + resetSelect(); + RenderSelectResult(cli, null, options); + cli.WriteLine(); + if (key.Modifiers.HasFlag(ConsoleModifiers.Control)) + select = 0; + temp = RenderData(cli, list, options, select); + break; + case ConsoleKey.F12: + { + cli.BackspaceToBeginning(); + resetSelect(); + RenderSelectResult(cli, "?", options); + cli.WriteLine(); + var item = collection.Get('?', out select); + return item == null + ? new SelectionResult("?", SelectionResultTypes.Canceled) + : new SelectionResult("?", select, item.Data, item.Title); + } + case ConsoleKey.PageUp: + if (key.Modifiers.HasFlag(ConsoleModifiers.Control)) + select = 0; + else + select = Math.Max(0, temp.Item1 - temp.Item7); + break; + case ConsoleKey.PageDown: + if (key.Modifiers.HasFlag(ConsoleModifiers.Control)) + select = list.Count - 1; + else + select = Math.Min(list.Count - 1, temp.Item1 + temp.Item7); + break; + case ConsoleKey.UpArrow: + if (select < temp.Item4) + { + select += list.Count - (list.Count % temp.Item4); + if (select >= list.Count) select -= temp.Item4; + if (select >= list.Count) select = list.Count - 1; + else if (select < 0) select = 0; + } + else + { + select -= temp.Item4; + } + + break; + case ConsoleKey.DownArrow: + select += temp.Item4; + if (select >= list.Count) + { + select %= temp.Item4; + if (select >= list.Count) select = list.Count - 1; + } + + break; + case ConsoleKey.LeftArrow: + select--; + if (select < 0) select = list.Count - 1; + break; + case ConsoleKey.RightArrow: + select++; + if (select >= list.Count) select = 0; + break; + case ConsoleKey.Home: + if (key.Modifiers.HasFlag(ConsoleModifiers.Control)) + select = 0; + else + select = temp.Item1; + break; + case ConsoleKey.End: + if (key.Modifiers.HasFlag(ConsoleModifiers.Control)) + select = list.Count - 1; + else + select = temp.Item1 + temp.Item2 - 1; + break; + default: + { + var item = collection.Get(key.KeyChar, out select); + if (item == null) + { + select = oldSelect; + continue; + } + + cli.WriteImmediately(' '); + cli.BackspaceToBeginning(); + resetSelect(); + RenderSelectResult(cli, item.Title, options); + cli.WriteLine(); + return new SelectionResult(item.Title, select, item.Data, item.Title); + } + } + } + } + + /// + /// Tests if the input string is to get help. + /// + /// The input string. + /// true if to get help; otherwise, false. + public static bool IsAboutToGetHelp(string s) + => !string.IsNullOrEmpty(s) && s.Trim().ToLowerInvariant() switch + { + "?" or "help" or "gethelp" or "get-help" or "-?" or "/h" or "--?" or "-help" or "--help" or "/help" or "帮助" or "bangzhu" or "/bangzhu" or "--bangzhu" or "获取帮助" or "助け" or "❓" => true, + _ => false + }; + + /// + /// Tests if the input string is to exit. + /// + /// The input string. + /// true if to exit; otherwise, false. + public static bool IsAboutToExit(string s) + => !string.IsNullOrEmpty(s) && s.Trim().ToLowerInvariant() switch + { + "exit" or "quit" or "close" or "bye" or "byebye" or "goodbye" or "good-bye" or "end" or "shutdown" or "shut-down" or "关闭" or "退出" or "再见" or "guanbi" or "tuichu" or "zaijian" or "さようなら" => true, + _ => false + }; + + /// + /// Writes a collection of item for selecting. + /// + /// The type of data. + /// The command line interface proxy. + /// The collection data. + /// The selection display options. + /// The index of item selected. + /// The result of selection: offset, count, rows, columns, paging tips, customized tips, page size, item length. + private static (int, int, int, int, bool, bool, int, int) RenderData(this StyleConsole cli, List> collection, SelectionConsoleOptions options, int select) + { + var maxWidth = GetBufferSafeWidth(cli); + var itemLen = options.Column.HasValue ? (int)Math.Floor(maxWidth * 1.0 / options.Column.Value) : maxWidth; + if (options.MaxLength.HasValue) itemLen = Math.Min(options.MaxLength.Value, itemLen); + if (options.MinLength.HasValue) itemLen = Math.Max(options.MinLength.Value, itemLen); + if (itemLen > maxWidth) itemLen = maxWidth; + var columns = (int)Math.Floor(maxWidth * 1.0 / itemLen); + if (options.Column.HasValue && columns > options.Column.Value) columns = options.Column.Value; + var maxRows = 50; + try + { + maxRows = Console.BufferHeight - 5; + if (maxRows < 1) maxRows = 50; + } + catch (InvalidOperationException) + { + } + catch (IOException) + { + } + catch (SecurityException) + { + } + catch (NotSupportedException) + { + } + + if (options.MaxRow.HasValue && options.MaxRow.Value < maxRows) + maxRows = options.MaxRow.Value; + var pageSize = columns * maxRows; + var needPaging = collection.Count > pageSize; + if (select >= collection.Count) select = collection.Count - 1; + var list = collection; + var offset = 0; + if (select >= pageSize) + { + offset = (int)Math.Floor(select * 1.0 / pageSize) * pageSize; + list = list.Skip(offset).Take(pageSize).ToList(); + } + else if (needPaging) + { + list = list.Take(pageSize).ToList(); + } + + var i = offset; + var lastColIndex = columns - 1; + var rows = -1; + SelectionItem selItem = null; + foreach (var item in list) + { + if (string.IsNullOrEmpty(item.Title)) continue; + var isSel = i == select; + if (isSel) selItem = item; + RenderData(cli, item, options, isSel, itemLen); + var indexInRow = i % columns; + if (indexInRow == lastColIndex) + cli.Write(Environment.NewLine); + else if (indexInRow == 0) + rows++; + i++; + } + + if (list.Count % columns > 0) cli.Write(Environment.NewLine); + var hasPagingTips = false; + var tipsP = options.PagingTips; + if (needPaging && !string.IsNullOrEmpty(tipsP)) + { + cli.Write( + new ConsoleTextStyle( + options.PagingForegroundRgbColor, + options.PagingForegroundConsoleColor ?? options.ForegroundColor, + options.PagingBackgroundRgbColor, + options.PagingBackgroundConsoleColor ?? options.BackgroundColor), + tipsP + .Replace("{from}", (offset + 1).ToString()) + .Replace("{end}", (offset + list.Count).ToString()) + .Replace("{count}", list.Count.ToString("g")) + .Replace("{size}", pageSize.ToString("g")) + .Replace("{total}", collection.Count.ToString("g"))); + cli.Write(Environment.NewLine); + hasPagingTips = true; + } + + var hasTips = false; + if (!string.IsNullOrEmpty(options.Tips)) + { + cli.Write( + new ConsoleTextStyle( + options.TipsForegroundRgbColor, + options.TipsForegroundConsoleColor ?? options.ForegroundColor, + options.TipsBackgroundRgbColor, + options.TipsBackgroundConsoleColor ?? options.BackgroundColor), + options.Tips.Length < maxWidth - 1 + ? options.Tips + : (options.Tips.Substring(0, maxWidth - 5) + "...")); + cli.Write(Environment.NewLine); + hasTips = true; + } + + RenderSelectResult(cli, selItem?.Title, options); + return (offset, list.Count, rows, columns, hasPagingTips, hasTips, pageSize, itemLen); + } + + private static void RenderSelectResult(StyleConsole cli, string value, SelectionConsoleOptions options) + { + cli.Write( + new ConsoleTextStyle( + options.QuestionForegroundRgbColor, + options.QuestionForegroundConsoleColor ?? options.ForegroundColor, + options.QuestionBackgroundRgbColor, + options.QuestionBackgroundConsoleColor ?? options.BackgroundColor), + options.Question); + if (!string.IsNullOrWhiteSpace(value)) + cli.WriteImmediately(options.ForegroundColor, options.BackgroundColor, value); + else + cli.Flush(); + } + + private static void RenderData(StyleConsole cli, SelectionItem item, SelectionConsoleOptions options, bool isSelect, int len) + { + var style = isSelect ? new ConsoleTextStyle( + options.SelectedForegroundRgbColor, + options.SelectedForegroundConsoleColor ?? options.ForegroundColor, + options.SelectedBackgroundRgbColor, + options.SelectedBackgroundConsoleColor ?? options.BackgroundColor) : new ConsoleTextStyle( + options.ItemForegroundRgbColor, + options.ItemForegroundConsoleColor ?? options.ForegroundColor, + options.ItemBackgroundRgbColor, + options.ItemBackgroundConsoleColor ?? options.BackgroundColor); + var sb = new StringBuilder(); + var j = 0; + var maxLen = len - 1; + var curLeft = TryGetCursorLeft(cli) ?? -1; + foreach (var c in (isSelect ? options.SelectedPrefix : options.Prefix) ?? string.Empty) + { + var c2 = c; + switch (c) + { + case '\t': + case '\r': + case '\n': + j++; + c2 = ' '; + break; + case '\0': + case '\b': + continue; + default: + j += GetLetterWidth(c); + break; + } + + if (j >= maxLen) break; + sb.Append(c2); + } + + foreach (var c in item.Title) + { + var c2 = c; + switch (c) + { + case '\t': + case '\r': + case '\n': + j++; + c2 = ' '; + break; + case '\0': + case '\b': + continue; + default: + j += GetLetterWidth(c); + break; + } + + if (j >= maxLen) break; + sb.Append(c2); + } + + if (curLeft >= 0) + { + cli.WriteImmediately(style, sb); + var rest = curLeft + len - cli.CursorLeft; + if (rest > 0) + cli.WriteImmediately(style, ' ', rest); + else if (rest < 0) + cli.WriteImmediately( + new ConsoleTextStyle( + options.ItemForegroundRgbColor, + options.ItemForegroundConsoleColor ?? options.ForegroundColor, + options.ItemBackgroundRgbColor, + options.ItemBackgroundConsoleColor ?? options.BackgroundColor), + " \b"); + } + else + { + sb.Append(' ', len - j); + cli.WriteImmediately(style, sb); + } + + try + { + if (curLeft >= 0) + { + curLeft += len; + var rest = curLeft - cli.CursorLeft; + if (rest < 0) + { + cli.MoveCursorBy(rest, 0); + cli.WriteImmediately( + new ConsoleTextStyle( + options.ItemForegroundRgbColor, + options.ItemForegroundConsoleColor ?? options.ForegroundColor, + options.ItemBackgroundRgbColor, + options.ItemBackgroundConsoleColor ?? options.BackgroundColor), + " \b"); + } + else if (rest > 0) + { + cli.MoveCursorBy(rest, 0); + } + } + } + catch (InvalidOperationException) + { + } + catch (IOException) + { + } + catch (SecurityException) + { + } + catch (NotSupportedException) + { + } + } + + private static int GetLetterWidth(char c) + { + if (c < 0x2E80) return 1; + return c < 0xA500 || (c >= 0xF900 && c < 0xFB00) || (c >= 0xFE30 && c < 0xFE70) + ? 2 + : 1; + } + + /// + /// Asks to type the item keyword. + /// + /// The type of data. + /// The command line interface proxy. + /// The collection data. + /// The selection display options. + /// true if list all data before typing; otherwise, false. + /// The result of selection. + private static SelectionResult SelectByManualTyping(StyleConsole cli, SelectionData collection, SelectionConsoleOptions options, bool listAllData = false) + { + cli.BackspaceToBeginning(); + int i; + if (listAllData) + { + var maxWidth = GetBufferSafeWidth(cli); + var itemLen = options.Column.HasValue ? (int)Math.Floor(maxWidth * 1.0 / options.Column.Value) : maxWidth; + if (options.MaxLength.HasValue) itemLen = Math.Min(options.MaxLength.Value, itemLen); + if (options.MinLength.HasValue) itemLen = Math.Max(options.MinLength.Value, itemLen); + if (itemLen > maxWidth) itemLen = maxWidth; + var columns = (int)Math.Floor(maxWidth * 1.0 / itemLen); + if (options.Column.HasValue && columns > options.Column.Value) columns = options.Column.Value; + var list = collection.ToList(); + i = 0; + var lastColIndex = columns - 1; + var rows = -1; + foreach (var ele in list) + { + if (string.IsNullOrEmpty(ele.Title)) continue; + RenderData(cli, ele, options, false, itemLen); + var indexInRow = i % columns; + if (indexInRow == lastColIndex) + cli.Write(Environment.NewLine); + else if (indexInRow == 0) + rows++; + i++; + } + + if (list.Count % columns > 0) cli.Write(Environment.NewLine); + return SelectByManualTyping(cli, collection, options); + } + + cli.WriteImmediately( + new ConsoleTextStyle( + options.QuestionForegroundRgbColor, + options.QuestionForegroundConsoleColor ?? options.ForegroundColor, + options.QuestionBackgroundRgbColor, + options.QuestionBackgroundConsoleColor ?? options.BackgroundColor), + options.ManualQuestion ?? options.Question); + string s; + try + { + s = cli.ReadLine(); + } + catch (IOException) + { + return new SelectionResult(string.Empty, SelectionResultTypes.NotSupported); + } + catch (InvalidOperationException) + { + return new SelectionResult(string.Empty, SelectionResultTypes.NotSupported); + } + catch (ArgumentException) + { + return new SelectionResult(string.Empty, SelectionResultTypes.NotSupported); + } + catch (NotSupportedException) + { + return new SelectionResult(string.Empty, SelectionResultTypes.NotSupported); + } + + if (string.IsNullOrEmpty(s)) + return new SelectionResult(s, SelectionResultTypes.Canceled); + SelectionItem item = null; + if (s.Trim().Length == 1) + { + item = collection.Get(s[0], out i); + if (item != null) + return new SelectionResult(s, i, item.Data, item.Title); + } + + i = -1; + foreach (var ele in collection.ToList()) + { + i++; + if (ele.Title != s) + continue; + item = ele; + break; + } + + return item == null + ? new SelectionResult(s, SelectionResultTypes.Typed) + : new SelectionResult(s, i, item.Data, item.Title, SelectionResultTypes.Typed); + } + + /// + /// Writes a collection of item for selecting. + /// + /// The type of data. + /// The command line interface proxy. + /// The collection data. + /// The selection display options. + /// The result of selection. + private static SelectionResult SelectForText(StyleConsole cli, SelectionData collection, SelectionConsoleOptions options) + { + var list = collection.ToList(); + cli.WriteLines(list.Select((ele, i) => $"#{i + 1}\t{ele.Title}")); + var style = new ConsoleTextStyle( + options.QuestionForegroundRgbColor, + options.QuestionForegroundConsoleColor ?? options.ForegroundColor, + options.QuestionBackgroundRgbColor, + options.QuestionBackgroundConsoleColor ?? options.BackgroundColor); + cli.Write(style, options.QuestionWhenNotSupported ?? options.ManualQuestion ?? options.Question); + string text; + try + { + text = cli.ReadLine(); + } + catch (NotSupportedException) + { + return new SelectionResult(null, SelectionResultTypes.NotSupported); + } + catch (InvalidOperationException) + { + return new SelectionResult(null, SelectionResultTypes.NotSupported); + } + catch (IOException) + { + return new SelectionResult(null, SelectionResultTypes.NotSupported); + } + catch (ArgumentException) + { + return new SelectionResult(null, SelectionResultTypes.NotSupported); + } + + if (string.IsNullOrEmpty(text)) + { + cli.Write(style, options.QuestionWhenNotSupported ?? options.ManualQuestion ?? options.Question); + text = cli.ReadLine(); + if (string.IsNullOrEmpty(text)) + return new SelectionResult(text, SelectionResultTypes.Canceled); + } + + SelectionItem item = null; + int i; + if (text.Trim().Length == 1) + { + item = collection.Get(text[0], out i); + if (item != null) + return new SelectionResult(text, i, item.Data, item.Title); + } + +#pragma warning disable IDE0057 + if (text.StartsWith("#") && int.TryParse(text.Substring(1).Trim(), out i) && i > 0 && i <= list.Count) + { + item = list[i]; + return new SelectionResult(text, i, item.Data, item.Title); + } +#pragma warning restore IDE0057 + + i = -1; + foreach (var ele in list) + { + i++; + if (ele.Title != text) + continue; + item = ele; + break; + } + + if (item != null) + return new SelectionResult(text, i, item.Data, item.Title, SelectionResultTypes.Typed); + + if (int.TryParse(text.Trim(), out i) && i > 0 && i <= list.Count) + { + item = list[i]; + return new SelectionResult(text, i, item.Data, item.Title); + } + + return new SelectionResult(text, SelectionResultTypes.Typed); + } +} diff --git a/Console/Console.csproj b/Console/Console.csproj new file mode 100644 index 0000000..1a033e7 --- /dev/null +++ b/Console/Console.csproj @@ -0,0 +1,39 @@ + + + + + + net8.0;net6.0;net48;net462;net461 + Trivial.Console + Trivial + Trivial.Console + The rich user interface console controls and utilities. + https://github.com/nuscien/trivial/wiki/console + true + true + snupkg + cmd.png + https://github.com/nuscien/trivial/raw/master/Materials/logo.png + console + + + + + + + + + True + True + Resource.resx + + + + + + ResXFileCodeGenerator + Resource.Designer.cs + + + + diff --git a/Console/README.md b/Console/README.md new file mode 100644 index 0000000..fb73391 --- /dev/null +++ b/Console/README.md @@ -0,0 +1,122 @@ +# [Trivial.Console](https://trivial.kingcean.net/cmdline) + +This library includes a lot of useful rich command line controls. + +## Import + +Add following namespace to your code file to use. + +```csharp +using Trivial.CommandLine; +``` + +## Select + +A beautiful list or table with customized style to the standard output stream +so that user just need press the arrow buttons and `ENTER` in keyboard to select. + +```csharp +// Create an instance for adding items and setting options. +var col = new Trivial.Collection.SelectionData(); + +// Add some items. For each item, you can set a hotkey, a display name and the data. +col.Add('a', "char a", "a"); +col.Add('b', "char b", "b"); +for (var i = 0; i < 120; i++) +{ + col.Add("num " + i, i.ToString()); +} + +// Create an options for display. +var options = new SelectionOptions +{ + // You can define a question string after the list. + Question = "Please select one: ", + + // We can define the colors of the item selected. + SelectedForegroundConsoleColor = ConsoleColor.White, + SelectedBackgroundConsoleColor = ConsoleColor.Blue, + + // The selected item will also be displayed after the question string. + // So you can define its color. + ItemForegroundConsoleColor = ConsoleColor.Cyan, + + // At the end of the list, the tips will be displayed before user press any key. + // There is a default value and you can customize it. + // And you can disable it by set it as null. + Tips = "Tips: You can use arrow key to select and press ENTER key to continue.", + + // Then you can define its color. + TipsForegroundConsoleColor = ConsoleColor.Yellow, + + // You can define the prefix for the item and the one selected. + SelectedPrefix = "> ", + Prefix = " ", + + // You can define the column count for the list. + Column = 5, + + // You can define the maximum rows to displayed. + // A paging will be displayed if the count of the list is greater than it. + MaxRow = 10, + + // Press ESC can cancel this selection. + // But you can enable the manual way by set a manual question + // so that user can type the words directly. + ManualQuestion = "Type: " +}; + +// Write it to the standard output stream and wait for user selection. +var result = DefaultConsole.Select(col, options); + +// You can get the result. +DefaultConsole.WriteLine("The result is {0}.", result.Value); +``` + +## Progress + +A progress bar in terminal that can present the latest state during the specific task running. + +```csharp +// Define an options that you can custom the style. +var progressStyle = new ConsoleProgressStyle +{ + ValueConsoleColor = ConsoleColor.White +}; + +// Ouput the component in console and get the progress instance to update. +var progress = DefaultConsole.WriteLine(progressStyle, "Processing"); + +// A time-consuming work here. +for (var i = 0; i <= 50; i++) +{ + await Task.Delay(10); + + // And report the progress updated. + progress.Report(0.02 * i); +} +``` + +## JSON + +Following is a sample to format JSON into terminal. + +```csharp +var json = new Trivial.Text.JsonObjectNode(); +// and then add some properties to json. +DefaultConsole.WriteLine(json); +``` + +## Linear gradient + +Output a string with linear gradient. + +```csharp +DefaultConsole.WriteLine(new LinearGradientConsoleStyle( + ConsoleColor.Gray, // Fallback color. + Color.FromArgb(15, 250, 250), // From color. + Color.FromArgb(85, 168, 255)) // To color + { + Bold = true // Additional font style + },"Trivial Sample"); +``` diff --git a/Demo/Demo.csproj b/Demo/Demo.csproj index d1dd38b..2de61c0 100644 --- a/Demo/Demo.csproj +++ b/Demo/Demo.csproj @@ -1,15 +1,13 @@  + + + WinExe net7.0-windows10.0.19041.0 10.0.17763.0 - 7.1.0 - 7.1.0.0 - 7.1.0.0 Trivial.WindowsKit.Demo Trivial.WindowsKit.Demo - Kingcean Tuan - Nanchang Jinchen Software Co., Ltd. Trivial.Demo app.manifest ..\Materials\WinKit.ico @@ -18,17 +16,10 @@ win10-$(Platform).pubxml true true - True - ..\Trivial.snk - - - - ..\bin\Debug\ - ..\bin\$(Configuration)\$(TargetFramework)\Trivial.WindowsKit.Demo.xml - - ..\bin\Release\ + + ..\bin\$(Configuration)\ ..\bin\$(Configuration)\$(TargetFramework)\Trivial.WindowsKit.Demo.xml diff --git a/Materials/cmd.png b/Materials/cmd.png new file mode 100644 index 0000000..1909b97 Binary files /dev/null and b/Materials/cmd.png differ diff --git a/Materials/web.png b/Materials/web.png new file mode 100644 index 0000000..86ea5b0 Binary files /dev/null and b/Materials/web.png differ diff --git a/Web/README.md b/Web/README.md new file mode 100644 index 0000000..1724f4c --- /dev/null +++ b/Web/README.md @@ -0,0 +1,19 @@ +# [Trivial.Web](https://trivial.kingcean.net/web/controller) + +ASP.NET Core extensions. + +## Import + +Add following namespace to your code file to use. + +```csharp +using Trivial.Web; +``` + +## Request + +Some extension methods to get the field from HTTP request. + +## Action result + +Some extension methods to convert some modes to `ActionResult`. diff --git a/Web/Web.csproj b/Web/Web.csproj new file mode 100644 index 0000000..bff6b4d --- /dev/null +++ b/Web/Web.csproj @@ -0,0 +1,32 @@ + + + + + + net8.0;net6.0; + Trivial.Web + Trivial + Trivial.Web + A library for web API. + true + true + snupkg + web.png + https://github.com/nuscien/trivial/raw/master/Materials/logo.png + web mvc + + + + ..\bin\$(Configuration)\ + ..\bin\$(Configuration)\$(TargetFramework)\Trivial.Web.xml + + + + + + + + + + + diff --git a/Web/Web/ControllerExtensions.cs b/Web/Web/ControllerExtensions.cs new file mode 100644 index 0000000..8f556aa --- /dev/null +++ b/Web/Web/ControllerExtensions.cs @@ -0,0 +1,913 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Security; +using System.Security.Claims; +using System.Text; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; + +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using Microsoft.Net.Http.Headers; +using Trivial.Collection; +using Trivial.Data; +using Trivial.Net; +using Trivial.Security; +using Trivial.Text; + +namespace Trivial.Web; + +/// +/// The controller extensions. +/// +public static class ControllerExtensions +{ + private const string NullString = "null"; + private static MethodInfo method; + + /// + /// Gets the first string value. + /// + /// The form collection. + /// The key. + /// true if need trim; otherwise, false. + /// The string value; or null, if non-exist. + public static string GetFirstStringValue(this IFormCollection request, string key, bool trim = false) + { + var col = request[key]; + string str = null; + if (trim) + { + foreach (var ele in col) + { + if (ele == null) continue; + var s = ele.Trim(); + if (s.Length == 0) + { + str ??= string.Empty; + continue; + } + + str = s; + } + } + else + { + foreach (var ele in col) + { + if (ele == null) continue; + if (ele.Length == 0) + { + str ??= string.Empty; + continue; + } + + str = ele; + } + } + + return str; + } + + /// + /// Gets the merged string value. + /// + /// The form collection. + /// The key. + /// The splitter charactor. + /// true if need trim; otherwise, false. + /// The string value; or null, if non-exist. + public static string GetMergedStringValue(this IFormCollection request, string key, char split = ',', bool trim = false) + { + IEnumerable col = request[key]; + if (trim) col = col.Select(ele => ele?.Trim()).Where(ele => !string.IsNullOrEmpty(ele)); + return string.Join(split, col); + } + + /// + /// Gets the first string value. + /// + /// The query collection. + /// The key. + /// true if need trim; otherwise, false. + /// The string value; or null, if non-exist. + public static string GetFirstStringValue(this IQueryCollection request, string key, bool trim = false) + { + var col = request[key]; + string str = null; + if (trim) + { + foreach (var ele in col) + { + if (ele == null) continue; + var s = ele.Trim(); + if (s.Length == 0) + { + str ??= string.Empty; + continue; + } + + str = s; + } + } + else + { + foreach (var ele in col) + { + if (ele == null) continue; + if (ele.Length == 0) + { + str ??= string.Empty; + continue; + } + + str = ele; + } + } + + return str; + } + + /// + /// Gets the merged string value. + /// + /// The query collection. + /// The key. + /// The splitter charactor. + /// true if need trim; otherwise, false. + /// The string value; or null, if non-exist. + public static string GetMergedStringValue(this IQueryCollection request, string key, char split = ',', bool trim = false) + { + IEnumerable col = request[key]; + if (trim) col = col.Select(ele => ele?.Trim()).Where(ele => !string.IsNullOrEmpty(ele)); + return string.Join(split, col); + } + + /// + /// Gets the integer value. + /// + /// The query collection. + /// The key. + /// The number value; or null, if non-exist or parse failed. + public static int? TryGetInt32Value(this IQueryCollection request, string key) + { + var s = request[key].Select(ele => ele?.Trim()).FirstOrDefault(ele => !string.IsNullOrEmpty(ele)); + if (Maths.Numbers.TryParseToInt32(s, 10, out var r)) return r; + return null; + } + + /// + /// Gets the integer value. + /// + /// The query collection. + /// The key. + /// The output result. + /// true if parse succeeded; otherwise, false. + public static bool TryGetInt32Value(this IQueryCollection request, string key, out int result) + { + var r = TryGetInt32Value(request, key); + if (r.HasValue) + { + result = r.Value; + return true; + } + + result = default; + return false; + } + + /// + /// Gets the integer value. + /// + /// The query collection. + /// The key. + /// The number value; or null, if non-exist or parse failed. + public static long? TryGetInt64Value(this IQueryCollection request, string key) + { + var s = request[key].Select(ele => ele?.Trim()).FirstOrDefault(ele => !string.IsNullOrEmpty(ele)); + if (Maths.Numbers.TryParseToInt64(s, 10, out var r)) return r; + return null; + } + + /// + /// Gets the integer value. + /// + /// The query collection. + /// The key. + /// The output result. + /// true if parse succeeded; otherwise, false. + public static bool TryGetInt64Value(this IQueryCollection request, string key, out long result) + { + var r = TryGetInt64Value(request, key); + if (r.HasValue) + { + result = r.Value; + return true; + } + + result = default; + return false; + } + + /// + /// Gets the floating value. + /// + /// The query collection. + /// The key. + /// The number value; or null, if non-exist or parse failed. + public static float? TryGetSingleValue(this IQueryCollection request, string key) + { + var s = request[key].Select(ele => ele?.Trim()).FirstOrDefault(ele => !string.IsNullOrEmpty(ele)); + if (float.TryParse(s, out var r)) return r; + return null; + } + + /// + /// Gets the floating value. + /// + /// The query collection. + /// The key. + /// The output result. + /// true if parse succeeded; otherwise, false. + public static bool TryGetSingleValue(this IQueryCollection request, string key, out float result) + { + var r = TryGetSingleValue(request, key); + if (r.HasValue) + { + result = r.Value; + return true; + } + + result = default; + return false; + } + + /// + /// Gets the floating value. + /// + /// The query collection. + /// The key. + /// The number value; or null, if non-exist or parse failed. + public static decimal? TryGetDecimalValue(this IQueryCollection request, string key) + { + var s = request[key].Select(ele => ele?.Trim()).FirstOrDefault(ele => !string.IsNullOrEmpty(ele)); + if (decimal.TryParse(s, out var r)) return r; + return null; + } + + /// + /// Gets the floating value. + /// + /// The query collection. + /// The key. + /// The output result. + /// true if parse succeeded; otherwise, false. + public static bool TryGetDecimalValue(this IQueryCollection request, string key, out decimal result) + { + var r = TryGetDecimalValue(request, key); + if (r.HasValue) + { + result = r.Value; + return true; + } + + result = default; + return false; + } + + /// + /// Gets the floating value. + /// + /// The query collection. + /// The key. + /// The number value; or null, if non-exist or parse failed. + public static double? TryGetDoubleValue(this IQueryCollection request, string key) + { + var s = request[key].Select(ele => ele?.Trim()).FirstOrDefault(ele => !string.IsNullOrEmpty(ele)); + if (double.TryParse(s, out var r)) return r; + return null; + } + + /// + /// Gets the floating value. + /// + /// The query collection. + /// The key. + /// The output result. + /// true if parse succeeded; otherwise, false. + public static bool TryGetDoubleValue(this IQueryCollection request, string key, out double result) + { + var r = TryGetDoubleValue(request, key); + if (r.HasValue) + { + result = r.Value; + return true; + } + + result = default; + return false; + } + + /// + /// Tries to get a property as boolean. + /// + /// The query. + /// The property key. + /// true if it is true; or false, if it is false; or null, if not supported. + public static bool? TryGetBoolean(this IQueryCollection request, string key) + { + var plain = request?.GetFirstStringValue(key, true)?.ToLowerInvariant(); + var isPlain = JsonBooleanNode.TryParse(plain); + return isPlain?.Value; + } + + /// + /// Tries to get a property as boolean. + /// + /// The query. + /// The property key. + /// The result. + /// true if parse succeeded; otherwise, false. + public static bool TryGetBoolean(this IQueryCollection request, string key, out bool result) + { + var plain = request?.GetFirstStringValue(key, true)?.ToLowerInvariant(); + var isPlain = JsonBooleanNode.TryParse(plain); + if (isPlain == null) + { + result = false; + return false; + } + + result = isPlain.Value; + return true; + } + + /// + /// Gets the query data. + /// + /// The HTTP request. + /// The optional encoding. + /// The string value; or null, if non-exist. + public static async Task ReadBodyAsQueryDataAsync(this HttpRequest request, Encoding encoding = null) + { + if (request == null || request.Body == null) return null; + encoding ??= Encoding.UTF8; + using var reader = new StreamReader(request.Body, encoding); + var query = await reader.ReadToEndAsync(); + var q = new QueryData(); + q.ParseSet(query, false, encoding); + return q; + } + + /// + /// Gets the JSON object from body. + /// + /// The HTTP request. + /// The token to monitor for cancellation requests. + /// A JSON object instance; or null, if no body. + /// json does not represent a valid single JSON object. + /// options contains unsupported options. + public static Task ReadBodyAsJsonAsync(this HttpRequest request, CancellationToken cancellationToken) + { + if (request == null || request.Body == null) return null; + return JsonObjectNode.ParseAsync(request.Body, default, cancellationToken); + } + + /// + /// Gets the JSON object from body. + /// + /// The HTTP request. + /// Options to control the reader behavior during parsing. + /// The token to monitor for cancellation requests. + /// A JSON object instance; or null, if no body. + /// json does not represent a valid single JSON object. + /// options contains unsupported options. + public static Task ReadBodyAsJsonAsync(this HttpRequest request, JsonDocumentOptions options, CancellationToken cancellationToken) + { + if (request == null || request.Body == null) return null; + return JsonObjectNode.ParseAsync(request.Body, options, cancellationToken); + } + + /// + /// Gets the JSON array from body. + /// + /// The HTTP request. + /// The token to monitor for cancellation requests. + /// A JSON array instance; or null, if no body. + /// json does not represent a valid single JSON object. + /// options contains unsupported options. + public static Task ReadBodyAsJsonArrayAsync(this HttpRequest request, CancellationToken cancellationToken) + { + if (request == null || request.Body == null) return null; + return JsonArrayNode.ParseAsync(request.Body, default, cancellationToken); + } + + /// + /// Gets the JSON array from body. + /// + /// The HTTP request. + /// Options to control the reader behavior during parsing. + /// The token to monitor for cancellation requests. + /// A JSON array instance; or null, if no body. + /// json does not represent a valid single JSON object. + /// options contains unsupported options. + public static Task ReadBodyAsJsonArrayAsync(this HttpRequest request, JsonDocumentOptions options, CancellationToken cancellationToken) + { + if (request == null || request.Body == null) return null; + return JsonArrayNode.ParseAsync(request.Body, options, cancellationToken); + } + + /// + /// Convert to an action result. + /// + /// The value. + /// The action result. + public static ActionResult ToActionResult(this ChangingResultInfo value) + { + if (value == null) return new NotFoundResult(); + var ex = value.GetException(); + var status = ex != null ? (GetStatusCode(ex) ?? 500) : 200; + if (status >= 300) + { + status = value.ErrorCode switch + { + ChangeErrorKinds.NotFound => 404, + ChangeErrorKinds.Busy => 503, + ChangeErrorKinds.Unsupported => 501, + ChangeErrorKinds.Conflict => 409, + _ => status + }; + } + + return new JsonResult(value) + { + StatusCode = status + }; + } + + /// + /// Convert to an action result. + /// + /// The value. + /// The action result. + public static ActionResult ToActionResult(this ChangeMethods value) + => ToActionResult(new ChangingResultInfo(value)); + + /// + /// Convert to an action result. + /// + /// The value. + /// The error message. + /// The action result. + public static ActionResult ToActionResult(this ChangeErrorKinds value, string message) + => ToActionResult(new ChangingResultInfo(value, message)); + + /// + /// Convert to an action result. + /// + /// The value. + /// The action result. + public static ContentResult ToActionResult(this JsonObjectNode value) + => new() + { + ContentType = WebFormat.JsonMIME, + StatusCode = 200, + Content = value?.ToString() ?? NullString + }; + + /// + /// Convert to an action result. + /// + /// The value. + /// The action result. + public static ContentResult ToActionResult(this JsonArrayNode value) + => new() + { + ContentType = WebFormat.JsonMIME, + StatusCode = 200, + Content = value?.ToString() ?? NullString + }; + + /// + /// Convert to an action result. + /// + /// The value. + /// The action result. + public static ContentResult ToActionResult(this IJsonObjectHost value) + => new() + { + ContentType = WebFormat.JsonMIME, + StatusCode = 200, + Content = value?.ToJson()?.ToString() ?? NullString + }; + + /// + /// Convert to an action result. + /// + /// The value. + /// The action result. + public static ContentResult ToActionResult(System.Text.Json.Nodes.JsonObject value) + => new() + { + ContentType = WebFormat.JsonMIME, + StatusCode = 200, + Content = value?.ToJsonString() ?? NullString + }; + + /// + /// Convert to an action result. + /// + /// The value. + /// The action result. + public static ContentResult ToActionResult(System.Text.Json.Nodes.JsonArray value) + => new() + { + ContentType = WebFormat.JsonMIME, + StatusCode = 200, + Content = value?.ToJsonString() ?? NullString + }; + + /// + /// Converts an exception to action result with exception message. + /// + /// The controller. + /// The exception. + /// true if return null for unknown exception; otherwise, false. + /// The action result. + public static ActionResult ExceptionResult(this ControllerBase controller, Exception ex, bool ignoreUnknownException = false) + { + if (ex == null) return controller.StatusCode(500); + var result = new ErrorMessageResult(ex); + var status = GetStatusCode(ex, ignoreUnknownException); + if (!status.HasValue) return null; + return new JsonResult(result) + { + StatusCode = status.Value + }; + } + + /// + /// Converts an exception to action result with exception message. + /// + /// The controller. + /// The exception. + /// The error code. + /// true if return null for unknown exception; otherwise, false. + /// The action result. + public static ActionResult ExceptionResult(this ControllerBase controller, Exception ex, string errorCode, bool ignoreUnknownException = false) + { + if (ex == null) return controller.StatusCode(500); + var result = new ErrorMessageResult(ex, errorCode); + var status = GetStatusCode(ex, ignoreUnknownException); + if (!status.HasValue) return null; + return new JsonResult(result) + { + StatusCode = status.Value + }; + } + + /// + /// Converts an exception to action result with exception message. + /// + /// The controller. + /// The HTTP status code. + /// The exception. + /// The action result. + public static ActionResult ExceptionResult(this ControllerBase controller, int status, Exception ex) + { + if (ex == null) return controller.StatusCode(status); + var result = new ErrorMessageResult(ex); + return new JsonResult(result) + { + StatusCode = status + }; + } + + /// + /// Converts an exception to action result with exception message. + /// + /// The controller. + /// The HTTP status code. + /// The exception. + /// The error code. + /// The action result. + public static ActionResult ExceptionResult(this ControllerBase controller, int status, Exception ex, string errorCode) + { + if (ex == null) return controller.StatusCode(status); + var result = new ErrorMessageResult(ex, errorCode); + return new JsonResult(result) + { + StatusCode = status + }; + } + + /// + /// Converts an exception to action result with exception message. + /// + /// The controller. + /// The HTTP status code. + /// The exception message. + /// The optional error code. + /// The action result. +#pragma warning disable IDE0060 + public static ActionResult ExceptionResult(this ControllerBase controller, int status, string ex, string errorCode = null) +#pragma warning restore IDE0060 + { + var result = new ErrorMessageResult(ex, errorCode); + return new JsonResult(result) + { + StatusCode = status + }; + } + + /// + /// Gets the query data instance. + /// + /// The request. + /// The query data instance. + public static QueryData GetQueryData(this IQueryCollection request) + { + if (request == null) return null; + var q = new QueryData(); + foreach (var item in request) + { + q.Add(item.Key, item.Value as IEnumerable); + } + + q.Remove("random"); + return q; + } + + /// + /// Signs in. + /// + /// The controller. + /// The token request route. + /// The token maker. + /// The login response. + public static async Task SignInAsync(ControllerBase controller, TokenRequestRoute route, Func tokenMaker) where TToken : TokenInfo + { + TToken result; + try + { + if (controller is null) return default; + var stream = controller.Request.Body; + if (stream is null) + { + result = tokenMaker(); + result.ErrorCode = TokenInfo.ErrorCodeConstants.InvalidRequest; + result.ErrorDescription = "The body was empty."; + return default; + } + + tokenMaker ??= () => Activator.CreateInstance(); + string input; + using (var reader = new StreamReader(controller.Request.Body, Encoding.UTF8)) + { + input = await reader.ReadToEndAsync(); + } + + var r = await route.SignInAsync(input); + result = r?.ItemSelected as TToken; + if (result != null) return result; + result = tokenMaker(); + result.ErrorCode = TokenInfo.ErrorCodeConstants.InvalidRequest; + result.ErrorDescription = "Cannot sign in."; + return result; + } + catch (ArgumentException ex) + { + result = tokenMaker(); + result.ErrorCode = TokenInfo.ErrorCodeConstants.InvalidRequest; + result.ErrorDescription = ex.Message; + return result; + } + catch (IOException ex) + { + result = tokenMaker(); + result.ErrorCode = TokenInfo.ErrorCodeConstants.ServerError; + result.ErrorDescription = ex.Message; + return result; + } + } + + /// + /// Converts a JSON format string to a result. + /// + /// The JSON format string. + /// The content result converted. + public static ContentResult JsonStringResult(string json) + => new() + { + StatusCode = 200, + ContentType = WebFormat.JsonMIME, + Content = json + }; + + /// + /// Creates a file result. + /// + /// The source. + /// The entity tag associated with the file. + /// The optional MIME content type; or null, if detects automatically. + /// A file result; or null, if non-exists. + public static FileStreamResult FileResult(FileInfo source, EntityTagHeaderValue entityTag = null, string mime = null) + { + if (source == null || !source.Exists) return null; + mime ??= GetByFileExtension(source.Extension, WebFormat.StreamMIME); + var result = new FileStreamResult(source.OpenRead(), mime) + { + LastModified = source.LastWriteTime, + FileDownloadName = source.Name, + EntityTag = entityTag + }; + return result; + } + + /// + /// Creates a file result. + /// + /// The source. + /// The entity tag associated with the file. + /// The optional MIME content type; or null, if detects automatically. + /// A file result; or null, if non-exists. + public static FileStreamResult FileResult(IO.BaseFileReferenceInfo source, EntityTagHeaderValue entityTag = null, string mime = null) + => FileResult(source?.Source, entityTag, mime); + + /// + /// Creates a file result. + /// + /// The source. + /// The file name used for downloading. + /// The last modified information associated with the file. + /// The optional MIME content type; or null, if detects automatically. + /// A file result; or null, if non-exists. + public static FileStreamResult FileResult(Stream source, string downloadName, DateTimeOffset? lastModified = null, string mime = null) + => FileResult(source, downloadName, null, lastModified, mime); + + /// + /// Creates a file result. + /// + /// The source. + /// The file name used for downloading. + /// The entity tag associated with the file. + /// The last modified information associated with the file. + /// The optional MIME content type; or null, if detects automatically. + /// A file result; or null, if stream source is null. + public static FileStreamResult FileResult(Stream source, string downloadName, EntityTagHeaderValue entityTag, DateTimeOffset? lastModified = null, string mime = null) + { + if (source == null) return null; + if (string.IsNullOrEmpty(mime) && downloadName?.Contains('.') == true) + { + var ext = downloadName.Trim().Split('.').LastOrDefault(); + if (string.IsNullOrEmpty(ext)) + mime = WebFormat.StreamMIME; + else + mime = GetByFileExtension("." + ext, WebFormat.StreamMIME); + } + + var result = new FileStreamResult(source, mime) + { + LastModified = lastModified, + EntityTag = entityTag + }; + if (!string.IsNullOrWhiteSpace(downloadName)) + result.FileDownloadName = downloadName; + return result; + } + + /// + /// Creates a file result. + /// + /// The source. + /// The file name used for downloading. + /// true if enables range requests processing; otherwise, false. + /// The entity tag associated with the file. + /// The last modified information associated with the file. + /// The optional MIME content type; or null, if detects automatically. + /// A file result; or null, if stream source is null. + public static FileStreamResult FileResult(Stream source, string downloadName, EntityTagHeaderValue entityTag, bool enableRangeProcessing, DateTimeOffset? lastModified = null, string mime = null) + { + var result = FileResult(source, downloadName, entityTag, lastModified, mime); + if (result == null) return null; + result.EnableRangeProcessing = enableRangeProcessing; + return result; + } + + /// + /// Creates a file result. + /// + /// The assembly with the embedded file. + /// The sub path of the embedded file. + /// The file name used for downloading. + /// The entity tag associated with the file. + /// The optional MIME content type; or null, if detects automatically. + /// A file result; or null, if non-exists. + public static FileStreamResult FileResult(Assembly assembly, string subPath, string downloadName, EntityTagHeaderValue entityTag, string mime = null) + { + if (string.IsNullOrWhiteSpace(subPath)) return null; + if (assembly == null) + assembly = Assembly.GetExecutingAssembly(); + var stream = assembly.GetManifestResourceStream(subPath); + if (stream == null) return null; + var file = IO.FileSystemInfoUtility.TryGetFileInfo(assembly.Location); + var lastModified = file?.LastWriteTime; + if (string.IsNullOrWhiteSpace(downloadName)) + downloadName = subPath.Split(new[] { '\\', '/' }).LastOrDefault(); + return FileResult(stream, downloadName, entityTag, lastModified, mime); + } + + /// + /// Creates a file result. + /// + /// The assembly with the embedded file. + /// The sub path of the embedded file. + /// The entity tag associated with the file. + /// The optional MIME content type; or null, if detects automatically. + /// A file result; or null, if non-exists. + public static FileStreamResult FileResult(Assembly assembly, string subPath, EntityTagHeaderValue entityTag, string mime = null) + => FileResult(assembly, subPath, null, entityTag, mime); + + /// + /// Gets the status code. + /// + /// The exception. + /// true if return null for unknown exception; otherwise, false. + /// The action result. + private static int? GetStatusCode(Exception ex, bool ignoreUnknownException = false) + { + if (ex == null) return 500; + if (ex.InnerException != null) + { + if (ex is AggregateException) + { + ex = ex.InnerException; + } + else if (ex is InvalidOperationException) + { + ex = ex.InnerException; + ignoreUnknownException = false; + } + } + + if (ex is SecurityException) return 403; + else if (ex is UnauthorizedAccessException) return 401; + else if (ex is NotSupportedException) return 502; + else if (ex is NotImplementedException) return 502; + else if (ex is TimeoutException) return 408; + else if (ex is OperationCanceledException) return 408; + if (ignoreUnknownException && !( + ex is InvalidOperationException + || ex is ArgumentException + || ex is NullReferenceException + || ex is System.Data.Common.DbException + || ex is JsonException + || ex is System.Runtime.Serialization.SerializationException + || ex is FailedHttpException + || ex is IOException + || ex is ApplicationException + || ex is InvalidCastException + || ex is FormatException + || ex is ArithmeticException + || ex is ExternalException + || ex is InvalidDataException)) return null; + return 500; + } + + /// + /// Gets the MIME content type by file extension part. + /// + /// The file extension. + /// The default MIME content type. + /// The MIME content type. + private static string GetByFileExtension(string fileExtension, string defaultMime) + { + if (string.IsNullOrWhiteSpace(fileExtension)) return null; + if (method == null) + { + method = typeof(WebFormat).GetMethod("GetMime", BindingFlags.Static | BindingFlags.NonPublic, null, new Type[] { typeof(string) }, null); + if (method == null) + method = typeof(WebFormat).GetMethod("GetMime", BindingFlags.Static | BindingFlags.Public, null, new Type[] { typeof(string) }, null); + if (method == null) + return defaultMime; + } + + var r = method.Invoke(null, new object[] { fileExtension }); + if (r == null) return defaultMime; + try + { + return (string)r; + } + catch (InvalidCastException) + { + } + + return defaultMime; + } +} diff --git a/WinKit.sln b/WinKit.sln index caa356e..91828d0 100644 --- a/WinKit.sln +++ b/WinKit.sln @@ -7,6 +7,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Common", "Common\Common.csp EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{03B291A4-6F6F-4A26-9772-A1DE02B5EF48}" ProjectSection(SolutionItems) = preProject + Common.props = Common.props LICENSE = LICENSE Materials\logo.png = Materials\logo.png README.md = README.md @@ -17,6 +18,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Demo", "Demo\Demo.csproj", EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cli", "Cli\Cli.csproj", "{B356FDA6-4A2D-4FC6-83DC-EB2E576DAF2C}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Web", "Web\Web.csproj", "{FD8946C4-AD7C-47F3-84F9-BB567E0F14A9}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Console", "Console\Console.csproj", "{99AC4C2E-6095-444F-AB5D-0E4E65C46B85}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -101,6 +106,46 @@ Global {B356FDA6-4A2D-4FC6-83DC-EB2E576DAF2C}.Release|x64.Build.0 = Release|Any CPU {B356FDA6-4A2D-4FC6-83DC-EB2E576DAF2C}.Release|x86.ActiveCfg = Release|Any CPU {B356FDA6-4A2D-4FC6-83DC-EB2E576DAF2C}.Release|x86.Build.0 = Release|Any CPU + {FD8946C4-AD7C-47F3-84F9-BB567E0F14A9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FD8946C4-AD7C-47F3-84F9-BB567E0F14A9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FD8946C4-AD7C-47F3-84F9-BB567E0F14A9}.Debug|ARM.ActiveCfg = Debug|Any CPU + {FD8946C4-AD7C-47F3-84F9-BB567E0F14A9}.Debug|ARM.Build.0 = Debug|Any CPU + {FD8946C4-AD7C-47F3-84F9-BB567E0F14A9}.Debug|arm64.ActiveCfg = Debug|Any CPU + {FD8946C4-AD7C-47F3-84F9-BB567E0F14A9}.Debug|arm64.Build.0 = Debug|Any CPU + {FD8946C4-AD7C-47F3-84F9-BB567E0F14A9}.Debug|x64.ActiveCfg = Debug|Any CPU + {FD8946C4-AD7C-47F3-84F9-BB567E0F14A9}.Debug|x64.Build.0 = Debug|Any CPU + {FD8946C4-AD7C-47F3-84F9-BB567E0F14A9}.Debug|x86.ActiveCfg = Debug|Any CPU + {FD8946C4-AD7C-47F3-84F9-BB567E0F14A9}.Debug|x86.Build.0 = Debug|Any CPU + {FD8946C4-AD7C-47F3-84F9-BB567E0F14A9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FD8946C4-AD7C-47F3-84F9-BB567E0F14A9}.Release|Any CPU.Build.0 = Release|Any CPU + {FD8946C4-AD7C-47F3-84F9-BB567E0F14A9}.Release|ARM.ActiveCfg = Release|Any CPU + {FD8946C4-AD7C-47F3-84F9-BB567E0F14A9}.Release|ARM.Build.0 = Release|Any CPU + {FD8946C4-AD7C-47F3-84F9-BB567E0F14A9}.Release|arm64.ActiveCfg = Release|Any CPU + {FD8946C4-AD7C-47F3-84F9-BB567E0F14A9}.Release|arm64.Build.0 = Release|Any CPU + {FD8946C4-AD7C-47F3-84F9-BB567E0F14A9}.Release|x64.ActiveCfg = Release|Any CPU + {FD8946C4-AD7C-47F3-84F9-BB567E0F14A9}.Release|x64.Build.0 = Release|Any CPU + {FD8946C4-AD7C-47F3-84F9-BB567E0F14A9}.Release|x86.ActiveCfg = Release|Any CPU + {FD8946C4-AD7C-47F3-84F9-BB567E0F14A9}.Release|x86.Build.0 = Release|Any CPU + {99AC4C2E-6095-444F-AB5D-0E4E65C46B85}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {99AC4C2E-6095-444F-AB5D-0E4E65C46B85}.Debug|Any CPU.Build.0 = Debug|Any CPU + {99AC4C2E-6095-444F-AB5D-0E4E65C46B85}.Debug|ARM.ActiveCfg = Debug|Any CPU + {99AC4C2E-6095-444F-AB5D-0E4E65C46B85}.Debug|ARM.Build.0 = Debug|Any CPU + {99AC4C2E-6095-444F-AB5D-0E4E65C46B85}.Debug|arm64.ActiveCfg = Debug|Any CPU + {99AC4C2E-6095-444F-AB5D-0E4E65C46B85}.Debug|arm64.Build.0 = Debug|Any CPU + {99AC4C2E-6095-444F-AB5D-0E4E65C46B85}.Debug|x64.ActiveCfg = Debug|Any CPU + {99AC4C2E-6095-444F-AB5D-0E4E65C46B85}.Debug|x64.Build.0 = Debug|Any CPU + {99AC4C2E-6095-444F-AB5D-0E4E65C46B85}.Debug|x86.ActiveCfg = Debug|Any CPU + {99AC4C2E-6095-444F-AB5D-0E4E65C46B85}.Debug|x86.Build.0 = Debug|Any CPU + {99AC4C2E-6095-444F-AB5D-0E4E65C46B85}.Release|Any CPU.ActiveCfg = Release|Any CPU + {99AC4C2E-6095-444F-AB5D-0E4E65C46B85}.Release|Any CPU.Build.0 = Release|Any CPU + {99AC4C2E-6095-444F-AB5D-0E4E65C46B85}.Release|ARM.ActiveCfg = Release|Any CPU + {99AC4C2E-6095-444F-AB5D-0E4E65C46B85}.Release|ARM.Build.0 = Release|Any CPU + {99AC4C2E-6095-444F-AB5D-0E4E65C46B85}.Release|arm64.ActiveCfg = Release|Any CPU + {99AC4C2E-6095-444F-AB5D-0E4E65C46B85}.Release|arm64.Build.0 = Release|Any CPU + {99AC4C2E-6095-444F-AB5D-0E4E65C46B85}.Release|x64.ActiveCfg = Release|Any CPU + {99AC4C2E-6095-444F-AB5D-0E4E65C46B85}.Release|x64.Build.0 = Release|Any CPU + {99AC4C2E-6095-444F-AB5D-0E4E65C46B85}.Release|x86.ActiveCfg = Release|Any CPU + {99AC4C2E-6095-444F-AB5D-0E4E65C46B85}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/docs/assets/badge_ASPNET_5_0.svg b/docs/assets/badge_ASPNET_5_0.svg new file mode 100644 index 0000000..3103b53 --- /dev/null +++ b/docs/assets/badge_ASPNET_5_0.svg @@ -0,0 +1 @@ +ASP.NET Core: 5.0ASP.NET Core5.0 \ No newline at end of file diff --git a/docs/assets/badge_ASPNET_6_0.svg b/docs/assets/badge_ASPNET_6_0.svg new file mode 100644 index 0000000..b4f0f48 --- /dev/null +++ b/docs/assets/badge_ASPNET_6_0.svg @@ -0,0 +1 @@ +ASP.NET Core: 6.0ASP.NET Core6.0 \ No newline at end of file diff --git a/docs/assets/badge_ASPNET_8_0.svg b/docs/assets/badge_ASPNET_8_0.svg new file mode 100644 index 0000000..95d324d --- /dev/null +++ b/docs/assets/badge_ASPNET_8_0.svg @@ -0,0 +1 @@ +ASP.NET: 8.0ASP.NET8.0 \ No newline at end of file diff --git a/docs/assets/badge_NET_5_Win10.svg b/docs/assets/badge_NET_5_Win10.svg deleted file mode 100644 index 544b222..0000000 --- a/docs/assets/badge_NET_5_Win10.svg +++ /dev/null @@ -1 +0,0 @@ -.NET: 5-windows10.NET5-windows10 \ No newline at end of file diff --git a/docs/assets/badge_NET_Fx_4_6.svg b/docs/assets/badge_NET_Fx_4_6.svg new file mode 100644 index 0000000..e6290b4 --- /dev/null +++ b/docs/assets/badge_NET_Fx_4_6.svg @@ -0,0 +1 @@ +.NET Framework: 4.6.NET Framework4.6 \ No newline at end of file diff --git a/docs/assets/badge_NET_Fx_4_6_1.svg b/docs/assets/badge_NET_Fx_4_6_1.svg new file mode 100644 index 0000000..52e190e --- /dev/null +++ b/docs/assets/badge_NET_Fx_4_6_1.svg @@ -0,0 +1 @@ +.NET Framework: 4.6.1.NET Framework4.6.1 \ No newline at end of file diff --git a/docs/assets/badge_NET_Fx_4_8.svg b/docs/assets/badge_NET_Fx_4_8.svg new file mode 100644 index 0000000..4a19cf6 --- /dev/null +++ b/docs/assets/badge_NET_Fx_4_8.svg @@ -0,0 +1 @@ +.NET Framework: 4.8.NET Framework4.8 \ No newline at end of file diff --git a/docs/assets/bg_title.jpg b/docs/assets/bg_title.jpg new file mode 100644 index 0000000..b49f950 Binary files /dev/null and b/docs/assets/bg_title.jpg differ diff --git a/docs/assets/chemistry.png b/docs/assets/chemistry.png new file mode 100644 index 0000000..866ba0f Binary files /dev/null and b/docs/assets/chemistry.png differ diff --git a/docs/assets/cmd.png b/docs/assets/cmd.png new file mode 100644 index 0000000..9996d53 Binary files /dev/null and b/docs/assets/cmd.png differ diff --git a/docs/assets/mime.png b/docs/assets/mime.png new file mode 100644 index 0000000..7dc0d97 Binary files /dev/null and b/docs/assets/mime.png differ diff --git a/docs/assets/web.png b/docs/assets/web.png new file mode 100644 index 0000000..05daf89 Binary files /dev/null and b/docs/assets/web.png differ diff --git a/docs/assets/wordmark.png b/docs/assets/wordmark.png new file mode 100644 index 0000000..bd8ec90 Binary files /dev/null and b/docs/assets/wordmark.png differ