Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 85 additions & 0 deletions TrueColorConsole/StyleRule.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text.RegularExpressions;

namespace TrueColorConsole.Styled {
public interface iRule {
void ApplyRuleToSegments(List<SegmentItem> items);
}
public class FormatRule : RawRule {
public FormatRule(Regex regex, IEnumerable<VTFormat> before, IEnumerable<VTFormat> after, Func<string, string> match_replace = null)
: base(regex, new Action[] { () => VTConsole.SetFormat(before.ToArray()) }, new Action[] { () => VTConsole.SetFormat(after.ToArray()) }, match_replace) {
}
}
public class RawRule : iRule {
public string only_use_capture_group_name;//if set only operate on this capture group rather than entire match
public RawRule(Regex regex, IEnumerable<Action> before, IEnumerable<Action> after, Func<string, string> match_replace = null) {
this.regex = regex;
this.match_replace = match_replace;
this.before_cmds = before;
this.after_cmds = after;
}
public void ApplyRuleToSegments(List<SegmentItem> items) {
foreach (var item in items.ToArray()) {
var cur_item = item;
var matches = regex.Matches(cur_item.str);
foreach (var entire_match in matches.Cast<Match>().OrderByDescending(a => a.Index)) {//need to work back to front, do we?
if (!entire_match.Success)
continue;
var match = ! string.IsNullOrWhiteSpace(only_use_capture_group_name) ? entire_match.Groups[only_use_capture_group_name] : entire_match;
var pos = match.Index;
//So we want to allow other items affecting the segment( ie uderline bg color etc) to conintue through that we don't set
var before_segment = new SegmentItem { str = cur_item.str.Substring(0, pos), before = cur_item.before };
var after_segment = new SegmentItem { str = cur_item.str.Substring(pos + match.Length), after = cur_item.after };
//even if there is nothing before or after we want to add them for styling
items.Insert(items.IndexOf(cur_item) + 1, after_segment);
items.Insert(items.IndexOf(cur_item), before_segment);

cur_item.str = cur_item.str.Substring(pos, match.Length);

if (match_replace != null)
cur_item.str = match_replace(cur_item.str);

cur_item.before = before_cmds;
cur_item.after = after_cmds;
cur_item = before_segment;//we work back to front
}
}
}
protected IEnumerable<Action> before_cmds;
protected IEnumerable<Action> after_cmds;
protected Func<string, string> match_replace;
protected Regex regex;
}
public class StyleRule : RawRule {
public StyleRule(String regex, Color foreground_color, Func<string, string> match_replace = null, Color? background_color = null) : this(new Regex(regex), foreground_color, match_replace, background_color) {

}
protected static IEnumerable<Action> GetCommands(Color foreground_color, Color? background_color, bool need_after) {
var cmds = new List<Action>();
if (foreground_color != Color.Empty && foreground_color != Color.Transparent) {
if (!need_after)
cmds.Add(() => VTConsole.SetColorForeground(foreground_color));
else
cmds.Add(() => VTConsole.PopForegroundColor());
}
if (background_color != null && background_color != Color.Empty && background_color != Color.Transparent) {
if (!need_after)
cmds.Add(() => VTConsole.SetColorBackground(background_color.Value));
else
cmds.Add(() => VTConsole.PopBackgroundColor());
}

return cmds;
}
public StyleRule(Regex regex, Color foreground_color, Func<string, string> match_replace = null, Color? background_color = null) :
base(regex, GetCommands(foreground_color, background_color, false), GetCommands(foreground_color, background_color, true), match_replace) {

}


}

}
36 changes: 36 additions & 0 deletions TrueColorConsole/StyleSheet.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Drawing;
using System.Text;
using TrueColorConsole;

