Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
108 commits
Select commit Hold shift + click to select a range
544d682
-
dusrdev Aug 14, 2025
39792c0
update rules
dusrdev Aug 14, 2025
ae2b5ef
-
dusrdev Aug 14, 2025
3f439be
-
dusrdev Aug 14, 2025
a2712c7
clamp percentage
dusrdev Aug 14, 2025
c8e7616
Optimizations and safe-guards
dusrdev Aug 14, 2025
7ec4e80
Removed whitespace buffer
dusrdev Aug 14, 2025
92ae2c4
Added constant whitespace and method for safe writing
dusrdev Aug 14, 2025
442269b
Added method to get writer based on OutputPipe
dusrdev Aug 14, 2025
356ba3f
Improved ClearNextLines and NewLine
dusrdev Aug 14, 2025
6ade6f7
Added release note
dusrdev Aug 14, 2025
d469e6c
Fix bounds issue
dusrdev Aug 14, 2025
49852bf
Update release note
dusrdev Aug 14, 2025
7476203
Removed StringBuffer from some menus
dusrdev Aug 14, 2025
ca10b3d
Updated treeMenu
dusrdev Aug 14, 2025
d75ccfa
Added s to WriteWhiteSpaces
dusrdev Aug 14, 2025
45dceeb
Simplified some methods
dusrdev Aug 14, 2025
e5741eb
Removed Sharpify dependency
dusrdev Aug 14, 2025
39aa445
updated unit tests to Xunit3
dusrdev Aug 14, 2025
d3a9347
Updated version
dusrdev Aug 14, 2025
5ac5d95
Updated GitHub workflow
dusrdev Aug 14, 2025
3fd8081
More array pooling to reduce stack pressure on large console buffers
dusrdev Aug 14, 2025
81142f4
Moved versions to root
dusrdev Aug 14, 2025
50465a9
Removed changelog - now releasenotes
dusrdev Aug 14, 2025
ba04301
Removed duplicate - better root link
dusrdev Aug 14, 2025
548ad40
Better naming
dusrdev Aug 14, 2025
6615923
Updated props
dusrdev Aug 14, 2025
58292b3
Added patterns to reduced binary size
dusrdev Aug 14, 2025
dba44a9
Fix current line clearing
dusrdev Aug 14, 2025
a4735e6
Updated release notes
dusrdev Aug 14, 2025
b83b501
Added new patterns to modified to allow emoji
dusrdev Aug 14, 2025
dc660ee
Updated animations
dusrdev Aug 14, 2025
3d29f47
-
dusrdev Aug 15, 2025
4cd861d
Multiple improvements to syncronization and perf
dusrdev Aug 15, 2025
5690fd9
use langversion 13
dusrdev Aug 15, 2025
fb20f87
FormatTimeSpan API works better now with non-managed buffers
dusrdev Aug 15, 2025
c893651
Improve update steadiness to reduce animation jitter
dusrdev Aug 15, 2025
bdf3ff7
Improve ping pong animation and remove pulse
dusrdev Aug 15, 2025
72feb90
Improve perf by using local buffer
dusrdev Aug 15, 2025
26e64e8
Enhance flexibility by using default buffer width if Output is redire…
dusrdev Aug 15, 2025
fc1729d
Moved status to the left
dusrdev Aug 15, 2025
c5585c4
optimizations1
dusrdev Aug 15, 2025
3dc152d
optimization2
dusrdev Aug 15, 2025
8b2ebb4
optimization3
dusrdev Aug 15, 2025
3a4ddbe
Optimization-final
dusrdev Aug 15, 2025
442cca3
Updated release notes
dusrdev Aug 15, 2025
6519d70
moved animation to leftmost side
dusrdev Aug 15, 2025
d940512
Updated release notes
dusrdev Aug 15, 2025
243c423
Better test scenario
dusrdev Aug 15, 2025
8b652a8
More accurate test scenario
dusrdev Aug 15, 2025
578da7f
Implicit operators with object
dusrdev Oct 7, 2025
5529587
Update workflow to run once in debug configuration as well
dusrdev Oct 7, 2025
73df9ac
Addressed multiple analyzer warnings
dusrdev Oct 7, 2025
2acead9
pooling improvements
dusrdev Oct 7, 2025
aefc967
Add tests for long ISpanFormattable implementations
dusrdev Oct 7, 2025
f6a2ddd
Removed dotnet 8 targeting from this release onward to support better…
dusrdev Oct 7, 2025
8695a36
Removed net8 conditional which is no longer possible
dusrdev Oct 7, 2025
3264886
Allow ref struct parameters to ISpanFormattable writers
dusrdev Oct 7, 2025
4d844a9
Add coverage + updates
dusrdev Oct 7, 2025
6a43509
Removed commented out code
dusrdev Oct 7, 2025
e729879
Added tests to cover Color and ColoredOutput
dusrdev Oct 7, 2025
4537d99
Added tests for menus and trees
dusrdev Oct 7, 2025
9a6edce
Added tests for progressbars
dusrdev Oct 7, 2025
cd5d05c
Added tests for utilities
dusrdev Oct 7, 2025
df560f8
Added operator and computed field for default colors
dusrdev Oct 17, 2025
4dcee57
Added custom interpolated string handler for hotpaths
dusrdev Oct 17, 2025
02b65cc
Added entry paths for interpolated string handler
dusrdev Oct 17, 2025
43cbabe
Added alignment
dusrdev Oct 17, 2025
05afffb
Moved Default to correct place
dusrdev Oct 17, 2025
e9cae41
Improve perf of padding writing
dusrdev Oct 17, 2025
40625c5
Return field to computed state
dusrdev Oct 17, 2025
dba4c2c
Remove discards
dusrdev Oct 17, 2025
15c374f
Added tests to cover interpolated handler and default colors and new …
dusrdev Oct 17, 2025
1b73220
Added memory allocation measurement function
dusrdev Oct 17, 2025
ef5ed00
migrated sln to slnx
dusrdev Oct 18, 2025
d4f6bd0
Vastly improved buffer pool
dusrdev Oct 18, 2025
b1285fb
Better more optimized buffer pool
dusrdev Oct 18, 2025
5add6ce
Remove check
dusrdev Oct 18, 2025
3f1e12b
Handler improvement
dusrdev Oct 18, 2025
bd846dd
Remove interpolated suffix
dusrdev Oct 18, 2025
1b6d9d7
Suppressed unused parameters - string handler
dusrdev Oct 18, 2025
023242b
Add compiler services
dusrdev Oct 18, 2025
43dbf98
Implemented string handler
dusrdev Oct 18, 2025
e45dfff
Moved string handler overloads to write and writeline respectively
dusrdev Oct 18, 2025
5fa3238
Added string handler overloads
dusrdev Oct 18, 2025
5ada6ce
Removed unused using statement
dusrdev Oct 18, 2025
923f5f0
Improved using handler
dusrdev Oct 18, 2025
fb7f686
Better whitespace
dusrdev Oct 18, 2025
3fffe3a
Added overloads for handler
dusrdev Oct 18, 2025
81c975f
Removed unused methods
dusrdev Oct 18, 2025
25359a7
Moved and added tests
dusrdev Oct 18, 2025
6e6eb0a
Added core write methods to slim down handler
dusrdev Oct 18, 2025
cc7c849
Removed pipe field
dusrdev Oct 18, 2025
4a4026d
Removed tests of old utilities
dusrdev Oct 18, 2025
63495d8
Added overwrite method capable of zero allocation reactive components…
dusrdev Oct 21, 2025
caa425b
Update release notes
dusrdev Oct 21, 2025
88fae56
Removed unused import
dusrdev Oct 21, 2025
06446bb
Fixed typo in release notes
dusrdev Oct 21, 2025
9afdd4d
Updated versions
dusrdev Oct 21, 2025
970adeb
Added color remarks
dusrdev Oct 21, 2025
36507da
Update readme and versions
dusrdev Oct 21, 2025
5eb49f8
Add agents.md
dusrdev Oct 21, 2025
b5a6417
Update agents.md
dusrdev Oct 21, 2025
8410392
format
dusrdev Oct 21, 2025
b6ea8c2
Improve test coverage
dusrdev Oct 21, 2025
9d3f6e8
Added more tests
dusrdev Oct 21, 2025
0820c19
Fixed typo in release notes
dusrdev Oct 21, 2025
458961e
Skip tests that fail due to limited environment
dusrdev Oct 21, 2025
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
9 changes: 7 additions & 2 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -77,12 +77,17 @@ dotnet_remove_unnecessary_suppression_exclusions = none
#### C# Coding Conventions ####
[*.cs]
# analyzers
dotnet_diagnostic.IDE0290.severity = none # use primary constuctor
dotnet_diagnostic.IDE0290.severity = none # use primary constructor
dotnet_diagnostic.IDE0028.severity = none # use collection expression
dotnet_diagnostic.IDE0056.severity = none # simplify index operator
dotnet_diagnostic.IDE0057.severity = none # use range operator
dotnet_diagnostic.IDE0301.severity = none # simplify collection initialization
dotnet_diagnostic.IDE0053.severity = none # expression body lambda
dotnet_diagnostic.IDE0046.severity = none # simplify if(s) - conditional operator
dotnet_diagnostic.IDE0305.severity = none # [, ...] instead of .ToArray()

# namespace decleration

# namespace declaration
csharp_style_namespace_declarations = file_scoped:warning

# var preferences
Expand Down
63 changes: 0 additions & 63 deletions .gitattributes

This file was deleted.

64 changes: 16 additions & 48 deletions .github/workflows/UnitTests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,56 +2,24 @@ name: Unit Tests

on:
pull_request:
workflow_dispatch: # Allows the workflow to be triggered manually
workflow_dispatch:

jobs:

run-tests:

runs-on: ${{ matrix.os }}
unit-tests-matrix:
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
configuration: [Debug, Release]

env:
# Define the path to project and test project
PROJECT_PATH: PrettyConsole/PrettyConsole.csproj
TEST_PROJECT_PATH: PrettyConsole.Tests.Unit/PrettyConsole.Tests.Unit.csproj

steps:
# 1. Checkout the repository code
- name: Checkout Repository
uses: actions/checkout@v4

# 2. Cache NuGet packages
- name: Cache NuGet Packages
uses: actions/cache@v4
with:
path: ~/.nuget/packages
key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj') }}
restore-keys: |
${{ runner.os }}-nuget-

# 3. Setup .NET
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: 9.0.x

# 4. Clean
- name: Clean
run: |
dotnet clean ${{ env.PROJECT_PATH }} -c ${{ matrix.configuration }}
dotnet clean ${{ env.TEST_PROJECT_PATH }} -c ${{ matrix.configuration }}