namespace TrueColorConsole.Styled {
public class StyleSheet {
public StyleSheet() { }
public StyleSheet(Color default_color) {
this.default_color = default_color;
}
public Color default_color = Color.Empty;
public List<iRule> rules = new List<iRule>();
public void AddStyle(iRule rule) => rules.Add(rule);
public IEnumerable<SegmentItem> ApplyRules(String str) {
var items = new List<SegmentItem>();
var def = new SegmentItem {str=str };
if (default_color != Color.Empty && default_color != Color.Transparent) {
def.before = new Action[] { () => VTConsole.SetColorForeground(default_color) };
def.after = new Action[] { () => VTConsole.PopForegroundColor() };
}
items.Add(def);
foreach (var rule in rules)
rule.ApplyRuleToSegments(items);
return items;
}
}
public class SegmentItem {
public string str;
public IEnumerable<Action> before;
public IEnumerable<Action> after;
}

}
53 changes: 53 additions & 0 deletions TrueColorConsole/VTConsole.Styled.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Drawing;
using System.Text;
using TrueColorConsole.Styled;
namespace TrueColorConsole {
partial class VTConsole {
public static void WriteStyled(String value, StyleSheet styleSheet) {
track_color_history = true;
var items = styleSheet.ApplyRules(value);
foreach (var item in items) {
if (item.before != null)
foreach (var action in item.before)
action();
Console.Write(item.str);
if (item.after != null)
foreach (var action in item.after)
action();
}
SetFormat(VTFormat.Default);
track_color_history = false;
foreground_color_history.Clear();
background_color_history.Clear();
}
public static void WriteLineStyled(string value, StyleSheet styleSheet) {
WriteStyled(value, styleSheet);
Console.WriteLine();
}
private static ConcurrentStack<Color> foreground_color_history = new ConcurrentStack<Color>();
private static ConcurrentStack<Color> background_color_history = new ConcurrentStack<Color>();
private static bool track_color_history;
public static void PopForegroundColor() {
if (foreground_color_history.TryPop(out Color cur)) {
if (foreground_color_history.TryPop(out Color prev))
SetColorForeground(prev);//will auto requeue it
else//set back to default color
SetFormat(VTFormat.ForegroundDefault);

}
}
public static void PopBackgroundColor() {
if (background_color_history.TryPop(out Color cur)) {
if (background_color_history.TryPop(out Color prev))
SetColorBackground(prev);//will auto requeue it
else//set back to default color
SetFormat(VTFormat.BackgroundDefault);

}
}

}
}
100 changes: 63 additions & 37 deletions TrueColorConsole/VTConsole.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,37 @@ public static partial class VTConsole
{
#region Interop

private const uint StdOutputHandle = unchecked((uint) -11);
private const uint StdInputHandle = unchecked((uint) -10);
private static readonly IntPtr InvalidHandleValue = new IntPtr(-1);

[DllImport("kernel32.dll", SetLastError = true)]
private static extern IntPtr GetStdHandle(uint nStdHandle);

[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool GetConsoleMode(IntPtr hConsoleHandle, out uint lpMode);
private static class NativeMethods
{
public const uint StdOutputHandle = unchecked((uint) -11);
public const uint StdInputHandle = unchecked((uint) -10);
public static readonly IntPtr InvalidHandleValue = new IntPtr(-1);

[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr GetStdHandle(uint nStdHandle);

[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GetConsoleMode(IntPtr hConsoleHandle, out uint lpMode);

[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SetConsoleMode(IntPtr hConsoleHandle, uint dwMode);

[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool WriteConsole(
IntPtr hConsoleOutput,
[MarshalAs(UnmanagedType.LPArray)] byte[] lpBuffer,
int lpNumberOfCharsToWrite,
out int lpNumberOfCharsToWritten,
IntPtr lpReserved
);
}

private static bool GetConsoleMode(IntPtr hConsoleHandle, out ConsoleModeOutput mode)
{
if (!GetConsoleMode(hConsoleHandle, out uint lpMode))
if (!NativeMethods.GetConsoleMode(hConsoleHandle, out uint lpMode))
{
mode = 0;
return false;
Expand All @@ -39,7 +56,7 @@ private static bool GetConsoleMode(IntPtr hConsoleHandle, out ConsoleModeOutput

private static bool GetConsoleMode(IntPtr hConsoleHandle, out ConsoleModeInput mode)
{
if (!GetConsoleMode(hConsoleHandle, out uint lpMode))
if (!NativeMethods.GetConsoleMode(hConsoleHandle, out uint lpMode))
{
mode = 0;
return false;
Expand All @@ -49,30 +66,16 @@ private static bool GetConsoleMode(IntPtr hConsoleHandle, out ConsoleModeInput m
return true;
}

[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool SetConsoleMode(IntPtr hConsoleHandle, uint dwMode);

[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool WriteConsole(
IntPtr hConsoleOutput,
[MarshalAs(UnmanagedType.LPArray)] byte[] lpBuffer,
int lpNumberOfCharsToWrite,
out int lpNumberOfCharsToWritten,
IntPtr lpReserved
);

private static bool GetStdIn(out IntPtr handle)
{
handle = GetStdHandle(StdInputHandle);
return handle != InvalidHandleValue;
handle = NativeMethods.GetStdHandle(NativeMethods.StdInputHandle);
return handle != NativeMethods.InvalidHandleValue;
}

private static bool GetStdOut(out IntPtr handle)
{
handle = GetStdHandle(StdOutputHandle);
return handle != InvalidHandleValue;
handle = NativeMethods.GetStdHandle(NativeMethods.StdOutputHandle);
return handle != NativeMethods.InvalidHandleValue;
}

#endregion
Expand All @@ -91,12 +94,24 @@ private static bool GetStdOut(out IntPtr handle)
private static ConsoleModeInput _inLast;
private static IntPtr _outHandle;
private static ConsoleModeOutput _outLast;
private static bool _isEnabled;

private static readonly bool IsWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);

/// <summary>
/// Gets if virtual terminal features are enabled.
/// </summary>
[PublicAPI]
public static bool IsEnabled { get; private set; }
public static bool IsEnabled
{
get
{
if (IsWindows)
return _isEnabled;
return true;
}
private set => _isEnabled = value;
}

/// <summary>
/// Gets if virtual terminal features are supported (see Remarks).
Expand Down Expand Up @@ -147,7 +162,7 @@ bool EnableInput()

var mode = _inLast | ConsoleModeInput.EnableVirtualTerminalInput;

return SetConsoleMode(StdIn, (uint) mode);
return NativeMethods.SetConsoleMode(StdIn, (uint) mode);
}

bool EnableOutput()
Expand All @@ -163,12 +178,12 @@ bool EnableOutput()
if (disableNewLineAutoReturn)
mode |= ConsoleModeOutput.DisableNewlineAutoReturn;

if (SetConsoleMode(StdOut, (uint) mode))
if (NativeMethods.SetConsoleMode(StdOut, (uint) mode))
return true;

mode = _outLast | ConsoleModeOutput.EnableVirtualTerminalProcessing;

return SetConsoleMode(StdOut, (uint) mode);
return NativeMethods.SetConsoleMode(StdOut, (uint) mode);
}

IsEnabled = EnableInput() && EnableOutput();
Expand All @@ -185,17 +200,19 @@ bool EnableOutput()
[PublicAPI]
public static bool Disable()
{
if (!IsWindows)
return true;
if (!IsEnabled)
return false;

bool DisableInput()
{
return GetStdIn(out var handle) && SetConsoleMode(handle, (uint) _inLast);
return GetStdIn(out var handle) && NativeMethods.SetConsoleMode(handle, (uint) _inLast);
}

bool DisableOutput()
{
return GetStdOut(out var handle) && SetConsoleMode(handle, (uint) _outLast);
return GetStdOut(out var handle) && NativeMethods.SetConsoleMode(handle, (uint) _outLast);
}

IsEnabled = !(DisableInput() && DisableOutput());
Expand Down Expand Up @@ -244,6 +261,8 @@ public static string GetColorForegroundString(int r, int g, int b)
public static void SetColorBackground(Color color)
{
Console.Write(GetColorBackgroundString(color.R, color.G, color.B));
if (track_color_history)
background_color_history.Push(color);
}

/// <summary>
Expand All @@ -256,6 +275,8 @@ public static void SetColorBackground(Color color)
public static void SetColorForeground(Color color)
{
Console.Write(GetColorForegroundString(color.R, color.G, color.B));
if (track_color_history)
foreground_color_history.Push(color);
}

/// <summary>
Expand Down Expand Up @@ -284,7 +305,12 @@ public static void SetFormat(params VTFormat[] formats)
[PublicAPI]
public static int WriteFast(byte[] buffer)
{
WriteConsole(StdOut, buffer, buffer.Length, out var written, IntPtr.Zero);
if (!IsWindows)
{
Console.Write(Console.OutputEncoding.GetString(buffer));
return buffer.Length;
}
NativeMethods.WriteConsole(StdOut, buffer, buffer.Length, out var written, IntPtr.Zero);
return written;
}

Expand Down
Loading