# 5. Restore dependencies
- name: Restore Dependencies
run: |
dotnet restore ${{ env.PROJECT_PATH }}
dotnet restore ${{ env.TEST_PROJECT_PATH }}

# 6. Run Unit Tests
- name: Run Unit Tests
run: dotnet test ${{ env.TEST_PROJECT_PATH }} -c ${{ matrix.configuration }}
platform: [ubuntu-latest, windows-latest, macos-latest]
uses: dusrdev/actions/.github/workflows/reusable-dotnet-test-mtp.yaml@main
with:
platform: ${{ matrix.platform }}
dotnet-version: 9.0.x
test-project-path: PrettyConsole.Tests.Unit/PrettyConsole.Tests.Unit.csproj

unit-tests-debug:
uses: dusrdev/actions/.github/workflows/reusable-dotnet-test-mtp.yaml@main
with:
platform: ubuntu-latest
dotnet-version: 9.0.x
test-project-path: PrettyConsole.Tests.Unit/PrettyConsole.Tests.Unit.csproj
use-debug: true
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
*.userosscache
*.sln.docstates
PrettyConsole.Tests.Integration/
WARP.md
# AGENTS.md

# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
Expand Down
80 changes: 80 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# AGENTS.md

Repository: PrettyConsole

Summary

- PrettyConsole is a high-performance, allocation-conscious wrapper over System.Console that provides structured colored output, input helpers, rendering controls, menus, and progress bars. It targets net9.0, is trimming/AOT ready, and ships SourceLink metadata for debugging.
- Solution layout:
- PrettyConsole/ — main library
- PrettyConsole.Tests/ — interactive/demo runner (manually selects visual feature demos)
- PrettyConsole.Tests.Unit/ — xUnit v3 unit tests using Microsoft Testing Platform

Commands you’ll use often

- Build
- Build library:
- dotnet build PrettyConsole/PrettyConsole.csproj
- Build unit tests:
- dotnet build PrettyConsole.Tests.Unit/PrettyConsole.Tests.Unit.csproj
- The solution using .slnx format; run it as usual but prefer to build individual projects as needed.
- Format (uses the repo’s .editorconfig conventions)
- Check and fix code style/formatting:
- dotnet format
- Run
- Never run interactive/demo tests (PrettyConsole.Tests)
- Run unit tests (xUnit v3 via Microsoft Testing Platform):
- dotnet run --project PrettyConsole.Tests.Unit
- Run a single unit test:
- dotnet run --project PrettyConsole.Tests.Unit --filter-method "*UniquePartOfMethodName*"
- Examples:
- dotnet run --project PrettyConsole.Tests.Unit --filter-method "*WritesColoredLine*"
- Pack - DO NOT DO THIS YOURSELF!

Repo-specific agent rules and conventions

- Prefer dotnet CLI for making, verifying, and running changes.
- When changing a specific project, build/run just that project to validate, not the entire solution.
- For tests using Microsoft Testing Platform and/or xUnit v3, use dotnet run, never dotnet test.
- Adhere to .editorconfig in the repo for style and analyzers.
- If code needs to be “removed” as part of a change, do not delete files; comment out their contents so they won’t compile.
- Avoid reflection/dynamic assembly loading in published library code unless explicitly requested.

High-level architecture and key concepts

- Console facade
- `PrettyConsole.Console` is a static, partial wrapper over `System.Console`. It exposes the live `In`, `Out`, and `Error` streams, and adds helpers like `NewLine`, `Clear`, `ClearNextLines`, `GetCurrentLine`, `GoToLine`, `SetColors`, and `ResetColors` for structured rendering.
- Output routing
- `OutputPipe` is a two-value enum (`Out`, `Error`). Most write APIs accept an optional pipe; internally `Console.GetWriter` resolves the correct `TextWriter` so sequences remain redirect-friendly.
- Interpolated string handler
- `PrettyConsoleInterpolatedStringHandler` enables zero-allocation `$"..."` calls for `Write`, `WriteLine`, `ReadLine`, `TryReadLine`, `Confirm`, and `RequestAnyInput`. Colors automatically reset after each invocation, and handlers respect the selected pipe and optional `IFormatProvider`.
- Coloring model
- `ColoredOutput` and the `Color` record provide terse composition via `"Text" * Color.Red / Color.Blue` and implicit conversions. Default foreground/background values are stored on `Color` so spans render without string allocations.
- Write APIs
- `Write`/`WriteLine` cover interpolated strings, `ColoredOutput` spans, raw `ReadOnlySpan<char>`, and generic `ISpanFormattable` values (including `ref struct`s) with optional foreground/background colors and format providers. Internally they rely on `BufferPool` to avoid allocation spikes.
- Inputs
- `ReadLine`/`TryReadLine` support `IParsable<T>` types, optional defaults, enum parsing with `ignoreCase`, and interpolated prompts. `Confirm` exposes `DefaultConfirmValues`, overloads for custom truthy tokens, and interpolated prompts; `RequestAnyInput` blocks on `ReadKey` with colored prompts if desired.
- Rendering controls
- `ClearNextLines`, `GoToLine`, and `GetCurrentLine` coordinate bounded screen regions; `Clear` wipes the buffer when safe. These helpers underpin progress rendering and overwrite scenarios.
- Advanced outputs
- `OverwriteCurrentLine`, `Overwrite`, and `Overwrite<TState>` run user actions while clearing a configurable number of lines, enabling reactive text dashboards without leaving artifacts. `TypeWrite`/`TypeWriteLine` animate character-by-character output with adjustable delays.
- Menus and tables
- `Selection` returns a single choice or empty string on invalid input; `MultiSelection` parses space-separated indices into string arrays; `TreeMenu` renders two-level hierarchies and validates input (throwing `ArgumentException` when selections are invalid); `Table` renders headers + columns with width calculations.
- Progress bars
- `IndeterminateProgressBar` binds to running `Task` instances, optionally starts tasks, supports cancellable `RunAsync` overloads, exposes `AnimationSequence`, `Patterns`, `ForegroundColor`, `DisplayElapsedTime`, and `UpdateRate`. Frames render on the error pipe and auto-clear.
- `ProgressBar` maintains a single-line bar on the error pipe. `Update` accepts `int`/`double` percentages plus optional status spans, and exposes `ProgressChar`, `ForegroundColor`, and `ProgressColor` for customization.
- Packaging and targets
- `PrettyConsole.csproj` targets net9.0, enables trimming/AOT (`IsTrimmable`, `IsAotCompatible`), embeds SourceLink, and grants `InternalsVisibleTo` the unit-test project.

Testing structure and workflows

- PrettyConsole.Tests (interactive)
- `Program.cs` allows to test things that need to be verified visually and can't be tested easily or at all using unit tests. It contains tests for various things like menues, tables, progress bar, etc... and at occations new overloads and other things. It's content doesn't need to be tracked, it is more like a playground.
- PrettyConsole.Tests.Unit (xUnit v3)
- Uses Microsoft.NET.Test.Sdk with the Microsoft Testing Platform runner; xunit.runner.json is included. Execute with dotnet run as shown above; pass filters after to narrow to a class or method.

Notes and gotchas

- The library aims to minimize allocations; prefer span-based overloads (ReadOnlySpan<char>, ReadOnlySpan<ColoredOutput>) for best performance when contributing.
- When authoring new features, pick the appropriate OutputPipe to keep CLI piping behavior intact.
- On macOS terminals, ANSI is supported; Windows legacy terminals are handled via ANSI-compatible rendering in the library.
68 changes: 60 additions & 8 deletions PrettyConsole.Tests.Unit/AdvancedInputs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ public void Confirm_Case_Y() {
var reader = Utilities.GetReader("y");
In = reader;
var res = Confirm(["Enter y" * Color.White]);
stringWriter.ToString().Should().Contain("Enter y");
res.Should().BeTrue();
Assert.Contains("Enter y", stringWriter.ToString());
Assert.True(res);
}

[Fact]
Expand All @@ -17,8 +17,8 @@ public void Confirm_Case_Yes() {
var reader = Utilities.GetReader("yes");
In = reader;
var res = Confirm(["Enter yes" * Color.White]);
stringWriter.ToString().Should().Contain("Enter yes");
res.Should().BeTrue();
Assert.Contains("Enter yes", stringWriter.ToString());
Assert.True(res);
}

[Fact]
Expand All @@ -27,8 +27,8 @@ public void Confirm_Case_Empty() {
var reader = Utilities.GetReader("");
In = reader;
var res = Confirm(["Enter yes" * Color.White]);
stringWriter.ToString().Should().Contain("Enter yes");
res.Should().BeTrue();
Assert.Contains("Enter yes", stringWriter.ToString());
Assert.True(res);
}

[Fact]
Expand All @@ -37,7 +37,59 @@ public void Confirm_Case_No() {
var reader = Utilities.GetReader("no");
In = reader;
var res = Confirm(["Enter no" * Color.White]);
stringWriter.ToString().Should().Contain("Enter no");
res.Should().BeFalse();
Assert.Contains("Enter no", stringWriter.ToString());
Assert.False(res);
}

[Fact]
public void Confirm_Case_Y_Interpolated() {
Out = Utilities.GetWriter(out var stringWriter);
var reader = Utilities.GetReader("y");
In = reader;
var res = Confirm($"Enter y:");
Assert.Contains("Enter y:", stringWriter.ToString());
Assert.True(res);
}

[Fact]
public void Confirm_Case_Yes_Interpolated() {
Out = Utilities.GetWriter(out var stringWriter);
var reader = Utilities.GetReader("yes");
In = reader;
var res = Confirm($"Enter yes:");
Assert.Contains("Enter yes", stringWriter.ToString());
Assert.True(res);
}

[Fact]
public void Confirm_Case_Empty_Interpolated() {
Out = Utilities.GetWriter(out var stringWriter);
var reader = Utilities.GetReader("");
In = reader;
var res = Confirm($"Enter yes:");
Assert.Contains("Enter yes", stringWriter.ToString());
Assert.True(res);
}

[Fact]
public void Confirm_Case_No_Interpolated() {
Out = Utilities.GetWriter(out var stringWriter);
var reader = Utilities.GetReader("no");
In = reader;
var res = Confirm($"Enter no:");
Assert.Contains("Enter no", stringWriter.ToString());
Assert.False(res);
}

[Fact]
public void Confirm_CustomTrueValues_WithInterpolatedPrompt() {
Out = Utilities.GetWriter(out var stringWriter);
var reader = Utilities.GetReader("ok");
In = reader;

var res = Confirm(["ok", "okay"], false, $"Proceed?");

Assert.Equal("Proceed?", stringWriter.ToStringAndFlush());
Assert.True(res);
}
}
Loading