diff --git a/README.md b/README.md
index 050c4f0..ec82d94 100644
--- a/README.md
+++ b/README.md
@@ -8,6 +8,7 @@
[](https://www.nuget.org/packages/ConsolePlus.net)
[](https://opensource.org/licenses/MIT)
[](https://dotnet.microsoft.com/)
+ [](https://www.nuget.org/packages/ConsolePlus.net)
[](https://www.nuget.org/packages/ConsolePlus.net)
diff --git a/docs/adr/adr-config.adrplus b/adr-config.adrplus
similarity index 77%
rename from docs/adr/adr-config.adrplus
rename to adr-config.adrplus
index f09c9e6..83237a2 100644
--- a/docs/adr/adr-config.adrplus
+++ b/adr-config.adrplus
@@ -1,7 +1,7 @@
{
"folderadr": "docs/adr",
"migrationpattern": "",
- "template": "",
+ "template": "## Context\n\nDescribe the forces, constraints, and background that make this decision necessary.\n\n## Decision\n\nState the decision that was made.\n\n## Consequences\n\n- **Positive:** \n- **Negative / trade-off:** \n",
"prefix": "ADR",
"lenseq": 4,
"lenversion": 2,
diff --git a/docs/adr/ADR0001V01R02-SplitConsolePlusAndPromptPlus.md b/docs/adr/ADR0001V01R02-SplitConsolePlusAndPromptPlus.md
index 4635ac7..8f2d321 100644
--- a/docs/adr/ADR0001V01R02-SplitConsolePlusAndPromptPlus.md
+++ b/docs/adr/ADR0001V01R02-SplitConsolePlusAndPromptPlus.md
@@ -1,6 +1,6 @@
-
-
+
-
+
-
+
-
+
-
+
-
+
+
True
-
+
+
True
- $(NoWarn);CS1591
+ $(NoWarn);CS1591
```
-Remova `;CS1591` de `NoWarn` para ver avisos de documentação faltando.
+Remove `;CS1591` from `NoWarn` to see warnings about missing documentation.
-## 🌐 Links Externos (Opcional)
+## 🌐 External Links (Optional)
-O arquivo `docs/api/links.json` permite configurar links externos para tipos do .NET Framework:
+The `docs/api/links.json` file lets you configure external links for .NET Framework types:
```json
{
@@ -172,77 +172,77 @@ O arquivo `docs/api/links.json` permite configurar links externos para tipos do
}
```
-Adicione novos tipos conforme necessário para melhorar os links da documentação.
+Add new types as needed to improve the documentation's links.
-## 🚀 Publicando a Documentação
+## 🚀 Publishing the Documentation
### GitHub Pages
-Para publicar a documentação no GitHub Pages:
+To publish the documentation on GitHub Pages:
-1. Configure o repositório para usar GitHub Pages
-2. Aponte para a branch/pasta que contém os arquivos Markdown
-3. A documentação estará disponível em `https://username.github.io/ConsolePlus/`
+1. Configure the repository to use GitHub Pages
+2. Point it to the branch/folder that contains the Markdown files
+3. The documentation will be available at `https://username.github.io/ConsolePlus/`
-### ReadTheDocs ou Outras Plataformas
+### ReadTheDocs or Other Platforms
-Os arquivos Markdown gerados podem ser usados com qualquer plataforma de documentação que suporte Markdown.
+The generated Markdown files can be used with any documentation platform that supports Markdown.
-## 🐛 Solução de Problemas
+## 🐛 Troubleshooting
-### Documentação Não Está Sendo Gerada
+### Documentation Is Not Being Generated
-1. Verifique se `GenerateDocumentationFile` está `True`
-2. Confirme que o pacote DefaultDocumentation está instalado
-3. Faça um Clean + Rebuild da solução
-4. Verifique erros de build no Output
+1. Check that `GenerateDocumentationFile` is `True`
+2. Confirm that the DefaultDocumentation package is installed
+3. Do a Clean + Rebuild of the solution
+4. Check for build errors in the Output
-### Avisos de Build
+### Build Warnings
-Se ver avisos relacionados ao DefaultDocumentation, verifique:
-- A versão do pacote é compatível com seu .NET SDK
-- Todas as propriedades de configuração estão corretas
-- Não há conflitos com outros analisadores
+If you see warnings related to DefaultDocumentation, check that:
+- The package version is compatible with your .NET SDK
+- All configuration properties are correct
+- There are no conflicts with other analyzers
-### Links Quebrados
+### Broken Links
-Se houver links quebrados na documentação:
-- Verifique se os namespaces/tipos referenciados existem
-- Atualize `links.json` para tipos externos
-- Use `` corretamente nos comentários XML
+If there are broken links in the documentation:
+- Check that the referenced namespaces/types exist
+- Update `links.json` for external types
+- Use `` correctly in the XML comments
-## 📦 Controle de Versão
+## 📦 Version Control
-### O que Commitar
+### What to Commit
-✅ **Commitar**:
-- Arquivos de configuração (`ConsolePlus.csproj`)
-- Comentários XML no código-fonte
-- `links.json` (se usado)
+✅ **Commit**:
+- Configuration files (`ConsolePlus.csproj`)
+- XML comments in the source code
+- `links.json` (if used)
-❓ **Opcional**:
-- Arquivos `.md` gerados em `docs/api/`
- - **Commitar**: Para ter histórico e facilitar revisão em PRs
- - **Não commitar**: Se preferir gerar sob demanda (adicionar `docs/api/*.md` ao `.gitignore`)
+❓ **Optional**:
+- Generated `.md` files in `docs/api/`
+ - **Commit**: To keep a history and make PR review easier
+ - **Don't commit**: If you prefer to generate them on demand (add `docs/api/*.md` to `.gitignore`)
-A escolha depende da preferência da equipe. Commitar permite ver mudanças na documentação em PRs.
+The choice depends on the team's preference. Committing lets you see documentation changes in PRs.
-## 🤝 Contribuindo
+## 🤝 Contributing
-Ao fazer um Pull Request que adiciona ou modifica API pública:
+When submitting a Pull Request that adds or modifies public API:
-1. ✅ Adicione comentários XML completos
-2. ✅ Inclua exemplos quando apropriado
-3. ✅ Regenere a documentação (build)
-4. ✅ Verifique se os arquivos `.md` foram atualizados
-5. ✅ Revise a documentação gerada para qualidade
+1. ✅ Add complete XML comments
+2. ✅ Include examples where appropriate
+3. ✅ Regenerate the documentation (build)
+4. ✅ Check that the `.md` files were updated
+5. ✅ Review the generated documentation for quality
-## 📚 Recursos
+## 📚 Resources
-- [DefaultDocumentation no GitHub](https://github.com/Doraku/DefaultDocumentation)
+- [DefaultDocumentation on GitHub](https://github.com/Doraku/DefaultDocumentation)
- [XML Documentation Comments (Microsoft)](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/xmldoc/)
- [Recommended XML Tags (Microsoft)](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/xmldoc/recommended-tags)
---
-**Última atualização**: Este guia foi criado junto com a configuração inicial do DefaultDocumentation para o ConsolePlus.
+**Last updated**: This guide was created together with the initial DefaultDocumentation setup for ConsolePlus.
diff --git a/docs/api/ConsolePlus.md b/docs/api/ConsolePlus.md
index deabaea..eff3d10 100644
--- a/docs/api/ConsolePlus.md
+++ b/docs/api/ConsolePlus.md
@@ -3183,7 +3183,7 @@ public static void WriteLines(int steps=1);
`steps` [System\.Int32](https://learn.microsoft.com/en-us/dotnet/api/system.int32 'System\.Int32')
-Numbers de lines\.
+Number of lines\.
| Events | |
| :--- | :--- |
diff --git a/docs/api/Style.md b/docs/api/Style.md
index 8895713..5e27c32 100644
--- a/docs/api/Style.md
+++ b/docs/api/Style.md
@@ -15,6 +15,7 @@ Implements [System\.IEquatable<](https://learn.microsoft.com/en-us/dotnet/api
### Remarks
Use the primary constructor to specify explicit colors and an [Overflow](Overflow.md 'ConsolePlusLibrary\.Overflow') strategy, or the
+implicit conversion from [Color](Color.md 'ConsolePlusLibrary\.Color') for a quick foreground\-only style with a default background\.
### Constructors
@@ -48,6 +49,7 @@ Overflow handling strategy applied when content exceeds the target width\.
### Remarks
Use the primary constructor to specify explicit colors and an [Overflow](Overflow.md 'ConsolePlusLibrary\.Overflow') strategy, or the
+implicit conversion from [Color](Color.md 'ConsolePlusLibrary\.Color') for a quick foreground\-only style with a default background\.
### Properties
diff --git a/src/ConsoleAbstractions/AnsiColorBuilder.cs b/src/ConsoleAbstractions/AnsiColorBuilder.cs
index 371722d..07c7318 100644
--- a/src/ConsoleAbstractions/AnsiColorBuilder.cs
+++ b/src/ConsoleAbstractions/AnsiColorBuilder.cs
@@ -23,10 +23,10 @@ public static IEnumerable Build(ColorSystem system, Color color, bool fore
return system switch
{
- ColorSystem.NoColors => [], // No colors
+ ColorSystem.NoColors => [],
ColorSystem.TrueColor => GetTrueColor(color, foreground), // 24-bit
ColorSystem.Standard => GetEightBit(color, foreground), // 8-bit
- ColorSystem.FourBit => GetFourBit(color, foreground), // 4-bit
+ ColorSystem.FourBit => GetFourBit(color, foreground),
_ => throw new InvalidOperationException("Could not determine ANSI color oe legacy."),
};
}
diff --git a/src/ConsoleAbstractions/AnsiConsoleAdapter.cs b/src/ConsoleAbstractions/AnsiConsoleAdapter.cs
index e2cf902..e5c6794 100644
--- a/src/ConsoleAbstractions/AnsiConsoleAdapter.cs
+++ b/src/ConsoleAbstractions/AnsiConsoleAdapter.cs
@@ -41,18 +41,16 @@ public AnsiConsoleAdapter(IProfileReadOnly profile, ILock lockenv, CancellationT
_mainToken = mainToken;
_lock.Run(() =>
{
- // Change the output and input code page to 65001 (UTF-8)
+ // Code page 65001 is UTF-8.
Console.OutputEncoding = Encoding.UTF8;
Console.InputEncoding = Encoding.UTF8;
- _currentBuffer = TargetScreen.Primary; //ensure we start with the primary buffer
+ _currentBuffer = TargetScreen.Primary;
_lastWidth = EnvironmentUtil.GetSafeWidth();
_lastHeight = EnvironmentUtil.GetSafeHeight();
_consoleForegroundColor = _profile.DefaultForegroundColor;
_consoleBackgroundColor = _profile.DefaultBackgroundColor;
- // Start monitoring console size changes
StartSizeMonitoring();
- // Ensure cursor is visible by default
ShowCursor();
});
@@ -1372,7 +1370,6 @@ public bool SwapBuffer(TargetScreen value)
return _lock.Run(() =>
{
ThrowIfDisposed();
- // Switch to TargetBuffer screen
switch (value)
{
case TargetScreen.Primary:
@@ -1418,7 +1415,7 @@ private async Task MonitorSizeChanges(CancellationToken cancellationToken)
if (currentWidth != pendingWidth || currentHeight != pendingHeight)
{
- // Tamanho ainda está mudando — reinicia o contador de silêncio
+ // Size is still changing — reset the silence counter.
pendingWidth = currentWidth;
pendingHeight = currentHeight;
silenceMs = 0;
@@ -1430,7 +1427,7 @@ private async Task MonitorSizeChanges(CancellationToken cancellationToken)
if (silenceMs >= StabilityThresholdMs)
{
- // Tamanho estável por StabilityThresholdMs ms — dispara UMA vez
+ // Size has been stable for StabilityThresholdMs ms — fire the event once.
_lastWidth = pendingWidth;
_lastHeight = pendingHeight;
@@ -1452,11 +1449,11 @@ private async Task MonitorSizeChanges(CancellationToken cancellationToken)
}
catch (OperationCanceledException)
{
- // Cancelamento esperado — saída limpa
+ // Expected cancellation — clean exit.
}
catch
{
- // Ignora falhas inesperadas de leitura do console
+ // Ignore unexpected console read failures.
}
}
diff --git a/src/ConsoleAbstractions/NoAnsiConsoleAdapter.cs b/src/ConsoleAbstractions/NoAnsiConsoleAdapter.cs
index f6d73fc..b78cb7e 100644
--- a/src/ConsoleAbstractions/NoAnsiConsoleAdapter.cs
+++ b/src/ConsoleAbstractions/NoAnsiConsoleAdapter.cs
@@ -65,7 +65,7 @@ public NoAnsiConsoleAdapter(IProfileReadOnly profile, ILock lockenv, Cancellatio
_mainToken = mainToken;
_lock.Run(() =>
{
- // Change the output and input code page to 65001 (UTF-8)
+ // Code page 65001 is UTF-8.
Console.OutputEncoding = Encoding.UTF8;
Console.InputEncoding = Encoding.UTF8;
@@ -73,10 +73,8 @@ public NoAnsiConsoleAdapter(IProfileReadOnly profile, ILock lockenv, Cancellatio
_lastHeight = EnvironmentUtil.GetSafeHeight();
_consoleForegroundColor = _profile.DefaultForegroundColor;
_consoleBackgroundColor = _profile.DefaultBackgroundColor;
- // Start monitoring console size changes
StartSizeMonitoring();
- // Ensure cursor is visible by default
- ShowCursor();
+ ShowCursor();
});
}
@@ -1411,7 +1409,7 @@ private async Task MonitorSizeChanges(CancellationToken cancellationToken)
if (currentWidth != pendingWidth || currentHeight != pendingHeight)
{
- // Tamanho ainda está mudando — reinicia o contador de silêncio
+ // Size is still changing — reset the silence counter.
pendingWidth = currentWidth;
pendingHeight = currentHeight;
silenceMs = 0;
@@ -1423,7 +1421,7 @@ private async Task MonitorSizeChanges(CancellationToken cancellationToken)
if (silenceMs >= StabilityThresholdMs)
{
- // Tamanho estável por StabilityThresholdMs ms — dispara UMA vez
+ // Size has been stable for StabilityThresholdMs ms — fire the event once.
_lastWidth = pendingWidth;
_lastHeight = pendingHeight;
@@ -1445,11 +1443,11 @@ private async Task MonitorSizeChanges(CancellationToken cancellationToken)
}
catch (OperationCanceledException)
{
- // Cancelamento esperado — saída limpa
+ // Expected cancellation — clean exit.
}
catch
{
- // Ignora falhas inesperadas de leitura do console
+ // Ignore unexpected console read failures.
}
}
diff --git a/src/ConsoleAbstractions/ProfileExtensions.cs b/src/ConsoleAbstractions/ProfileExtensions.cs
index e87e792..e950137 100644
--- a/src/ConsoleAbstractions/ProfileExtensions.cs
+++ b/src/ConsoleAbstractions/ProfileExtensions.cs
@@ -31,7 +31,7 @@ public static void EnrichersCI(this ProfileConsole profile)
new Tfs(),
new Travis()
];
- //overwrite profile properties (Interactive and SupportsAnsi) based on known CI environments, the first that matches will enrich the profile and break the loop
+ // First matching CI environment enriches the profile (Interactive, SupportsAnsi) and stops the search.
foreach (var itemCI in defaultEnrichers)
{
if (itemCI.TryEnrich(profile))
diff --git a/src/ConsolePlus.Extra.cs b/src/ConsolePlus.Extra.cs
index 57f3eb4..b660a5c 100644
--- a/src/ConsolePlus.Extra.cs
+++ b/src/ConsolePlus.Extra.cs
@@ -37,7 +37,7 @@ public static void ClearLine(int? row = null, Style? style = null)
///
/// Write lines with line terminator
///
- /// Numbers de lines.
+ /// Number of lines.
public static void WriteLines(int steps = 1)
{
_consoledrive.WriteLines(steps);
diff --git a/src/ConsolePlus.csproj b/src/ConsolePlus.csproj
index 4764c15..b88838d 100644
--- a/src/ConsolePlus.csproj
+++ b/src/ConsolePlus.csproj
@@ -24,7 +24,7 @@
ConsolePlus.net
README.md
https://github.com/FRACerqueira/ConsolePlus
- 1.0.0-Beta4
+ 1.0.0-Beta5
icon.png
© 2026 - Fernando Cerqueira
True
diff --git a/src/Core/AnsiDetector.cs b/src/Core/AnsiDetector.cs
index 7f9702b..c97adc0 100644
--- a/src/Core/AnsiDetector.cs
+++ b/src/Core/AnsiDetector.cs
@@ -127,10 +127,8 @@ internal static bool Detect(
private static bool Detect(bool stdError, bool upgrade)
{
- // Running on Windows?
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
- // Running under ConEmu?
var conEmu = Environment.GetEnvironmentVariable("ConEmuANSI");
if (!string.IsNullOrEmpty(conEmu) && conEmu.Equals("On", StringComparison.OrdinalIgnoreCase))
{
@@ -145,7 +143,6 @@ private static bool Detect(bool stdError, bool upgrade)
private static bool DetectFromTerm()
{
- // Check if the terminal is of type ANSI/VT100/xterm compatible.
var term = Environment.GetEnvironmentVariable("TERM");
if (!string.IsNullOrWhiteSpace(term))
{
@@ -198,11 +195,9 @@ public static bool SupportsAnsi(bool upgrade, bool stdError) {
return false;
}
- // Try enable ANSI support.
mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING | DISABLE_NEWLINE_AUTO_RETURN;
if (!SetConsoleMode(@out, mode))
{
- // Enabling failed.
return false;
}
}
diff --git a/src/Core/EnvironmentUtil.cs b/src/Core/EnvironmentUtil.cs
index d102471..fefadc1 100644
--- a/src/Core/EnvironmentUtil.cs
+++ b/src/Core/EnvironmentUtil.cs
@@ -91,65 +91,54 @@ public static int GetSafeHeight(int defaultValue = Constants.DefaultTerminalHeig
public static void ResetState(bool isansi, string originalCulture, ConsoleColor originalForecolor, ConsoleColor originalBackcolor, Encoding originalInputEncoding, Encoding originalOutputEncoding)
{
+ // Each restore runs in its own try/catch so a single failure doesn't prevent the others from running.
try
{
- // try restore cursor visibility if it was changed
Console.CursorVisible = true;
}
catch (Exception)
{
- //skip Exception
}
try
{
- // try restore input encoding if it was changed
Console.InputEncoding = originalInputEncoding;
}
catch (Exception)
{
- //skip Exception
}
try
{
- // try restore output encoding if it was changed
Console.OutputEncoding = originalOutputEncoding;
}
catch (Exception)
{
- //skip Exception
}
try
{
if (isansi)
{
var (left, top) = Console.GetCursorPosition();
- // try Exit alternate screen buffer if it was used
Console.Write("\u001b[?1049l");
Console.SetCursorPosition(left, top);
}
}
catch (Exception)
{
- //skip Exception
}
try
{
- // try Restore original culture
Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo(originalCulture);
}
catch (Exception)
{
- //skip Exception
}
try
{
- // try Restore original console colors
Console.ForegroundColor = originalForecolor;
Console.BackgroundColor = originalBackcolor;
}
catch (Exception)
{
- //skip Exception
}
}
@@ -429,10 +418,8 @@ public static ColorSystem ColorSystemDetector(bool supportsAnsi)
/// True if the environment has terminal support; otherwise, false.
public static bool HasTerminalSupport()
{
- // Check if running on Windows
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
- // Check for Windows Terminal or ConEmu
string? wtSession = Environment.GetEnvironmentVariable("WT_SESSION");
string? conEmu = Environment.GetEnvironmentVariable("ConEmuANSI");
@@ -465,14 +452,12 @@ public static bool HasTerminalSupport()
}
catch
{
- //none;
}
return true;
}
}
else
{
- // Check terminal type on Unix-like systems
string term = Environment.GetEnvironmentVariable("TERM") ?? "";
return AnsiDetector.IsValidTerminal(term);
}
diff --git a/src/Core/UnicodeDetector.cs b/src/Core/UnicodeDetector.cs
index 6a37e54..0f75908 100644
--- a/src/Core/UnicodeDetector.cs
+++ b/src/Core/UnicodeDetector.cs
@@ -31,16 +31,16 @@ public static bool Detect(TextWriter buffer, AutoDetect unicode)
return true;
}
- // Regras específicas por sistema operacional
+ // OS-specific detection rules
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
- // Atalho rápido: encodings Unicode explícitos.
+ // Fast path: known Unicode code pages (UTF-8 = 65001, UTF-16 LE/BE = 1200/1201, UTF-32 LE/BE = 12000/12001).
if (encoding.CodePage is 65001 or 1200 or 1201 or 12000 or 12001)
{
return true;
}
- // Terminais modernos normalmente suportam Unicode
+ // Presence of these env vars indicates a modern terminal (Windows Terminal / ConEmu), which supports Unicode.
var wt = Environment.GetEnvironmentVariable("WT_SESSION");
var conEmu = Environment.GetEnvironmentVariable("ConEmuANSI");
@@ -52,13 +52,13 @@ public static bool Detect(TextWriter buffer, AutoDetect unicode)
RuntimeInformation.IsOSPlatform(OSPlatform.FreeBSD) ||
RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
- // Em Unix, locale UTF-8 é o melhor indicativo
+ // On Unix, the locale's UTF-8 declaration is the best indicator of Unicode support.
var lang = Environment.GetEnvironmentVariable("LC_ALL")
?? Environment.GetEnvironmentVariable("LC_CTYPE")
?? Environment.GetEnvironmentVariable("LANG")
?? string.Empty;
- // TERM=dumb geralmente não oferece recursos completos de terminal
+ // TERM=dumb means the terminal generally lacks full terminal capabilities.
var term = Environment.GetEnvironmentVariable("TERM") ?? string.Empty;
if (term.Equals("dumb", StringComparison.OrdinalIgnoreCase))
{
@@ -69,14 +69,13 @@ public static bool Detect(TextWriter buffer, AutoDetect unicode)
lang.Contains("UTF8", StringComparison.OrdinalIgnoreCase);
}
-
- //Capacidade real de codificação (regra principal)
+ // Fallback: probe the actual encoding capability directly.
var strict = Encoding.GetEncoding(
encoding.CodePage,
EncoderFallback.ExceptionFallback,
DecoderFallback.ExceptionFallback);
- // Caracteres de planos diferentes (BMP + suplementar)
+ // Deliberately spans multiple Unicode planes (BMP + supplementary) to exercise the encoder fully.
_ = strict.GetBytes("✓漢🙂");
return true;
diff --git a/src/Properties/AssemblyInfo.cs b/src/Properties/AssemblyInfo.cs
index f3124ae..394f826 100644
--- a/src/Properties/AssemblyInfo.cs
+++ b/src/Properties/AssemblyInfo.cs
@@ -6,7 +6,6 @@
using System.Resources;
using System.Runtime.CompilerServices;
-// Specifies the neutral culture for the assembly's resources
[assembly: NeutralResourcesLanguage("en-US")]
// Grants the headless VirtualTerminal test driver access to internal types (ConsoleWriter, ProfileConsole, IConsolePlus)
diff --git a/src/RuntimeEnvironment/Bitbucket.cs b/src/RuntimeEnvironment/Bitbucket.cs
index fdd40a3..cf61c91 100644
--- a/src/RuntimeEnvironment/Bitbucket.cs
+++ b/src/RuntimeEnvironment/Bitbucket.cs
@@ -1,4 +1,4 @@
-// ************************************// ***************************************************************************************
+// ***************************************************************************************
// MIT LICENCE
// Copyright 2020 Patrik Svensson, Phil Scott, Nils Andresen.
// https://spectreconsole.net
diff --git a/src/Shared/Color.cs b/src/Shared/Color.cs
index 368a9da..1e39f9c 100644
--- a/src/Shared/Color.cs
+++ b/src/Shared/Color.cs
@@ -113,7 +113,6 @@ public Color ExactOrClosest(ColorSystem system)
///
public double GetLuminance()
{
- // Convert RGB from 0-255 to 0-1
double r = R / 255.0;
double g = G / 255.0;
double b = B / 255.0;
@@ -123,7 +122,6 @@ public double GetLuminance()
g = g <= 0.03928 ? g / 12.92 : Math.Pow((g + 0.055) / 1.055, 2.4);
b = b <= 0.03928 ? b / 12.92 : Math.Pow((b + 0.055) / 1.055, 2.4);
- // Calculate relative luminance
return 0.2126 * r + 0.7152 * g + 0.0722 * b;
}
@@ -161,8 +159,6 @@ public double GetContrast(Color other)
///
public static Color GetContrastForegroundColor(Color backgroundColor)
{
- // Use relative luminance to determine if background is dark or light
- // If luminance is high (> 0.5), use black; otherwise use white
double luminance = backgroundColor.GetLuminance();
return luminance > 0.5 ? Black : White;
}
@@ -180,10 +176,8 @@ public static Color GetContrastForegroundColor(Color backgroundColor)
///
public Color AdjustForegroundColorForContrast(Color backgroundColor, double minimumContrastRatio = 2.5)
{
- // Calculate current contrast
double currentContrast = GetContrast(backgroundColor);
- // If contrast is already adequate, return the original color
if (currentContrast >= minimumContrastRatio)
{
return this;
diff --git a/src/Shared/EmacsConsoleBuffer.cs b/src/Shared/EmacsConsoleBuffer.cs
index d7f8680..d453c9e 100644
--- a/src/Shared/EmacsConsoleBuffer.cs
+++ b/src/Shared/EmacsConsoleBuffer.cs
@@ -97,33 +97,27 @@ public bool TryAcceptedReadlineConsoleKey(ConsoleKeyInfo keyinfo,int maxlength =
switch (keyinfo.Key)
{
- //toggle input replacement mode.
case ConsoleKey.Insert:
{
_overwritemode = !_overwritemode;
}
break;
- //Emacs keyboard shortcut when when have any text with length > 1
- //Transpose the previous two characters
+ // Swaps the two characters straddling the cursor (or the last two, if at the end).
case ConsoleKey.T when _enableEmacsKeys && !_readonly && keyinfo.Modifiers == ConsoleModifiers.Control && Length > 1:
{
TransposeChars();
}
break;
- //Emacs keyboard shortcut, when when have any text
- // Clears the content
case ConsoleKey.L when _enableEmacsKeys && !_readonly && keyinfo.Modifiers == ConsoleModifiers.Control && Length > 0:
{
Clear();
}
break;
- //Emacs keyboard shortcut when when have any text
- //Lowers the case of every character from the cursor's position to the end of the current word
+ // Lowercases only up to the next space, not the whole rest of the buffer.
case ConsoleKey.L when _enableEmacsKeys && !_readonly && keyinfo.Modifiers == ConsoleModifiers.Alt && Length > 0:
LowerAfterCursor();
break;
- //Emacs keyboard shortcut when when have any text
- // Clears the line content before the cursor
+ // Rebuilds the buffer from the text after the cursor, discarding everything before it.
case ConsoleKey.U when _enableEmacsKeys && !_readonly && keyinfo.Modifiers == ConsoleModifiers.Control && Length > 0:
{
string aux = ToForward();
@@ -132,13 +126,11 @@ public bool TryAcceptedReadlineConsoleKey(ConsoleKeyInfo keyinfo,int maxlength =
Position = 0;
}
break;
- //Emacs keyboard shortcut when when have any text
- //Upper the case of every character from the cursor's position to the end of the current word
+ // Uppercases only up to the next space, not the whole rest of the buffer.
case ConsoleKey.U when _enableEmacsKeys && !_readonly && keyinfo.Modifiers == ConsoleModifiers.Alt && Length > 0:
UpperAfterCursor();
break;
- //Emacs keyboard shortcut when when have any text
- //Clears the line content after the cursor
+ // Rebuilds the buffer from the text before the cursor, discarding everything after it.
case ConsoleKey.K when _enableEmacsKeys && !_readonly && keyinfo.Modifiers == ConsoleModifiers.Control && Length > 0:
{
string aux = ToBackward();
@@ -147,63 +139,41 @@ public bool TryAcceptedReadlineConsoleKey(ConsoleKeyInfo keyinfo,int maxlength =
Position = Length;
}
break;
- //Emacs keyboard shortcut when when have any text
- //Clear the word before the cursor
case ConsoleKey.W when _enableEmacsKeys && !_readonly && keyinfo.Modifiers == ConsoleModifiers.Control && Length > 0:
RemoveWordBeforeCursor();
break;
- //Emacs keyboard shortcut when when have any text
- //Capitalizes the character under the cursor and moves to the end of the word
case ConsoleKey.C when _enableEmacsKeys && !_readonly && keyinfo.Modifiers != ConsoleModifiers.Alt && Length > 0:
UpperCharMoveEndWord();
break;
- //Emacs keyboard shortcut when when have any text
- // Clear the word after the cursor
case ConsoleKey.D when _enableEmacsKeys && !_readonly &&keyinfo.Modifiers == ConsoleModifiers.Alt && Length > 0:
RemoveWordAfterCursor();
break;
- //Emacs keyboard shortcut when when have any text
- // Moves the cursor forward one word.
case ConsoleKey.F when _enableEmacsKeys && keyinfo.Modifiers == ConsoleModifiers.Alt && Length > 0:
ForwardWord();
break;
- //Emacs keyboard shortcut when when have any text
- //Moves the cursor backward one word.
case ConsoleKey.B when _enableEmacsKeys && keyinfo.Modifiers == ConsoleModifiers.Alt && Length > 0:
BackwardWord();
break;
- //Emacs keyboard shortcut when when have any text
- //Deletes the previous character (same as backspace).
case ConsoleKey.H when _enableEmacsKeys && !_readonly && keyinfo.Modifiers == ConsoleModifiers.Control && Length > 0:
case ConsoleKey.Backspace when !_readonly && keyinfo.Modifiers == 0 && Length > 0:
Backspace();
break;
- //Emacs keyboard shortcut when when have any text
- //(end) moves the cursor to the line end (equivalent to the key End).
case ConsoleKey.E when _enableEmacsKeys && keyinfo.Modifiers == ConsoleModifiers.Control && Length > 0:
case ConsoleKey.End when keyinfo.Modifiers == 0 && Length > 0:
ToEnd();
break;
- //Emacs keyboard shortcut when when have any text
- //Moves the cursor to the line start (equivalent to the key Home).
case ConsoleKey.A when _enableEmacsKeys && keyinfo.Modifiers == ConsoleModifiers.Control && Length > 0:
case ConsoleKey.Home when keyinfo.Modifiers == 0 && Length > 0:
ToStart();
break;
- //Emacs keyboard shortcut when when have any text
- //Moves the cursor back one character (equivalent to the key ←).
case ConsoleKey.B when _enableEmacsKeys && keyinfo.Modifiers == ConsoleModifiers.Control && Length > 0:
case ConsoleKey.LeftArrow when keyinfo.Modifiers == 0 && Length > 0:
Backward();
break;
- //Emacs keyboard shortcut when when have any text
- //Moves the cursor forward one character (equivalent to the key →).
case ConsoleKey.F when _enableEmacsKeys && keyinfo.Modifiers == ConsoleModifiers.Control && Length > 0:
case ConsoleKey.RightArrow when keyinfo.Modifiers == 0 && Length > 0:
Forward();
break;
- //Emacs keyboard shortcut when when have any text
- //Delete the current character (then equivalent to the key Delete).
case ConsoleKey.D when _enableEmacsKeys && !_readonly && keyinfo.Modifiers == ConsoleModifiers.Control && Length > 0:
case ConsoleKey.Delete when !_readonly && keyinfo.Modifiers == 0 && Length > 0:
Delete();
diff --git a/src/Shared/Style.cs b/src/Shared/Style.cs
index b79e71e..c44bd82 100644
--- a/src/Shared/Style.cs
+++ b/src/Shared/Style.cs
@@ -16,6 +16,7 @@ namespace ConsolePlusLibrary
///
///
/// Use the primary constructor to specify explicit colors and an strategy, or the
+ /// implicit conversion from for a quick foreground-only style with a default background.
///
/// Foreground used when writing content.
/// Background used behind the content.
diff --git a/tests/ConsolePlus.Tests/Rendering/BannerWidgetTests.cs b/tests/ConsolePlus.Tests/Rendering/BannerWidgetTests.cs
index 4af8523..e8d479f 100644
--- a/tests/ConsolePlus.Tests/Rendering/BannerWidgetTests.cs
+++ b/tests/ConsolePlus.Tests/Rendering/BannerWidgetTests.cs
@@ -8,9 +8,9 @@
namespace ConsolePlus.Tests.Rendering
{
- // BannerWidget (Figlet/BannerWidget.cs) — camada 2 (precisa de IConsolePlus para Show(), usa
- // VirtualTerminal). Foco no cálculo de largura da borda em GetSegments (largura = maior linha
- // renderizada), usando a mesma fonte .flf mínima de FigletFontTests para saída previsível.
+ // BannerWidget (Figlet/BannerWidget.cs) needs IConsolePlus for Show() and uses VirtualTerminal.
+ // Focus is on the border-width calculation in GetSegments (width = widest rendered line), using
+ // the same minimal .flf font as FigletFontTests for predictable output.
public class BannerWidgetTests
{
private static Stream MinimalFont() => new MemoryStream(Encoding.UTF8.GetBytes(
diff --git a/tests/ConsolePlus.Tests/Rendering/OverflowTests.cs b/tests/ConsolePlus.Tests/Rendering/OverflowTests.cs
index bbd4e41..9128035 100644
--- a/tests/ConsolePlus.Tests/Rendering/OverflowTests.cs
+++ b/tests/ConsolePlus.Tests/Rendering/OverflowTests.cs
@@ -6,7 +6,7 @@
namespace ConsolePlus.Tests.Rendering
{
// ConsoleWriter.WriteOutput(Fragment[]) overflow handling (ConsoleAbstractions/ConsoleWriter.cs:98-181).
- // Needs a real Width/CursorLeft, hence VirtualTerminal (camada 2) rather than a pure unit test.
+ // Needs a real Width/CursorLeft, hence VirtualTerminal rather than a pure unit test.
public class OverflowTests
{
private static VirtualTerminal MakeTerminal(bool supportsUnicode) => VirtualTerminal.Create(o =>
diff --git a/tests/ConsolePlus.Tests/Unit/AnsiColorBuilderTests.cs b/tests/ConsolePlus.Tests/Unit/AnsiColorBuilderTests.cs
index 015a3bb..2a30148 100644
--- a/tests/ConsolePlus.Tests/Unit/AnsiColorBuilderTests.cs
+++ b/tests/ConsolePlus.Tests/Unit/AnsiColorBuilderTests.cs
@@ -6,7 +6,7 @@
namespace ConsolePlus.Tests.Unit
{
- // AnsiColorBuilder.Build (ConsoleAbstractions/AnsiColorBuilder.cs) — camada 1, sem VirtualTerminal.
+ // AnsiColorBuilder.Build (ConsoleAbstractions/AnsiColorBuilder.cs) — no VirtualTerminal needed.
// Color(255,0,0) matches palette index 9 in both the Standard and FourBit tables
// (ColorPalette.cs:128,140 — Color equality only compares R/G/B, ignoring Number).
public class AnsiColorBuilderTests
diff --git a/tests/ConsolePlus.Tests/Unit/AnsiCommandsTests.cs b/tests/ConsolePlus.Tests/Unit/AnsiCommandsTests.cs
index 1b17392..5f23857 100644
--- a/tests/ConsolePlus.Tests/Unit/AnsiCommandsTests.cs
+++ b/tests/ConsolePlus.Tests/Unit/AnsiCommandsTests.cs
@@ -11,12 +11,12 @@
namespace ConsolePlus.Tests.Unit
{
- // AnsiCommands (ConsoleAbstractions/AnsiCommands.cs) — camada 1, unidade pura: verifica a
- // string ANSI literal emitida por cada comando. Usa um IConsolePlus fake mínimo em vez de
- // VirtualTerminal porque AnsiScreenInterpreter só reconhece o subconjunto que a produção
- // realmente emite (CUP/EL/ED/SGR/cursor/alt-screen) — vários comandos aqui (setas, tabulação,
- // scroll, save/restore cursor) não são interpretados e lançariam NotSupportedException.
- // AnsiCommands só toca Out/Error/WriteToErrorOutput em IConsolePlus; todo o resto é stub.
+ // AnsiCommands (ConsoleAbstractions/AnsiCommands.cs) — pure unit test: verifies the literal ANSI
+ // string emitted by each command. Uses a minimal fake IConsolePlus instead of VirtualTerminal
+ // because AnsiScreenInterpreter only recognizes the subset that production actually emits
+ // (CUP/EL/ED/SGR/cursor/alt-screen) — several commands here (arrows, tab, scroll, save/restore
+ // cursor) are not interpreted and would throw NotSupportedException.
+ // AnsiCommands only touches Out/Error/WriteToErrorOutput on IConsolePlus; everything else is a stub.
public class AnsiCommandsTests
{
private sealed class RecordingConsole : IConsolePlus
diff --git a/tests/ConsolePlus.Tests/Unit/ColorExtensionsTests.cs b/tests/ConsolePlus.Tests/Unit/ColorExtensionsTests.cs
index ebf403a..06a0592 100644
--- a/tests/ConsolePlus.Tests/Unit/ColorExtensionsTests.cs
+++ b/tests/ConsolePlus.Tests/Unit/ColorExtensionsTests.cs
@@ -4,7 +4,7 @@
namespace ConsolePlus.Tests.Unit
{
- // ColorExtensions.Weighted (Shared/ColorExtensions.cs) — camada 1, unidade pura.
+ // ColorExtensions.Weighted (Shared/ColorExtensions.cs) — pure unit test, no dependencies.
public class ColorExtensionsTests
{
private static readonly Color Base = new(200, 100, 50);
diff --git a/tests/ConsolePlus.Tests/Unit/ColorJsonConverterTests.cs b/tests/ConsolePlus.Tests/Unit/ColorJsonConverterTests.cs
index 0987706..8ec450f 100644
--- a/tests/ConsolePlus.Tests/Unit/ColorJsonConverterTests.cs
+++ b/tests/ConsolePlus.Tests/Unit/ColorJsonConverterTests.cs
@@ -7,7 +7,7 @@
namespace ConsolePlus.Tests.Unit
{
- // ColorJsonConverter (Core/ColorJsonConverter.cs) — camada 1, unidade pura.
+ // ColorJsonConverter (Core/ColorJsonConverter.cs)
public class ColorJsonConverterTests
{
private static JsonSerializerOptions Options() => new() { Converters = { new ColorJsonConverter() } };
diff --git a/tests/ConsolePlus.Tests/Unit/ColorPaletteTests.cs b/tests/ConsolePlus.Tests/Unit/ColorPaletteTests.cs
index 1817f9e..8156640 100644
--- a/tests/ConsolePlus.Tests/Unit/ColorPaletteTests.cs
+++ b/tests/ConsolePlus.Tests/Unit/ColorPaletteTests.cs
@@ -6,8 +6,7 @@
namespace ConsolePlus.Tests.Unit
{
- // ColorPalette (Core/ColorPalette.cs) — camada 1, unidade pura. Casamento exato/mais próximo
- // de cor para as profundidades FourBit/Standard/TrueColor.
+ // ColorPalette (Core/ColorPalette.cs)
public class ColorPaletteTests
{
[Fact]
diff --git a/tests/ConsolePlus.Tests/Unit/ColorTableCssTests.cs b/tests/ConsolePlus.Tests/Unit/ColorTableCssTests.cs
index 9047daf..a9afdae 100644
--- a/tests/ConsolePlus.Tests/Unit/ColorTableCssTests.cs
+++ b/tests/ConsolePlus.Tests/Unit/ColorTableCssTests.cs
@@ -6,8 +6,8 @@
namespace ConsolePlus.Tests.Unit
{
- // ColorTableCss (Core/ColorTableCss.cs) — camada 1, unidade pura. Tabela de nomes CSS e
- // resolução de variantes com peso (ex.: "red500").
+ // ColorTableCss (Core/ColorTableCss.cs) — CSS name table and weighted-variant resolution
+ // (e.g. "red500").
public class ColorTableCssTests
{
[Fact]
diff --git a/tests/ConsolePlus.Tests/Unit/ColorTests.cs b/tests/ConsolePlus.Tests/Unit/ColorTests.cs
index b143cd0..891a5c9 100644
--- a/tests/ConsolePlus.Tests/Unit/ColorTests.cs
+++ b/tests/ConsolePlus.Tests/Unit/ColorTests.cs
@@ -5,8 +5,7 @@
namespace ConsolePlus.Tests.Unit
{
- // Color (Shared/Color.cs) — camada 1, unidade pura: Blend, luminance/contrast (WCAG), hex
- // parsing, numeric/ConsoleColor conversions.
+ // Color (Shared/Color.cs)
public class ColorTests
{
[Fact]
diff --git a/tests/ConsolePlus.Tests/Unit/ConsoleKeyInfoExtensionsTests.cs b/tests/ConsolePlus.Tests/Unit/ConsoleKeyInfoExtensionsTests.cs
index f385643..7664d57 100644
--- a/tests/ConsolePlus.Tests/Unit/ConsoleKeyInfoExtensionsTests.cs
+++ b/tests/ConsolePlus.Tests/Unit/ConsoleKeyInfoExtensionsTests.cs
@@ -6,9 +6,9 @@
namespace ConsolePlus.Tests.Unit
{
- // ConsoleKeyInfoExtensions (Shared/ConsoleKeyInfoExtensions.cs) — camada 1, unidade pura.
- // Decide, para todo controle, se uma tecla "e" Enter/seta/Home/etc; ja tinha 1 bug real conhecido
- // (IsPressEnterKey diverge Windows/nao-Windows) que motivou esta suite completa.
+ // ConsoleKeyInfoExtensions (Shared/ConsoleKeyInfoExtensions.cs) decides, for every control,
+ // whether a key is Enter/arrow/Home/etc; a real known bug (IsPressEnterKey diverges between
+ // Windows and non-Windows) motivated this full suite.
public class ConsoleKeyInfoExtensionsTests
{
private static ConsoleKeyInfo K(ConsoleKey key, ConsoleModifiers modifiers = 0, char keyChar = '\0')
@@ -66,7 +66,7 @@ public void IsPressSpecialKey_matches_the_given_key_and_modifier_exactly()
K(ConsoleKey.X, ConsoleModifiers.Control).IsPressSpecialKey(ConsoleKey.S, ConsoleModifiers.Control).Should().BeFalse();
}
- // ---- IsPressEnterKey: OS-dependent (the bug found in Fase 1) ----
+ // ---- IsPressEnterKey: OS-dependent ----
[Fact]
public void Enter_key_alone_is_accepted_on_any_platform()
@@ -88,7 +88,7 @@ public void CtrlJ_is_accepted_as_enter_only_when_emacskeys_is_true()
public void Enter_detection_matches_the_current_platforms_semantics()
{
// No Skippable-fact package here on purpose: this runs the branch matching whatever OS
- // executes it, and the project's own CI matrix (Windows + Linux, TEST-PLAN.md section 8)
+ // executes it, and the project's own CI matrix (Windows + Linux)
// exercises both branches across the two runners without adding a test dependency.
if (OperatingSystem.IsWindows())
{
diff --git a/tests/ConsolePlus.Tests/Unit/CultureExtensionsTests.cs b/tests/ConsolePlus.Tests/Unit/CultureExtensionsTests.cs
index 44ccdc9..c43b95a 100644
--- a/tests/ConsolePlus.Tests/Unit/CultureExtensionsTests.cs
+++ b/tests/ConsolePlus.Tests/Unit/CultureExtensionsTests.cs
@@ -4,7 +4,7 @@
namespace ConsolePlus.Tests.Unit
{
- // CultureExtensions.ExistsCulture (Shared/CultureExtensions.cs) — camada 1, unidade pura.
+ // CultureExtensions.ExistsCulture (Shared/CultureExtensions.cs)
public class CultureExtensionsTests
{
[Fact]
diff --git a/tests/ConsolePlus.Tests/Unit/CultureInfoJsonConverterTests.cs b/tests/ConsolePlus.Tests/Unit/CultureInfoJsonConverterTests.cs
index b83ab7c..1d64b97 100644
--- a/tests/ConsolePlus.Tests/Unit/CultureInfoJsonConverterTests.cs
+++ b/tests/ConsolePlus.Tests/Unit/CultureInfoJsonConverterTests.cs
@@ -7,7 +7,7 @@
namespace ConsolePlus.Tests.Unit
{
- // CultureInfoJsonConverter (Core/CultureInfoJsonConverter.cs) — camada 1, unidade pura.
+ // CultureInfoJsonConverter (Core/CultureInfoJsonConverter.cs)
public class CultureInfoJsonConverterTests
{
private static JsonSerializerOptions Options() => new() { Converters = { new CultureInfoJsonConverter() } };
diff --git a/tests/ConsolePlus.Tests/Unit/DashUtilTests.cs b/tests/ConsolePlus.Tests/Unit/DashUtilTests.cs
index 213f253..74dfe8e 100644
--- a/tests/ConsolePlus.Tests/Unit/DashUtilTests.cs
+++ b/tests/ConsolePlus.Tests/Unit/DashUtilTests.cs
@@ -6,8 +6,8 @@
namespace ConsolePlus.Tests.Unit
{
- // DashUtil (Core/DashUtil.cs) — camada 1 lógica, mas depende de IConsole.SupportsUnicode,
- // por isso usa VirtualTerminal (camada 2 mínima) em vez de um double manual.
+ // DashUtil (Core/DashUtil.cs) is pure logic but depends on IConsole.SupportsUnicode, so it uses
+ // a minimal VirtualTerminal instead of a hand-rolled test double.
public class DashUtilTests
{
private static VirtualTerminal Terminal(bool supportsUnicode) => VirtualTerminal.Create(o => o.SupportsUnicode = supportsUnicode);
diff --git a/tests/ConsolePlus.Tests/Unit/DisplayLengthTests.cs b/tests/ConsolePlus.Tests/Unit/DisplayLengthTests.cs
index 78b9e3f..6ae3214 100644
--- a/tests/ConsolePlus.Tests/Unit/DisplayLengthTests.cs
+++ b/tests/ConsolePlus.Tests/Unit/DisplayLengthTests.cs
@@ -5,7 +5,7 @@
namespace ConsolePlus.Tests.Unit
{
- // string.GetDisplayLength (Shared/StringExtensions.cs:59) — camada 1, sem VirtualTerminal.
+ // string.GetDisplayLength (Shared/StringExtensions.cs:59)
public class DisplayLengthTests
{
[Fact]
diff --git a/tests/ConsolePlus.Tests/Unit/EmacsConsoleBufferTests.cs b/tests/ConsolePlus.Tests/Unit/EmacsConsoleBufferTests.cs
index 13073f5..4966e82 100644
--- a/tests/ConsolePlus.Tests/Unit/EmacsConsoleBufferTests.cs
+++ b/tests/ConsolePlus.Tests/Unit/EmacsConsoleBufferTests.cs
@@ -5,8 +5,8 @@
namespace ConsolePlus.Tests.Unit
{
- // EmacsConsoleBuffer (Shared/EmacsConsoleBuffer.cs) — camada 1, unidade pura. Backs the answer
- // buffer of most interactive controls (Input, and others via BaseControlPrompt's answer viewport).
+ // EmacsConsoleBuffer (Shared/EmacsConsoleBuffer.cs) backs the answer buffer of most interactive
+ // controls (Input, and others via BaseControlPrompt's answer viewport).
// Every Emacs key binding implemented by TryAcceptedReadlineConsoleKey is covered; exact
// expected values were confirmed by running each operation and observing the real output
// (Ctrl+W/Ctrl+T/Alt+U/Alt+L/Alt+D/Alt+F/Alt+B/Ctrl+C have non-obvious boundary behavior that
diff --git a/tests/ConsolePlus.Tests/Unit/EmojiGroupResolutionTests.cs b/tests/ConsolePlus.Tests/Unit/EmojiGroupResolutionTests.cs
index 0c87115..e57ddc6 100644
--- a/tests/ConsolePlus.Tests/Unit/EmojiGroupResolutionTests.cs
+++ b/tests/ConsolePlus.Tests/Unit/EmojiGroupResolutionTests.cs
@@ -4,9 +4,9 @@
namespace ConsolePlus.Tests.Unit
{
- // Emoji.GetByName group-qualified shortcode resolution (Shared/Emoji.cs:51-78,104-157) —
- // camada 1, unidade pura. Deepens EmojiTests.cs (which only covers plain names) with the
- // "group/name" form and its compatibility aliases.
+ // Emoji.GetByName group-qualified shortcode resolution (Shared/Emoji.cs:51-78,104-157).
+ // Deepens EmojiTests.cs (which only covers plain names) with the "group/name" form and its
+ // compatibility aliases.
public class EmojiGroupResolutionTests
{
[Fact]
diff --git a/tests/ConsolePlus.Tests/Unit/EmojiTests.cs b/tests/ConsolePlus.Tests/Unit/EmojiTests.cs
index 37a102f..efc25d3 100644
--- a/tests/ConsolePlus.Tests/Unit/EmojiTests.cs
+++ b/tests/ConsolePlus.Tests/Unit/EmojiTests.cs
@@ -4,7 +4,7 @@
namespace ConsolePlus.Tests.Unit
{
- // Emoji.GetByName (Shared/Emoji.cs:51) — camada 1, sem VirtualTerminal.
+ // Emoji.GetByName (Shared/Emoji.cs:51)
public class EmojiTests
{
[Theory]
diff --git a/tests/ConsolePlus.Tests/Unit/FigletFontTests.cs b/tests/ConsolePlus.Tests/Unit/FigletFontTests.cs
index 857ff1b..4baf36c 100644
--- a/tests/ConsolePlus.Tests/Unit/FigletFontTests.cs
+++ b/tests/ConsolePlus.Tests/Unit/FigletFontTests.cs
@@ -9,9 +9,8 @@
namespace ConsolePlus.Tests.Unit
{
- // FigletFont (Figlet/FigletFont.cs) — camada 1, unidade pura. Parser do formato .flf (FIGlet
- // font). Fixtures são fontes .flf mínimas construídas em memória (Stream), sem tocar o recurso
- // embutido real nem o disco.
+ // FigletFont (Figlet/FigletFont.cs) — parser for the .flf (FIGlet font) format. Fixtures are
+ // minimal .flf fonts built in memory (Stream), without touching the real embedded resource or disk.
public class FigletFontTests
{
// Minimal valid .flf: signature "flf2a", hardblank '$', Height=1 (one line per glyph),
diff --git a/tests/ConsolePlus.Tests/Unit/MarkupColorTokenizerTests.cs b/tests/ConsolePlus.Tests/Unit/MarkupColorTokenizerTests.cs
index 822c4bc..77af218 100644
--- a/tests/ConsolePlus.Tests/Unit/MarkupColorTokenizerTests.cs
+++ b/tests/ConsolePlus.Tests/Unit/MarkupColorTokenizerTests.cs
@@ -4,9 +4,9 @@
namespace ConsolePlus.Tests.Unit
{
- // MarkupColorTokenizer (Core/MarkupColorTokenizer.cs) — camada 1, unidade pura. Parser
- // caractere-a-caractere por trás de Fragment.FromText; fault-tolerant por design (markup
- // malformado degrada para texto puro em vez de lançar).
+ // MarkupColorTokenizer (Core/MarkupColorTokenizer.cs) — the character-by-character parser
+ // behind Fragment.FromText; fault-tolerant by design (malformed markup degrades to plain text
+ // instead of throwing).
public class MarkupColorTokenizerTests
{
[Fact]
diff --git a/tests/ConsolePlus.Tests/Unit/MarkupHelperTests.cs b/tests/ConsolePlus.Tests/Unit/MarkupHelperTests.cs
index b54a610..ea0700d 100644
--- a/tests/ConsolePlus.Tests/Unit/MarkupHelperTests.cs
+++ b/tests/ConsolePlus.Tests/Unit/MarkupHelperTests.cs
@@ -4,8 +4,8 @@
namespace ConsolePlus.Tests.Unit
{
- // Markup.Escape/Remove/Length (Shared/Markup.cs) — camada 1, unidade pura. Built on top of
- // MarkupColorTokenizer (already covered in MarkupColorTokenizerTests).
+ // Markup.Escape/Remove/Length (Shared/Markup.cs) — built on top of MarkupColorTokenizer
+ // (already covered in MarkupColorTokenizerTests).
public class MarkupHelperTests
{
[Fact]
diff --git a/tests/ConsolePlus.Tests/Unit/MarkupTests.cs b/tests/ConsolePlus.Tests/Unit/MarkupTests.cs
index 25cfa3f..bec609f 100644
--- a/tests/ConsolePlus.Tests/Unit/MarkupTests.cs
+++ b/tests/ConsolePlus.Tests/Unit/MarkupTests.cs
@@ -5,7 +5,7 @@
namespace ConsolePlus.Tests.Unit
{
- // Fragment.FromText (Shared/Fragment.cs:74) — camada 1, sem VirtualTerminal.
+ // Fragment.FromText (Shared/Fragment.cs:74)
public class MarkupTests
{
private static readonly Style DefaultStyle = new(Color.White, Color.Black);
diff --git a/tests/ConsolePlus.Tests/Unit/ProfileExtensionsTests.cs b/tests/ConsolePlus.Tests/Unit/ProfileExtensionsTests.cs
index ffd6048..1bd2956 100644
--- a/tests/ConsolePlus.Tests/Unit/ProfileExtensionsTests.cs
+++ b/tests/ConsolePlus.Tests/Unit/ProfileExtensionsTests.cs
@@ -10,27 +10,26 @@
namespace ConsolePlus.Tests.Unit
{
- // ProfileExtensions.EnrichersCI (ConsoleAbstractions/ProfileExtensions.cs) — despacha para 14
- // detectores de CI (RuntimeEnvironment/*.cs), cada um IProfileEnrich.TryEnrich checando uma ou
- // mais env vars; o primeiro que der match "ganha" e interrompe o loop.
+ // ProfileExtensions.EnrichersCI (ConsoleAbstractions/ProfileExtensions.cs) dispatches to 14 CI
+ // detectors (RuntimeEnvironment/*.cs), each an IProfileEnrich.TryEnrich checking one or more env
+ // vars; the first one that matches "wins" and stops the loop.
//
- // Blocker de testabilidade: BaseClassCI cacheia Environment.GetEnvironmentVariables() num campo
- // private static compartilhado por TODAS as subclasses, populado uma vez e nunca invalidado
- // (confirmado por probe empírico). Isso impede testar múltiplos cenários de CI no mesmo processo
- // de teste sem resetar o cache manualmente — daqui o uso de reflection (mesmo padrão já usado
- // para métodos privados estáticos, ex. MaskEditControl.NormalizeStringMask) para zerar o campo
- // antes de cada cenário, sem alterar o código de produção.
+ // Testability blocker: BaseClassCI caches Environment.GetEnvironmentVariables() in a private
+ // static field shared by ALL subclasses, populated once and never invalidated (confirmed by
+ // empirical probe). This prevents testing multiple CI scenarios in the same test process without
+ // manually resetting the cache — hence the use of reflection (the same pattern already used for
+ // private static methods, e.g. MaskEditControl.NormalizeStringMask) to zero out the field before
+ // each scenario, without changing production code.
//
- // Precisa do GlobalStateCollection (DisableParallelization) porque ConsolePlusExtendsTests
- // também toca esse mesmo cache indiretamente (via inicialização real do singleton
- // ConsolePlusLibrary.ConsolePlus) — sem isolamento as duas classes competem pelo mesmo campo
- // static entre threads paralelas do xUnit (achado real, flake intermitente em CI no macOS,
- // 2026-07-25).
+ // Needs GlobalStateCollection (DisableParallelization) because ConsolePlusExtendsTests also
+ // touches this same cache indirectly (via real initialization of the ConsolePlusLibrary.ConsolePlus
+ // singleton) — without isolation the two classes race on the same static field across xUnit's
+ // parallel threads (real finding, intermittent flake in CI on macOS, 2026-07-25).
[Collection(GlobalStateCollection.Name)]
public class ProfileExtensionsTests : IDisposable
{
- // Todas as env vars conhecidas pelos 14 detectores, para isolar cada teste do ambiente real
- // (inclusive quando este próprio test suite roda dentro de um CI real).
+ // All env vars known to the 14 detectors, to isolate each test from the real environment
+ // (including when this very test suite runs inside a real CI).
private static readonly string[] AllKnownCiVars =
[
"APPVEYOR", "AZURE_PIPELINES", "bamboo_buildNumber",
diff --git a/tests/ConsolePlus.Tests/Unit/StringExtensionsNewLineTests.cs b/tests/ConsolePlus.Tests/Unit/StringExtensionsNewLineTests.cs
index 1d84bcc..2ce9807 100644
--- a/tests/ConsolePlus.Tests/Unit/StringExtensionsNewLineTests.cs
+++ b/tests/ConsolePlus.Tests/Unit/StringExtensionsNewLineTests.cs
@@ -5,9 +5,9 @@
namespace ConsolePlus.Tests.Unit
{
- // StringExtensions.NormalizeNewLines/SplitLines (Shared/StringExtensions.cs) — camada 1,
- // unidade pura, mas com comportamento explicitamente dependente de Environment.NewLine —
- // asserções usam Environment.NewLine em vez de "\r\n" hardcoded para funcionar em Windows e Linux.
+ // StringExtensions.NormalizeNewLines/SplitLines (Shared/StringExtensions.cs) — behavior is
+ // explicitly dependent on Environment.NewLine, so assertions use Environment.NewLine instead of
+ // a hardcoded "\r\n" to work on both Windows and Linux.
public class StringExtensionsNewLineTests
{
[Fact]
diff --git a/tests/ConsolePlus.Tests/Unit/StyleExtensionsTests.cs b/tests/ConsolePlus.Tests/Unit/StyleExtensionsTests.cs
index 086583a..2574b83 100644
--- a/tests/ConsolePlus.Tests/Unit/StyleExtensionsTests.cs
+++ b/tests/ConsolePlus.Tests/Unit/StyleExtensionsTests.cs
@@ -4,8 +4,8 @@
namespace ConsolePlus.Tests.Unit
{
- // StyleExtensions (Shared/StyleExtensions.cs) — camada 1, unidade pura. Fluent "with"-style
- // helpers used throughout production to derive one Style from another.
+ // StyleExtensions (Shared/StyleExtensions.cs) — fluent "with"-style helpers used throughout
+ // production to derive one Style from another.
public class StyleExtensionsTests
{
private static readonly Style Base = new(new Color(1, 2, 3), new Color(4, 5, 6), Overflow.Crop);
diff --git a/tests/ConsolePlus.Tests/Unit/TextWriterExtensionsTests.cs b/tests/ConsolePlus.Tests/Unit/TextWriterExtensionsTests.cs
index ae2c0d8..6fb7dbc 100644
--- a/tests/ConsolePlus.Tests/Unit/TextWriterExtensionsTests.cs
+++ b/tests/ConsolePlus.Tests/Unit/TextWriterExtensionsTests.cs
@@ -5,10 +5,10 @@
namespace ConsolePlus.Tests.Unit
{
- // TextWriterExtensions (Core/TextWriterExtensions.cs) — camada 1, unidade pura: pure reference
- // comparison against System.Console.Out/Error. Only reads the singleton (no redirection), so
- // it is safe against the "avoid touching the ConsolePlus singleton" rule in TEST-PLAN.md — this
- // is System.Console, a different singleton, and only its getter is read, never mutated.
+ // TextWriterExtensions (Core/TextWriterExtensions.cs) — pure reference comparison against
+ // System.Console.Out/Error. Only reads the singleton (no redirection), so it is safe against
+ // the "avoid touching the ConsolePlus singleton" rule — this is System.Console, a different
+ // singleton, and only its getter is read, never mutated.
public class TextWriterExtensionsTests
{
[Fact]
diff --git a/tests/TEST-PLAN.md b/tests/TEST-PLAN.md
deleted file mode 100644
index 9e93cfb..0000000
--- a/tests/TEST-PLAN.md
+++ /dev/null
@@ -1,1269 +0,0 @@
-# Plano de Testes — ConsolePlus + PromptPlus
-
-> Driver de console **headless** para validar **posicionamento** e **estilo** dos dois produtos,
-> rodando em **Windows e Linux**. Decisão de isolamento: **B — isolamento total, driver por projeto**.
-> Status: **Fase 1 concluída + cobertura extra de `EmacsConsoleBuffer` (2026-07-22)** — piloto Input+Select
-> implementado, mais suíte exaustiva de todas as key bindings emacs (`EmacsConsoleBuffer`, usada pela
-> maioria dos controles interativos para editar o buffer de resposta). 69 testes verdes
-> (59 ConsolePlus + 10 PromptPlus) em net10.0/net9.0/net8.0 no Windows; Linux cobre-se via CI (seção 8,
-> ainda não executado). 1 snapshot Verify estabelecido. Próxima: Fase 2 (rollout dos demais controles).
-
----
-
-## 1. Objetivo e escopo
-
-- Testar, de forma automatizada, determinística e cross-platform, as funcionalidades de **ConsolePlus**
- (camada de terminal) e **PromptPlus** (controles/widgets).
-- Validar **onde** cada glifo é escrito (posição) e **com qual estilo** (fg/bg e, quando modelado, atributos).
-- Rodar 100% headless em CI (sem terminal real), idêntico em Windows e Linux.
-
-**Fora de escopo (por decisão de ROI):** paridade pixel/glifo com terminais reais, timing de animações,
-detecção de capacidade dependente de ambiente real. Ver seção 9.
-
----
-
-## 2. Restrições de testabilidade descobertas no código (base das decisões)
-
-| Fato | Arquivo | Consequência |
-|---|---|---|
-| `ConsolePlus` é `static` + `static ctor` (detecta ambiente, cria driver único, troca encoding, inicia `Task` de monitor de tamanho, registra `ProcessExit`/`CancelKeyPress`) | `ConsolePlus.Startup.cs` | Tocar o singleton em teste = estado global; seria serial e sujo. **Evitar.** |
-| Saída vai para `Console.Out`/`Console.Error` singletons (`Out => Console.Out`) | `AnsiConsoleAdapter.cs` | Capturar só `Console.Out` não paraleliza. |
-| Estilo e cursor são **ANSI inline** (SGR + CSI) quando `SupportsAnsi` | `ConsoleWriter.cs`, `AnsiCommands.cs` | Observáveis se interpretarmos o ANSI. |
-| `CursorLeft/Top`, `Width/Height`, `GetCursorPosition()` leem o **`System.Console` real** (defaults em `IOException`) | `EnvironmentUtil.cs`, `AnsiConsoleAdapter.cs` | Capturar `Console.Out` quebra o *read-back* → **inviabiliza testar os controles** que dependem dele. |
-| `ReadKey` usa `Console.KeyAvailable`/`Console.ReadKey` (não redirecionável por `SetIn`) | `AnsiConsoleAdapter.cs`, `BaseControlPrompt.WaitKeypress` | Input interativo precisa de **fila de teclas própria**. |
-| **Seam de injeção**: `internal sealed PromptPlusControls(IConsole, PromptConfig)`; `BaseControlPrompt(... IConsole console ...)` | `Core/PromptControls.cs`, `Controls/Common/BaseControlPrompt.cs` | Com `InternalsVisibleTo`, dá para instanciar qualquer controle contra um `IConsole` **falso**. |
-| PromptPlus já depende de ConsolePlus (`_console = ConsolePlus.Driver`) | `PromptPlus.Startup.cs` | Driver de teste baseado em ConsolePlus **não cria acoplamento novo**. |
-| `PromptPlus/src/PromptPlus.csproj:69` referencia ConsolePlus via **`PackageReference` NuGet** (`ConsolePlus.net 0.5.2-Beta`), não via `ProjectReference` para `ConsolePlus/src` | `PromptPlus.csproj` | Um `ProjectReference` de `PromptPlus.Tests` → `PromptPlus/src` traria o ConsolePlus **publicado**, não o build local — o `InternalsVisibleTo` adicionado em `ConsolePlus/src` (seção 6) não alcançaria esse assembly. **Decisão D6, ver seção 12.** |
-
-**Conclusão:** a estratégia viável é um **terminal virtual em memória que implementa `IConsole`/`IConsolePlus`**,
-injetado nos controles, reutilizando o writer real do ConsolePlus. Capturar `Console.Out` sozinho **não serve**
-para o PromptPlus.
-
-> Identidade atual do projeto: `AssemblyName`/`PackageId` = **`ConsolePlus.net`**, `RootNamespace` = `ConsolePlusLibrary`.
-> (Artefatos `obj/**/TerminalPlus.*` são resquício de rename antigo — lixo, ignorar/limpar.)
-
----
-
-## 3. Estratégia escolhida
-
-**VirtualTerminal**: implementação in-memory de `IConsole` (+ `IConsolePlus`) que **hospeda o `ConsoleWriter`/
-`AnsiCommands`/`AnsiColorBuilder` reais** do ConsolePlus. O ANSI emitido pelo código de produção é **interpretado**
-por um `TextWriter` que carimba células `(Rune, Style)` num *grid*. O grid é a fonte da verdade para o *read-back*
-de cursor/tamanho — mais fiel e determinístico que o adapter real.
-
-Camadas de teste (ordem de valor):
-1. **Unidade pura** (ConsolePlus): `Fragment.FromText` (markup), `AnsiColorBuilder` (cor→SGR nas 3 profundidades),
- Emoji, overflow Crop/Ellipsis, `GetDisplayLength` (largura CJK/emoji). Não precisa de terminal.
-2. **Render + estado via VirtualTerminal** (ambos): layout inicial, item selecionado, cursor final, máquinas de estado
- (navegação, edição/máscara, validação, filtros, paginação), viewport/ellipsis lateral.
-3. **Resize/relayout** (PromptPlus): poucos cenários canônicos via `SizeChanged` + mudança de `Width/Height`.
-4. **PTY E2E** (opcional, poucos smokes): ConPTY/pty rodando samples, snapshot da tela — só para fidelidade.
-
----
-
-## 4. Estrutura de projetos — decisão B (isolamento total)
-
-Cada projeto de teste é **autossuficiente**: contém sua própria cópia do driver, sem `ProjectReference` para
-uma lib de testes compartilhada. Nenhum acoplamento de binário entre `ConsolePlus.Tests` e `PromptPlus.Tests`.
-
-```
-tests/
- ConsolePlus.Tests/
- Driver/ # driver isolado desta suíte
- VirtualTerminal.cs
- VirtualScreen.cs
- AnsiScreenInterpreter.cs
- InputQueue.cs
- ScreenAssertions.cs
- VirtualTerminalOptions.cs
- Unit/ # camada 1 (componentes puros)
- Rendering/ # camada 2 (via VirtualTerminal)
- ConsolePlus.Tests.csproj # ProjectReference -> ../../ConsolePlus/src/ConsolePlus.csproj
- PromptPlus.Tests/
- Driver/ # MESMO driver, cópia isolada
- ...
- Controls/ # camada 2 por controle
- Resize/ # camada 3
- PromptPlus.Tests.csproj # ProjectReference -> ../../PromptPlus/src/PromptPlus.csproj
-```
-
-### 4.1 Mecanismo de "cópia" do driver — decisão interna a B
-
-Para honrar "driver por projeto" **sem** manutenção em dobro divergir, o padrão recomendado é
-**source linkado a partir de uma fonte única**, compilado dentro de cada suíte (cada assembly de teste
-sai self-contained; não há dependência de binário/lib compartilhada — o isolamento de saída de B é mantido):
-
-```
-tests/_driver-src/*.cs # fonte única do driver (não é projeto)
-```
-```xml
-
-
-
-
-```
-
-- **Recomendado (default):** linked source acima → um só código-fonte, duas compilações isoladas.
-- **Alternativa (isolamento estrito no nível de fonte):** copiar fisicamente os arquivos em cada `Driver/`.
- Custo: duplicação real da peça mais complexa; usar só se a exigência for zero compartilhamento inclusive de fonte.
-
-> **Decisão em aberto (D1):** confirmar linked-source (default) vs cópia física. Ver seção 12.
-
-> **Atualização 2026-07-23 — D1 revertida, e a estrutura acima mudou de local.** Depois de fechada,
-> D1 (linked-source único em `tests/_driver-src` no topo do repo) foi invalidada: ConsolePlus e
-> PromptPlus são dois repositórios GitHub **distintos**, cada um com seu próprio `.git` — só coexistem
-> na mesma pasta local por conveniência do desenvolvedor. Um `tests/` no topo (fora de ambos os
-> `.git`) não pertence a nenhum dos dois repos publicados: quem clona qualquer um isoladamente não
-> recebe pasta de teste nenhuma. D1 foi revertida pra **cópia física** (a alternativa já prevista
-> nesta seção): `ConsolePlus/tests/` e `PromptPlus/tests/` agora são as raízes reais, cada uma com sua
-> própria cópia de `_driver-src/` e seu próprio projeto de testes — a estrutura ilustrada acima existe,
-> mas repetida uma vez dentro de cada repo, não uma vez no topo. Ver
-> `docs/testing-driver-maintenance.md` (idêntico nos dois repos) para o procedimento de manter as duas
-> cópias em sincronia, e a decisão revista no início da seção 12 (D1).
-
----
-
-## 5. Componentes do driver (VirtualTerminal)
-
-- **`VirtualScreen`** — `Cell[Height,Width]` com `Cell(Rune Glyph, Style Style)`, cursor (`Left/Top`), `Current` style;
- `Put` (respeita wrap na largura), `EraseToEol`, `EraseDisplay`, `Clear`; leitura `StyleAt/CharAt/TextAt/Snapshot`.
-- **`AnsiScreenInterpreter : TextWriter`** — recebe o stream ANSI do writer real e interpreta **apenas o subconjunto
- que o ConsolePlus emite** (ver `IAnsiCommands`): `CUP` (posição), `EL` (erase-in-line), `ED` (erase-display),
- `SGR ...m` (cor 4/8/24-bit → `Style`), show/hide cursor, enter/exit alt-screen. **Falhar explicitamente** em
- sequência desconhecida (evita falso-verde).
-- **`VirtualTerminal : IConsole, IConsolePlus`** — `SupportsAnsi => true` (força caminho ANSI determinístico);
- `Width/Height` fixos por opção; `GetCursorPosition` lê do grid (read-back fiel); `SetCursorPosition` via
- `AnsiCommands` real; `Out => interpreter`; hospeda `ConsoleWriter(this)` de produção; delega markup/estilo/overflow.
-- **`InputQueue`** — `Enqueue(ConsoleKey/ConsoleKeyInfo/string)`; alimenta `KeyAvailable`/`ReadKey`/`ReadKeyAsync`.
-- **`ScreenAssertions`** — verbos: `CharAt`, `TextAt`, `StyleAt`, `RegionSnapshot`, integração snapshot (Verify).
-- **`VirtualTerminalOptions`** — `Width`, `Height`, `ColorDepth`, `SupportsUnicode`, `Interactive`.
-
-**Esqueletos de código completos no Apêndice A** (este documento é autocontido: dá para implementar sem o histórico da conversa).
-
----
-
-## 6. InternalsVisibleTo (mudança de produção mínima)
-
-Nenhuma mudança de comportamento; só atributos:
-
-- **ConsolePlus** concede IVT a: `ConsolePlus.Tests` **e** `PromptPlus.Tests`
- (ambas embedam o driver, que toca `IConsolePlus`, `ConsoleWriter`, `Fragment`, `ProfileConsole`).
-- **PromptPlus** concede IVT a: `PromptPlus.Tests`
- (para `PromptPlusControls`, `PromptConfig`, `BaseControlPrompt` e controles internos).
-
----
-
-## 7. Ferramentas
-
-- **Framework de teste:** xUnit (padrão .NET). Assertions: FluentAssertions.
-- **Snapshot:** Verify (golden files do grid completo de um frame — ótimo para "layout inteiro do controle").
-- **Build:** referenciar `src/*.csproj` diretamente (o `.slnx` está quebrado — ver memória de build).
-- **TargetFrameworks:** espelhar os do `src` (net8.0; net9.0; net10.0).
-
----
-
-## 8. CI (Windows + Linux)
-
-GitHub Actions, matriz `os: [windows-latest, ubuntu-latest]`:
-```
-dotnet test tests/ConsolePlus.Tests/ConsolePlus.Tests.csproj
-dotnet test tests/PromptPlus.Tests/PromptPlus.Tests.csproj
-```
-Camadas 1–3 são headless → resultado idêntico nos dois SOs. Camada 4 (PTY), se existir, roda em job separado
-não-bloqueante (tolerante a flakiness/divergência de SO).
-
-> **Atualização 2026-07-23.** Caminhos reais pós-migração (seção 4):
-> `ConsolePlus/tests/ConsolePlus.Tests/ConsolePlus.Tests.csproj` e
-> `PromptPlus/tests/PromptPlus.Tests/PromptPlus.Tests.csproj` (cada um dentro do próprio repo). O
-> `.slnx` de cada repo passou a incluir o projeto de testes e hoje builda sem erro (a nota "`.slnx`
-> está quebrado" da seção 7 não reflete mais o estado atual — carrega e builda normalmente). Ainda
-> assim, `ci.yml`/`publish-nuget.yml` **não** dependem do `.slnx`/auto-discovery: cada um chama
-> `dotnet build src/*.csproj --configuration Release` (o que é empacotado) e
-> `dotnet test tests/*.Tests/*.Tests.csproj --configuration Debug` (onde o driver tem acesso aos
-> internos via `ProjectReference`, decisão D6) como dois passos explícitos e independentes — nunca
-> `dotnet build`/`dotnet test` sem projeto/config explícitos. Motivo: com os testes agora dentro do
-> repo, um `dotnet build --configuration Release` "cego" (que descobre o `.slnx` inteiro) tentava
-> compilar também o projeto de testes em Release — e isso quebrava no PromptPlus, porque em Release
-> o `PromptPlus/src` resolve o ConsolePlus via `PackageReference` (pacote publicado, sem
-> `InternalsVisibleTo` pro driver), não via `ProjectReference` (D6). Validado localmente rodando os
-> comandos exatos do CI nos dois repos antes de confiar neles.
-
----
-
-## 9. O que testar — faixas de ROI
-
-**ROI alto (fazer):**
-- ConsolePlus puro: markup, cor→SGR (3 profundidades), emoji, overflow Crop/Ellipsis, largura CJK/emoji.
-- Render inicial de cada controle: prompt, colunas, estilo do item selecionado, posição final do cursor.
-- Máquinas de estado: navegação, edição/máscara, validação, filtros do Select, paginação.
-- Posicionamento sensível: viewport, ellipsis lateral (`WriteAnswerViewport`, `ViewportSlice`).
-
-**ROI médio (parcimônia):**
-- Resize/relayout do `BaseControlPrompt` (parte mais complexa/bugável): cobrir ~4–5 cenários canônicos
- (shrink/grow largura, shrink altura c/ scroll), não a matriz inteira.
-
-**ROI baixo (NÃO automatizar; manual/visual):**
-- Fidelidade em terminal real (WT vs VT100 vs conhost legacy; wrapping/scrollback/emoji largo real).
-- Timing de animações (testar estado, não o "quando").
-- Detecção de capacidades (`EnvironmentUtil`/`AnsiDetector`) — testar as funções puras com env-vars mockadas,
- nunca o singleton.
-
----
-
-## 10. API de asserção (posição + estilo)
-
-```csharp
-vt.CursorLeft.Should().Be(12);
-vt.TextAt(row: 2, col: 4, len: 5).Should().Be("Nome:");
-vt.StyleAt(row: 2, col: 4).Foreground.Should().Be(Color.Cyan1);
-vt.StyleAt(row: 3, col: 0).Should().Be(cfg.SelectedStyle);
-vt.Snapshot().Should().MatchVerified();
-```
-
----
-
-## 11. Fases de execução (pilot-then-rollout)
-
-- **Fase 0 — Fundação** ✅ **CONCLUÍDA (2026-07-22)**
- - Criar `tests/ConsolePlus.Tests` e `tests/PromptPlus.Tests` (multi-target, ProjectReference a `src/*.csproj`).
- - Adicionar os `InternalsVisibleTo` (seção 6) — `ConsolePlus/src/Properties/AssemblyInfo.cs`, `PromptPlus/src/Properties/AssemblyInfo.cs`.
- - Corrigir D6 (`PromptPlus/src/PromptPlus.csproj`: `ProjectReference` condicional em Debug para o ConsolePlus local).
- - Implementar o driver (fonte única em `_driver-src`, linkado) — `VirtualScreen`/`Cell` + `AnsiScreenInterpreter` + `VirtualTerminal`(+`.Overloads`) + `InputQueue` + `ScreenAssertions`.
- - **DoD:** um "hello" via `ConsolePlus`/`VirtualTerminal` grava glifos com estilo correto no grid; build/teste verde em net10.0/net9.0/net8.0 no Windows (`dotnet test` nos dois `*.Tests.csproj`). Linux ainda não validado localmente — cobre-se via CI (seção 8/A.10), pendente de execução.
- - **Correções encontradas na implementação (não previstas no Apêndice A original):** `ResetColor()`/`Clear()` precisam setar `ForegroundRgbColor`/`BackgroundRgbColor` (que disparam `ConsoleWriter.ApplyStyle`) em vez de mutar campos privados direto, senão `VirtualScreen.Current` fica dessincronizado do estado real; `IConsolePlus.Writer` exige implementação explícita de interface (`ConsoleWriter IConsolePlus.Writer => _writer;`) porque `ConsoleWriter` é internal e um membro implícito (público) não pode expor um tipo menos acessível (CS0053); `IConsole.Write(string?, Style)`/`WriteLine(string?, Style)` são overloads de 2 argumentos distintas da versão de 3 argumentos com default — precisam de implementação própria, um parâmetro default não as satisfaz.
-
-- **Fase 1 — Piloto** ✅ **CONCLUÍDA (2026-07-22)**
- - ConsolePlus: unidade pura de markup + cor→SGR + overflow (camada 1) — `tests/ConsolePlus.Tests/Unit/*`,
- `Rendering/OverflowTests.cs`. 20 testes.
- - PromptPlus: `Input` e `Select` (render inicial + navegação + estilo do selecionado + cursor) —
- `tests/PromptPlus.Tests/Controls/{SelectControlTests,InputControlTests}.cs`. 8 testes + 1 snapshot.
- - **DoD:** ✅ 30 testes verdes (meta era ~15–25) em net10/9/8 no Windows (Linux via CI, seção 8, ainda
- não executado); API de asserção validada (`TextAt`/`StyleAt`/`GetCursorPosition`/`Find`); 1 snapshot
- Verify estabelecido (`SelectControlSnapshotTests.cs` + `.verified.txt`, idêntico nos 3 TFMs).
- - **3 achados de arquitetura documentados na seção 13** (hang em `WaitKeypress` sem tecla terminal;
- `InputQueue.Enqueue(Enter)` não confirmava fora do Windows, corrigido; `VirtualTerminal` com
- `Width<80`/`Height<10` trava `Run()` para sempre no aviso de terminal pequeno).
-
-- **Extra (pedido do usuário, 2026-07-22): cobertura de `EmacsConsoleBuffer`** — `ConsolePlus/src/Shared/EmacsConsoleBuffer.cs`,
- classe **pública** que sustenta o buffer de edição de resposta (Emacs-style) usado por `Input` e pelo
- viewport de resposta de outros controles. Unidade pura (camada 1, sem `VirtualTerminal` — o método
- central `TryAcceptedReadlineConsoleKey` recebe `ConsoleKeyInfo` direto). 39 testes em
- `tests/ConsolePlus.Tests/Unit/EmacsConsoleBufferTests.cs` cobrindo **todas** as key bindings
- implementadas: Ctrl+A/B/C/D/E/F/H/K/L/T/U/W, Alt+D/F/L/U, teclas físicas (Home/End/setas/Delete/
- Backspace/Insert/Tab), Enter (sempre rejeitado), `enableEmacsKeys=false` (desliga atalhos mas mantém
- teclas físicas), `readonly`, `maxlength` (achado: retorna `true`/aceito mesmo quando o limite bloqueia
- a inserção), `CaseOptions.Uppercase/Lowercase`, função `validate`, `LoadPrintable`, `ToForward`/`ToBackward`.
- Valores exatos de operações não-triviais (`Ctrl+W`/`Ctrl+T`/`Alt+U`/`Alt+L`/`Alt+D`/`Alt+F`/`Alt+B`/`Ctrl+C`)
- foram confirmados rodando uma sonda descartável e observando o resultado real, não por simulação manual —
- `BackwardWord` (Alt+B) tem uma peculiaridade real: só para depois de cruzar **dois** limites de palavra,
- então de "hello world foo" (fim) um único Alt+B pula "foo" inteiro e para no início de "world", não em "foo".
-
-- **Extra (pedido do usuário, 2026-07-22): cobertura de classes fundacionais pequenas do ConsolePlus**
- ("base para as classes mais complexas", buscando bugs escondidos). 6 classes cobertas, 145 testes novos,
- **1 bug real encontrado e corrigido**:
- - `ConsoleKeyInfoExtensions.cs` (53 testes) — todos os `IsPressXKey`/`IsPressXKeyOrEmacs`. Sem bug novo,
- mas o teste de `IsPressEnterKey` cobre a divergência Windows/não-Windows já conhecida via branching em
- runtime (`OperatingSystem.IsWindows()`) em vez de um pacote de skip — o CI Windows+Linux (seção 8)
- exercita os dois ramos naturalmente, cada um no seu runner.
- - `DashUtil.cs` (27 testes) — `GetBorderUp`/`GetBorderDown` para as 10 combinações de `DashOptions` × unicode/ascii. Sem bug.
- - `StyleExtensions.cs` (6 testes) — `Colors`/`ForeGround`/`Background`/`Overflow`. Sem bug.
- - `ColorExtensions.cs` + `Color.cs` (22 testes) — `Weighted`, `Blend`, `GetLuminance`, `GetContrast`,
- `ToHex`/`FromHex`/`TryFromHex`, `FromInt32`, `FromConsoleColor`↔`ToConsoleColor`.
- **🐛 Bug real corrigido**: `Color.GetInvertedColor()` (`Shared/Color.cs:78-81`) comparava
- `GetLuminance() < 140`, mas `GetLuminance()` (fórmula WCAG) sempre retorna um valor em **[0,1]** — a
- condição era sempre verdadeira, então o método **sempre retornava `White`**, para qualquer cor
- (confirmado: `White.GetInvertedColor()` também dava `White`, quando deveria dar `Black`). O método
- irmão `GetContrastForegroundColor` já usava o threshold correto (`0.5`) na mesma escala. Corrigido para
- `GetLuminance() < 0.5`; só usado em samples/docs, nenhum controle de produção dependia do valor errado.
- - `ColorPalette.cs` + `ColorTableCss.cs` (20 testes) — `ExactOrClosest` (match exato + "closest" via
- fórmula redmean) nas 3 profundidades; tabela CSS (`TryGetColor`/`TryGetName`/`TryGetWeightedColor`,
- incluindo nomes com peso tipo `"red500"`). Sem bug.
- - `MarkupColorTokenizer.cs` (17 testes) — parser char-a-char por trás de `Fragment.FromText`: tags
- abertas/fechadas, `[[`/`]]` escapados, tags malformadas (`[/x]`, `[tag` sem fechar, `[tag[next]`),
- posição de token. Sem bug — o parser é mais fault-tolerant do que aparenta (degrada pra texto puro
- consistentemente em vez de lançar).
- - Total após este lote: **204 testes** em `ConsolePlus.Tests` (+ 10 em `PromptPlus.Tests`) verdes em
- net10.0/net9.0/net8.0 no Windows.
-
-- **2º lote de classes fundacionais (pedido do usuário, 2026-07-22, "continua com mais"): 57 testes novos,
- 2º bug real encontrado e corrigido**:
- - `Markup.cs` (11 testes) — `Escape`/`Remove`/`Length` sobre o `MarkupColorTokenizer` já testado;
- round-trip `Remove(Escape(x)) == x` confirmado. Sem bug.
- - `ColorJsonConverter.cs` (6 testes) + `CultureInfoJsonConverter.cs` (7 testes) + `CultureExtensions.cs`
- (2 testes) — descobertos ao explorar vizinhos do `Markup.cs`.
- **🐛 2º bug real corrigido**: `ColorJsonConverter.Read` (`Core/ColorJsonConverter.cs:34-41`) só
- capturava `ArgumentException`, mas hex com dígitos inválidos (ex.: `"#GGGGGG"`) faz `Color.FromHex`
- lançar `FormatException` (via `byte.Parse`) — não capturado, vazava cru pro consumidor em vez do
- `JsonException` com mensagem legível que o método promete. Corrigido para
- `catch (Exception ex) when (ex is ArgumentException or FormatException)`.
- - `StringExtensions.NormalizeNewLines`/`SplitLines` (9 testes) — comportamento cross-platform via
- `Environment.NewLine` (não hardcoded `"\r\n"`, pra funcionar em Windows e Linux). Achado não-óbvio
- documentado num teste: `\r` solto (estilo Mac antigo) é apenas removido, não convertido — a quebra de
- linha se perde. Sem bug (comportamento documentado no comentário do código).
- - `AnsiCommands.cs` (22 testes) — formatação literal de CSI/SGR pra **todos** os comandos (não só o
- subconjunto que `AnsiScreenInterpreter` reconhece), usando um `IConsolePlus` fake mínimo (só
- `Out`/`Error`/`WriteToErrorOutput` reais, resto `NotImplementedException`) em vez de `VirtualTerminal`,
- já que comandos como setas/tabulação/scroll/save-restore-cursor não são interpretados pelo driver e
- lançariam `NotSupportedException`. Sem bug de produção — 1 erro no próprio teste (`"\x1bD"` em C# é
- interpretado como um único char Unicode U+01BD, já que `\x` consome até 4 dígitos hex e `D` é hex
- válido; corrigido para `"\x1b" + "D"`).
- - Total após os dois lotes extra: **261 testes** em `ConsolePlus.Tests` (+ 10 em `PromptPlus.Tests`)
- verdes em net10.0/net9.0/net8.0 no Windows.
-
-- **3º lote de classes fundacionais (pedido do usuário, 2026-07-22): 16 testes novos, sem bug**:
- - `Helper.cs` (15 testes) — `MainToken`/`ExitCode`/`LastException` são estado global mutável
- compartilhado por todo o assembly ConsolePlus. Cada teste salva o valor original e restaura em
- `finally`, para não vazar estado pra outra classe de teste rodando em paralelo (xUnit paraleliza
- entre classes por padrão).
- - `TextWriterExtensions.cs` (4 testes) — `IsStandardOut`/`IsStandardError` são comparação de
- referência contra `System.Console.Out`/`Error`; só *leem* o singleton real (nunca redirecionam),
- então não violam a regra do plano de "evitar o singleton" — essa é uma leitura inofensiva.
- - `Emoji.cs` — resolução de shortcode qualificado por grupo (`"activities/balloon"`,
- `Shared/Emoji.cs:51-78,104-157`), aprofundando `EmojiTests.cs` (que só cobria nomes simples):
- grupo válido resolve, case-insensitive, grupo desconhecido rejeita (não cai pra busca de texto
- livre), nome desconhecido dentro de grupo válido retorna vazio, alias de grupo sem "And"
- (`"peoplebody/thumbs_up"` via `PeopleAndBody`) também resolve. 8 testes, sem bug.
- - Total após os três lotes extra: **277 testes** em `ConsolePlus.Tests` (+ 10 em `PromptPlus.Tests`)
- verdes em net10.0/net9.0/net8.0 no Windows.
-
-- **Classes fundacionais/de apoio do PromptPlus (pedido do usuário, 2026-07-22, "mesmos critérios"):
- 45 testes novos, 3º e 4º bugs reais encontrados e corrigidos**:
- - `Paginator` (`Controls/Common/Paginator.cs`, 20 testes) — motor de paginação reusado por
- ~10 controles (Select, MultiSelect, Tree, MultiTree, MultiTasks, MultiFile, ChartBar, Table,
- MultiTable, FileExec, Calendar). **🐛 3º bug real corrigido**: `UpdateCollection()` não resetava
- `SelectedPage` antes de chamar `FirstItem()` — recarregar uma coleção **mais curta** enquanto
- parado numa página posterior deixava o paginador sem seleção (`SelectedIndex=-1`) mesmo havendo
- itens válidos na nova coleção. O método irmão `UpdateFilter()` já resetava `SelectedPage=0`
- corretamente; `UpdateCollection()` só esqueceu. **Afeta produção de verdade**: ~9 chamadas em
- `MultiSelectControl` e 3 em `MultiTableControl` (features "mostrar só selecionados"/refresh).
- Corrigido adicionando `SelectedIndex=-1; SelectedPage=0;` no início do método, espelhando
- `UpdateFilter`. Também documentado (sem ser bug): `LastItem()`/`FirstItem()` chamados diretamente
- são relativos à página ATUAL, não à coleção inteira — só funcionam com semântica de
- "primeiro/último absoluto" quando chamados via `End()`/`Home()`, que navegam pra página certa
- primeiro; e `NextItem()`/`PreviousItem()` não dão wrap-around nos extremos absolutos da coleção
- (só `NextPage`/`PreviousPage` dão wrap, via `%PageCount`).
- - `Optional` (`Controls/Common/Optional.cs`, 13 testes). **🐛 4º bug real corrigido (latente)**:
- `operator ==(T left, Optional right)` comparava `left` contra `right.Value` sem checar
- `right.HasValue` — `0 == Optional.Empty()` retornava `True` (deveria ser `False`). Confirmado
- via investigação que nenhum código de produção usa esse operador hoje (só `.HasValue`/`.Value`
- diretamente); risco latente, não bug visível ainda. Corrigido para checar `HasValue` primeiro.
- - `HotKey` (`Shared/Common/HotKey.cs`, 12 testes). **🐛 5º bug real corrigido (latente)**:
- `KeyInfo` fazia `(char)Key` sem qualquer filtro — funcionava certo só por coincidência pra
- A-Z/D0-D9/Escape/Tab/Backspace/Enter (valores do enum `ConsoleKey` batem com códigos ASCII reais
- nesses casos), mas produzia `KeyChar` sem sentido pra qualquer outra tecla (F1→`'p'`, setas,
- Home/End/PageUp/Down, teclas Oem, etc.). Uma correção inicial por faixa numérica (`>32 and <127`)
- não bastou — muitas teclas especiais caem dentro dessa faixa por coincidência (setas 37-40,
- Home/End 35-36, F1-F14 112-125 se sobrepõem à banda imprimível). Corrigido com allowlist explícita
- (Backspace/Tab/Enter/Escape/Spacebar/D0-D9/A-Z), retornando `'\0'` pra tudo fora dela. `KeyInfo`
- não é usado em nenhum lugar da produção hoje (`grep` confirmou zero chamadas) — risco latente.
- - Total: **332 testes** (277 `ConsolePlus.Tests` + 55 `PromptPlus.Tests`) verdes em
- net10.0/net9.0/net8.0 no Windows. **5 bugs reais de produção** encontrados e corrigidos até agora
- nesta frente de testes fundacionais (2 no ConsolePlus, 3 no PromptPlus).
- - `BufferScreen`/`BufferState`/`LineScreen` (`Controls/Common/{BufferScreen,BufferState,LineScreen}.cs`,
- 19 testes) — motor de diff de renderização usado por **todo** controle via
- `BaseControlPrompt.RenderBuffer` (`UpdateBufferDiff`, `PhysicalLineCount` pro cálculo de
- reflow no resize). Sem bug — matemática de `PhysicalLineCount` (clipping pela largura de render
- anterior + wrap na largura nova) e as regras de diff (`UpdateBufferDiff` só reporta linhas
- mudadas ou genuinamente novas, nunca remoções — isso é responsabilidade separada do chamador)
- conferem exatamente com o esperado, confirmado por sonda antes de travar as asserções.
- - Total final: **351 testes** (277 `ConsolePlus.Tests` + 74 `PromptPlus.Tests`) verdes em
- net10.0/net9.0/net8.0 no Windows.
-
-- **3º lote de classes de apoio do PromptPlus (pedido do usuário, 2026-07-22, "siga a ordem até o
- final, pulando AnsiDetector/UnicodeDetector"): 79 testes novos, 6º bug real (duplicado) + 1
- fragilidade latente corrigida + 1 mudança de arquitetura (nova dependência)**:
- - `PromptPlus/src/Core/ColorJsonConverter.cs` (5 testes). **🐛 6º bug real corrigido**: cópia exata
- do `ColorJsonConverter` do ConsolePlus, com o **mesmo bug** (`catch(ArgumentException)` não pega
- `FormatException` de `Color.FromHex`) — mas é uma classe compilada num assembly diferente, então
- a correção do ConsolePlus não valia pra essa. `CultureInfoJsonConverter.cs` (PromptPlus) também é
- cópia exata, mas já estava correta (sem teste extra, coberto pela suíte do ConsolePlus).
- - `MaskEditBuffer`/`MaskElement` (`Controls/MaskEdit/*.cs`, 19 testes) — máquina de estado de
- edição de máscara (string/número/moeda/data-hora), base de toda a família `MaskEdit*Control`.
- Fixtures construídas via reflection chamando os métodos `private static`
- `NormalizeStringMask`/`NormalizeNumberMask` reais de `MaskEditControl` (IVT não relaxa
- `private`), garantindo que o layout dos elementos bate exatamente com a produção. Comportamento
- de entrada numérica "shift-left" (dígito novo empurra os anteriores, backspace remove o
- mais-recente-digitado) e navegação (`Tab`/setas/Home/End/Emacs) confirmados por sonda antes de
- travar as asserções. **Fragilidade latente corrigida**: o construtor indexava
- `_charElements[_decimalposition]` sem checar limites — uma máscara numérica sem separador
- decimal deixava `CursorPosition=-1` e lançava `KeyNotFoundException` na primeira tecla.
- Inalcançável hoje (`MaskEditControl.SetNumberFormat` sempre inclui um `.`, mesmo pra inteiros
- com 0 casas decimais), mas adicionada guarda no construtor (`ArgumentException` imediato) para
- não depender desse invariante silenciosamente pra sempre.
- - `ComponentStyles.cs` — acabou sendo só um **enum** (nomes de região de estilo), sem lógica; nada
- a testar (suposição inicial errada de que seria uma classe de dicionário).
- - `HotKeyJsonConverter.cs` (10 testes) — JSON custom (objeto `{key,alt,ctrl,shift}`), único do
- PromptPlus. Sem bug — validação de token/propriedade já bem escrita.
- - `SpinnerBase.cs` (6 testes, via double de teste já que é abstrata) + catálogo `SpinnerBase.Known`
- (1 teste reflexivo cobrindo todas as entradas) — ciclo de frames circular, base de spinners de
- Task/Progress. Sem bug.
- - `FileHistory.cs` (15 testes) — **mudança de arquitetura, pedida explicitamente pelo usuário**:
- a classe escrevia direto em `%USERPROFILE%\PromptPlus.History\` sem nenhum seam de injeção.
- Adicionado `System.IO.Abstractions` (produção) + `System.IO.Abstractions.TestingHelpers` (testes)
- — `FileHistory.FileSystem` agora é um `IFileSystem` estático substituível (`internal`, default
- `new FileSystem()` real), testes usam `MockFileSystem` (puramente em memória, idêntico em
- Windows/Linux) salvo/restaurado via `IDisposable` por teste, mesmo padrão de
- `HelperTests`. `AddHistory` (puro, sem I/O) testado direto. Sem bug — dedup/expiração/ordenação/
- corte por `maxitem` todos corretos.
- - Total após este lote: **407 testes** (277 `ConsolePlus.Tests` + 130 `PromptPlus.Tests`) verdes em
- net10.0/net9.0/net8.0 no Windows. **6 bugs reais de produção** corrigidos até agora (3 no
- ConsolePlus, 3 diretos + 1 duplicado no PromptPlus — GetInvertedColor, ColorJsonConverter(x2),
- Paginator.UpdateCollection, Optional, HotKey.KeyInfo) + 1 fragilidade latente adicional
- (MaskEditBuffer). (Correção de aritmética: o total/contagem de `PromptPlus.Tests` reportado
- antes, 153, estava errado — o valor correto, confirmado por execução, é 130.)
-
-- **4º lote — classes finais do ConsolePlus (pedido do usuário, 2026-07-22, mesma ordem "siga até o
- final, pulando AnsiDetector/UnicodeDetector"): 36 testes novos, 7º bug real corrigido**:
- - `Figlet/FigletFont.cs` (10 testes, `Unit/FigletFontTests.cs`) — camada 1, unidade pura. Parser do
- formato `.flf` (FIGlet font). Fixture mínima construída em memória (`MemoryStream`), sem tocar o
- recurso embutido real nem o disco: header, `CommentLines`, substituição de hardblank,
- concatenação de glyphs por caractere, `ToAsciiArtMarkup` (fragmentos de conteúdo + quebra de
- linha), assinatura inválida (`InvalidDataException`), caminho nulo/vazio
- (`ArgumentNullException`), arquivo inexistente (`FileNotFoundException`, caminho cross-platform
- via `Path.GetTempPath()`), construtor sem parâmetros carregando a fonte `Standard` embutida. Sem
- bug.
- - `Figlet/BannerWidget.cs` (4 testes, `Rendering/BannerWidgetTests.cs`) — camada 2 (usa
- `IConsolePlus`/`VirtualTerminal` via `Show()`). `Show()` escreve o glyph renderizado na grade;
- `Border(DashOptions)` adiciona linha superior/inferior com a largura da linha mais larga
- renderizada; sem borda por padrão só escreve a linha do glyph; texto vazio não escreve nada.
- Reutiliza a mesma fonte `.flf` mínima de `FigletFontTests`. **Erro de teste autocorrigido antes
- de rodar**: a primeira versão comparava `vt.TextAt(...)` com `string.Empty`, mas células não
- tocadas da grade renderizam como espaço (`' '`), nunca como string vazia — corrigido para
- `.Trim().Should().BeEmpty()`, mesmo padrão de lição já registrada pra `VirtualScreen.Snapshot()`.
- Sem bug.
- - `ConsoleAbstractions/ProfileExtensions.cs` (22 testes, `Unit/ProfileExtensionsTests.cs`) —
- `EnrichersCI()` despacha pra 14 detectores de CI (`RuntimeEnvironment/*.cs`: AppVeyor,
- AzurePipelines, Bamboo, Bitbucket, Bitrise, Continua, GitHub, GitLab, GoCD, Jenkins, MyGet,
- TeamCity, Tfs, Travis), o primeiro que der match nas env vars conhecidas "ganha" e para o loop.
- **Bloqueio de testabilidade confirmado por sonda empírica**: `BaseClassCI` cacheia
- `Environment.GetEnvironmentVariables()` num campo `private static` compartilhado por TODAS as
- subclasses, populado uma vez e nunca invalidado — a sonda provou que, depois do primeiro match
- (ex. GitHub), trocar a env var real e chamar outro detector (GitLab) ainda retornava o resultado
- do primeiro (GitHub), por causa do cache obsoleto. Resolvido via reflection nos testes
- (`BaseClassCI._environmentVariables` zerado antes de cada cenário — mesmo padrão já usado pra
- métodos `private static`), sem alterar código de produção. Cada teste também limpa as 14 env vars
- conhecidas antes/depois (`IDisposable`) pra não depender do ambiente real onde a suíte rodar.
- **🐛 7º bug real corrigido**: `Continua.cs` era o único dos 14 detectores que não setava
- `profile.ChangedColorDepth = true` ao forçar `ColorDepth = FourBit`. Como
- `EnvironmentUtil.cs:315` faz `if (!profile.ChangedColorDepth) { profile.ColorDepth = colordetect; }`
- logo depois de `EnrichersCI()`, o `FourBit` forçado pelo Continua era silenciosamente sobrescrito
- pela auto-detecção — a detecção de CI "funcionava" (retornava `true`, `Interactive=false`) mas o
- `ColorDepth` correto nunca chegava a valer. Corrigido para setar `ChangedColorDepth = true` como
- os outros 13, com teste de regressão dedicado.
- - Total após este lote: **443 testes** (313 `ConsolePlus.Tests` + 130 `PromptPlus.Tests`) verdes em
- net10.0/net9.0/net8.0 no Windows. **7 bugs reais de produção** corrigidos até agora (3 no
- ConsolePlus — GetInvertedColor, ColorJsonConverter, Continua.ChangedColorDepth; 4 no PromptPlus —
- ColorJsonConverter (duplicado do bug do ConsolePlus), Paginator.UpdateCollection, Optional,
- HotKey.KeyInfo) + 1 fragilidade latente adicional (MaskEditBuffer). Lista aprovada pelo usuário
- ("siga a ordem até o final, pulando AnsiDetector/UnicodeDetector") esgotada.
-
-- **Fase 2 — Rollout**
- - Cobrir os demais controles/widgets em ondas, reusando o padrão do piloto (catalog-then-fix).
- - **DoD:** todos os controles com pelo menos render inicial + fluxo principal.
-
-- **Fase 3 — Resize (driver estendido em 2026-07-24)** — continuação da Fase 2, cujo tracker
- detalhado (`FASE2-CONTROLS-PLAN.md`) está congelado em 2026-07-23; os achados abaixo (pós-Fase 2)
- não estão lá, só aqui.
- - `VirtualTerminal.RaiseResize` antes só disparava o evento `SizeChanged`, sem redimensionar o
- `VirtualScreen` de fato (`Width`/`Height` eram `{ get; }`, sem setter) — o subsistema de resize
- da `BaseControlPrompt` era literalmente intestável. Estendido: `VirtualScreen.Resize(w,h)` real
- (realoca a grade preservando a região de interseção nas mesmas coordenadas — sem reflow de
- texto, igual a um terminal real — e blanks nas células recém-expostas; cursor é clampado se
- caiu fora); `RaiseResize` chama esse resize ANTES de disparar o evento.
- - Primeiro teste real de resize (`PromptPlus/tests/PromptPlus.Tests/Controls/
- ResizeRelayoutTests.cs`) achou um **bug real de produção só visível com resize de verdade**:
- `InputControl.TryResult` setava `_updatePosAnswerBuffer = true` antes de ler a próxima tecla e
- só resetava pra `false` no caminho de tecla normal — um `break` por `press.IsResize` saía do
- loop com a flag ainda `true`, e a passagem de render seguinte (o relayout de resize) recarregava
- `_inputdata` a partir de `_lastinput` + `ToHome()`, resetando o cursor de edição pro início do
- texto mesmo sem o usuário ter feito nada. Corrigido movendo o reset pra antes do `if
- (press.IsResize || press.IsCancelled)`.
- - **Achado não perseguido nesta sessão**: o mesmo padrão (`press.IsResize || press.IsCancelled`
- seguido de `break`) existe em outros 18 controles — candidato natural pra uma auditoria futura
- agora que resize é testável, mas fora do escopo desta rodada.
-
-- **Fase 4 — PTY E2E (opcional/baixo ROI)**
- - Poucos smokes rodando samples sob ConPTY/pty.
-
----
-
-## 12. Decisões (fechadas em 2026-07-22; D1 revista em 2026-07-23 — ver nota)
-
-- **D1 (mecanismo do driver em B):** ✅ **linked-source único** (`tests/_driver-src/*.cs`, `` em cada `*.Tests.csproj`). Confirmado: nenhum projeto de teste ou padrão de link-source existe hoje no repo (terreno limpo, sem conflito).
- → **REVISTA em 2026-07-23**: linked-source presumia um único repo; como ConsolePlus/PromptPlus são
- dois repos GitHub distintos, D1 foi trocada pra **cópia física** (`ConsolePlus/tests/_driver-src` e
- `PromptPlus/tests/_driver-src`, cópias independentes, cada uma versionada no próprio repo). Ver
- nota na seção 4 e `docs/testing-driver-maintenance.md`. Alternativas rejeitadas na hora (pacote
- NuGet próprio, drivers independentes do zero) documentadas no mesmo arquivo.
-- **D2:** ✅ **xUnit + Verify** (não TUnit) — mantém os esqueletos do Apêndice A como estão.
-- **D3:** ✅ Confirmado por leitura direta: `ConsolePlus/src/ConsolePlus.csproj:4` e `PromptPlus/src/PromptPlus.csproj:3` usam ambos `net10.0;net9.0;net8.0`. Espelhar exatamente essa lista/ordem nos `*.Tests.csproj`.
-- **D4:** ✅ **Só cor (fg/bg), sem atributos.** Verificado em código: `Style` (`ConsolePlus/src/Shared/Style.cs:23-38`) só tem `Foreground`/`Background`/`OverflowStrategy`; `ConsoleWriter.ApplyStyle` (`ConsoleAbstractions/ConsoleWriter.cs:257-268`) só emite SGR de `AnsiColorBuilder` (cor). Produção não emite bold/underline/etc hoje — nada a modelar no grid. Os comentários `// TODO Fase 2: 4-bit / 256` e a nota de D4 no Apêndice A.3/A.5 ficam confirmados como "não aplicável", não como pendência.
-- **D5:** ✅ Piloto = **`Input` + `Select`**, confirmado, **com correção de API** em relação ao Apêndice A.9 (ver nota abaixo): `PromptPlusControls.Input(...)`/`.Select(...)` retornam interfaces **públicas** (`IInputControl`, `ISelectControl` — `PromptPlus/src/Core/PromptControls.cs:167-188`), não é preciso `dynamic`/cast para classe concreta interna. Execução é `Run(CancellationToken token = default)` (não `Run()` sem argumento) — `IInputControl.cs:173`, `ISelectControl.cs:37` — retornando `ResultPrompt` (`Shared/Common/ResultPrompt.cs:16-38`) cujas propriedades são **`Content`** (não `Value`) e **`IsAborted`**.
-- **D6 (nova, descoberta na verificação — divergência de arquitetura):** `PromptPlus/src/PromptPlus.csproj:69` traz o ConsolePlus via `PackageReference` (NuGet publicado), não `ProjectReference`. Isso quebraria o `InternalsVisibleTo` planejado (seção 6) para `PromptPlus.Tests`, pois o assembly resolvido seria o pacote publicado, não o build local de `ConsolePlus/src`.
- → **Decisão:** `ProjectReference` condicional por `Configuration` em `PromptPlus/src/PromptPlus.csproj` — `PackageReference` em Release (empacotamento normal), `ProjectReference` para `../../ConsolePlus/src/ConsolePlus.csproj` em Debug (testes/dev). Este `.csproj` **já usa esse padrão** (`ItemGroup Condition="'$(Configuration)' == 'Release' ..."` na linha 62 para `DefaultDocumentation`), então a mudança segue um padrão já existente no arquivo, não introduz um novo. Ação concreta na Fase 0 (não aplicada ainda):
- ```xml
-
-
-
-
-
-
- ```
- `dotnet test` roda em `Debug` por padrão, então `PromptPlus.Tests` passa a puxar o ConsolePlus local automaticamente sem precisar de `-c` explícito.
-
----
-
-## 13. Riscos e mitigações
-
-- **"Testar o espelho"** (driver e lib compartilharem lógica de largura): o interpretador ANSI é independente do writer;
- largura/overflow são cobertos na camada 1 (unidade pura) contra valores fixos, não contra o próprio driver.
-- **Escopo do interpretador:** implementar só o subconjunto emitido; falhar em sequência desconhecida.
-- **Divergência do driver duplicado (se D1 = cópia física):** mitigar com linked-source.
-- **InternalsVisibleTo:** acopla testes a internos — aceitável em libs deste tipo.
-- **Trava (hang) em testes de `Input`/`Select` (achado na Fase 1, 2026-07-22):** `WaitKeypress`
- (`PromptPlus/src/Controls/Common/BaseControlPrompt.cs:745-758`) faz spin-wait em `console.KeyAvailable`
- até haver tecla ou `token` cancelar — **não lança excessão nem retorna se a fila esvaziar antes de uma
- tecla terminal**. Regra obrigatória para todo teste de controle: (1) sempre enfileirar uma tecla terminal
- (Enter para confirmar, Escape para abortar) como última tecla da sequência; (2) sempre passar um
- `CancellationToken` com timeout curto (ex.: 2s) em `Run(token)` como rede de segurança.
-- **`InputQueue.Enqueue(ConsoleKey.Enter)` não confirmava nada fora do Windows (achado e corrigido na
- Fase 1, 2026-07-22):** `IsPressEnterKey` (`ConsolePlus/src/Shared/ConsoleKeyInfoExtensions.cs:42-48`)
- compara `.Key` no Windows mas **`.KeyChar` (13/10)** nos demais SOs. O driver original da Fase 0 criava
- `ConsoleKeyInfo` com `KeyChar='\0'` para qualquer `ConsoleKey`, quebrando a promessa "idêntico em
- Windows e Linux" especificamente para Enter. Corrigido em `tests/_driver-src/InputQueue.cs`
- (`DefaultCharFor`): `Enqueue(ConsoleKey.Enter/Tab/Backspace/Escape)` agora usa o `KeyChar` real que um
- console de verdade reportaria para essas teclas. Navegação (setas, Home/End) não é afetada — essas
- comparam só `.Key`.
-- **`VirtualTerminal` com `Width`/`Height` abaixo do mínimo trava `Run()` para sempre (achado na Fase 1,
- investigado com o usuário, 2026-07-22):** `RenderBuffer` (`BaseControlPrompt.cs:1523-1530`) tem um
- "small-terminal safeguard": se `console.Height < MinSafeRenderHeight` (10) ou
- `console.Width < MinSafeRenderWidth` (80) — `BaseControlPrompt.cs:67,73` —, chama
- `ShowResizeWarningAndWait`, que bloqueia num loop até o terminal crescer, usando `console.CancelToken`
- (não o `CancellationToken` passado a `Run()`!) como única saída (`BaseControlPrompt.cs:1626-1627`).
- Como `VirtualTerminal.CancelToken => CancellationToken.None` (nunca cancela) e `Width`/`Height` do
- `VirtualScreen` são fixos por design (grid determinístico, sem resize real), esse loop nunca termina —
- nem o `CancellationToken` passado ao teste em `Run(token)` ajuda, porque o safeguard não olha para ele.
- Diagnosticado instrumentando temporariamente `BaseControlPrompt.Run`/`SelectControl.TryResult` com
- breadcrumbs em arquivo (revertido depois, nenhum vestígio ficou em produção). **Não é bug de
- produção** (o safeguard faz sentido para um terminal real onde um humano redimensiona a janela ou
- aperta Ctrl+C) — é uma regra obrigatória para qualquer teste de controle interativo: **sempre usar
- `Width >= 80` e `Height >= 10`** em `VirtualTerminalOptions` (os defaults, seção A.1, já satisfazem
- isso — o problema só aparece se um teste reduzir explicitamente as dimensões). Testar o aviso de
- terminal pequeno em si (ROI baixo, fora do piloto) exigiria plumbing adicional no driver (ex.: linkar
- `VirtualTerminal.CancelToken` a um `CancellationTokenSource` que o teste controla).
-
----
-
-## 14. Fase 2 (piloto) — cobertura por modo em Select/Input, achados e bug corrigido (2026-07-23)
-
-Depois da migração da seção 4 (D1 revista), o usuário pediu uma avaliação técnica+P.O. de expandir a
-cobertura de `Input`/`Select` além do caminho feliz: cada controle tem um `TryResult` com uma cadeia
-longa de `if/else if` por tecla, e as classes de apoio (`EmacsConsoleBuffer`, `Paginator` etc.) já
-estavam exaustivamente testadas isoladamente — o gap real estava no *roteamento* da tecla pelo
-controle + no *render* resultante (VT), camada que os testes de classe de apoio não alcançam.
-
-**Levantamento do código real revelou mais modos do que a suposição inicial:** `Select` tem 2
-(`Select`, `Filter`); `Input` tem **3** (`Input`, `History`, `Sugestions` — este último com 2
-sub-comportamentos: autocomplete inline vs dropdown). Reorganizado em uma suíte por modo, por
-sugestão do usuário:
-
-```
-tests/PromptPlus.Tests/Controls/
- SelectControlTests.cs # globais + modo Select
- SelectControlFilterModeTests.cs # modo Filter
- InputControlTests.cs # globais + modo Input (edição básica, tooltip)
- InputControlHistoryModeTests.cs # modo History
- InputControlSuggestionsModeTests.cs # modo Sugestions (inline + dropdown)
- InputSecretControlTests.cs # Secret/senha (F2) — API pública distinta (IInputSecretControl)
-```
-
-**Descobertas reais de comportamento** (confirmadas por código + execução, não são bug de teste):
-- `Paginator.NextPage`/`PreviousPage` usam módulo (`Paginator.cs:220,239`) — paginação é cíclica, não
- "clampada": PageDown na última página volta pra primeira; PageUp na primeira pula pra última.
-- `PageUp` usa `IndexOption.LastItemWhenHasPages` — pousa no **último** item da página anterior, não
- no primeiro (assimetria deliberada com PageDown, imita "rolar pra cima").
-- `FileHistory.LoadHistory` reordena por `TimeOutTicks` decrescente (`FileHistory.cs:55-57`) — não
- preserva ordem de inserção; ao popular histórico em teste, timeouts iguais/próximos podem inverter
- a ordem esperada por pura corrida de ticks (usar timeouts explicitamente distintos).
-- `InputControl` reverte `ModeView.History`/`Sugestions` de volta pra `Input` (restaurando o texto
- anterior) sempre que `Run()` termina por cancelamento fora do modo `Input` — diferente do `Select`,
- cujo modo `Filter` sobrevive ao cancelamento. A técnica "sem tecla, espera cancelar" (funciona no
- Select) não observa esses modos no Input; só um Enter/Tab real revela o estado.
-- Abrir o dropdown de sugestões **não** carrega a sugestão destacada no buffer de edição — só
- Tab/Enter (aceitar) fazem isso (`InputControl.cs:459,717`). Editar enquanto só navega o dropdown
- continua a partir do texto de ANTES de abrir, não da sugestão em destaque.
-- Shift+Tab só cicla pra trás dentro do dropdown (`autocomplete:false`) — com autocomplete inline
- (`true`, default), Shift+Tab cai no handler genérico e não faz nada.
-
-**Bug real #8 (achado e corrigido)**: `InputControl.TryResult` (`InputControl.cs:415-428`) não setava
-`ResultCtrl` no ramo de cancelamento, diferente do `SelectControl` (que seta). Isso fazia `TryResult`
-retornar `false` mesmo cancelado; em `BaseControlPrompt.Run` (`BaseControlPrompt.cs:568`), a condição
-do `do...while` avalia `TryResult` como parte de si mesma, então um retorno `false` faz o loop rodar
-**uma passada extra de render** — essa passada reconstrói o template com `_errorMessage` já limpo (o
-`WriteError` já tinha escrito+limpado na passada anterior), apagando da tela qualquer erro de
-validação (`PredicateSelected`/`PredicateSelectedAsync` rejeitando) que estivesse visível bem antes do
-controle sair de verdade. Confirmado com uma sonda que tira snapshot em pleno voo (thread de fundo,
-antes de cancelar) vs. depois do cancelamento: o meio-de-voo mostrava a mensagem certinha, o final
-não. Escopo real: só afeta cancelamento externo pegando o controle com um erro na tela — teclas
-reais subsequentes (Escape, corrigir o texto) já seguiam o fluxo normal e funcionavam. **Corrigido**
-adicionando `ResultCtrl = new ResultPrompt(_inputdata!.ToString(), true);` no ramo de
-cancelamento, espelhando o `SelectControl`. Teste de regressão dedicado:
-`InputControlTests.Cancellation_while_a_validation_error_is_showing_does_not_erase_it`.
-
-**Achado colateral (infraestrutura de teste, não é bug de produção)**: `InputControlHistoryModeTests`
-e `Unit/FileHistoryTests.cs` trocam o mesmo campo estático `FileHistory.FileSystem` por um
-`MockFileSystem`; como o xUnit roda classes de teste diferentes em paralelo por padrão, isso causava
-corrida intermitente entre as duas classes. Corrigido com `[CollectionDefinition(...,
-DisableParallelization = true)]` + `[Collection(...)]` nas duas classes (`FileHistoryCollection`,
-definida em `InputControlHistoryModeTests.cs`), forçando-as a rodar sequencialmente uma em relação à
-outra — outras classes continuam paralelas normalmente.
-
-**Cobertura adicional pedida explicitamente pelo usuário nesta rodada**: tecla de toggle de tooltip
-(F1, cicla o índice) e show/hide (Ctrl+F1) em `Select` e `Input`; suíte separada para o controle
-Secret/senha (`InputSecretControlTests.cs` — mascaramento com caractere default `#`/customizado,
-F2 pra revelar/ocultar, `MaskSecret(enabledView:false)` desativando o F2, confirmação com o valor real
-não-mascarado).
-
-**Correção 2026-07-23 (mesma sessão) — o gap do Ctrl+A/Ctrl+K era coberto, era só uma leitura errada
-do código**: a primeira versão desta seção afirmava que `ConsoleHandler.EnabledEmacs`
-(`InputControl.cs:357`) lia um singleton estático global (`ConsolePlusLibrary.ConsolePlus.EnabledEmacs`),
-e por isso pulava testar Ctrl+A/Ctrl+K através do controle (risco de corrida entre classes de teste,
-mesma categoria do `FileHistory.FileSystem`). Isso estava **errado**: `ConsoleHandler` dentro de
-`InputControl` (e de todo `BaseControlPrompt`) é a propriedade de **instância**
-`BaseControlPrompt.ConsoleHandler => console` (`BaseControlPrompt.cs:149`), não uma classe estática —
-então `ConsoleHandler.EnabledEmacs` resolve pra `console.EnabledEmacs`, a propriedade da PRÓPRIA
-`VirtualTerminal` injetada (`VirtualTerminal.cs:90`, autoproperty comum, default `false`). Nenhum
-estado global envolvido, nenhum risco de corrida. Corrigido: `InputControlTests.cs` agora tem 3
-testes (`CtrlA_then_CtrlK_through_the_control_jumps_home_and_kills_to_the_end`,
-`CtrlE_through_the_control_jumps_to_the_end`,
-`CtrlA_then_CtrlK_is_a_no_op_when_emacs_bindings_are_disabled_on_the_console`), bastando
-`vt.EnabledEmacs = true;` na instância antes de `Run()`.
-
-**Lacunas conhecidas, deixadas de fora deliberadamente (não é esquecimento)**:
-- `AnsiDetector.cs`/`UnicodeDetector.cs` — **decisão definitiva do usuário (2026-07-23): não serão
- cobertos.** Não é mais uma pendência aberta, é escopo fechado.
-- Fase 2 **real** (rollout do mesmo padrão pros demais controles — 18 controles, 4 deles "Live") —
- **planejamento controle a controle criado em 2026-07-23**, ver `tests/FASE2-CONTROLS-PLAN.md`
- (só existe no PromptPlus, já que todos os controles são dele; é o tracker de progresso vivo desta
- frente, atualizar lá — não aqui). Progresso até agora: **Grupo 1 concluído** (KeyPress, Switch,
- Slider, ChartBar — 2 bugs reais de produção corrigidos no ChartBar + 1 gap do driver de teste
- corrigido, SGR de 256 cores nunca implementado); Grupo 5 com infraestrutura pronta (`IFileSystem`
- migrado em `FileControl`/`MultiFileControl`); Grupos 2-4 e 6 não iniciados.
-
-**Total ao fim desta frente**: 313 (ConsolePlus.Tests) + 250 (PromptPlus.Tests) = **563 testes**,
-verdes de forma estável em múltiplas execuções completas seguidas (3 TFMs cada). 10 bugs reais de
-produção corrigidos no total desde o início do projeto de testes.
-
----
-
-## Apêndice A — Código do driver (esqueletos completos)
-
-> **Convenções.** Namespace `ConsolePlusLibrary.Testing`. Código em inglês (segue o repo). Os esqueletos
-> compilam contra a API real verificada (`IConsole`, `IConsolePlus`, `ConsoleWriter`, `Style`, `Color`,
-> `ProfileConsole`, `IAnsiCommands`, `ColorSystem`, `AutoDetect`). Pontos que dependem de confirmação de
-> assinatura estão marcados com `// VERIFY`. Fatos de emissão ANSI confirmados no código de produção:
-> CSI = `ESC[`; DEC-private = `ESC[?`; CUP = `ESC[{row+1};{col+1}H` (1-indexed); EL = `ESC[{mode}K`;
-> ED = `ESC[{mode}J`; SGR termina em `m`; cursor `ESC[?25h`/`ESC[?25l`; alt-screen `ESC[?1049h`/`l`;
-> SGR truecolor = `38;2;r;g;b` (fg) / `48;2;r;g;b` (bg); reset = `0`.
-
-### A.1 `VirtualTerminalOptions`
-
-```csharp
-namespace ConsolePlusLibrary.Testing;
-
-public sealed class VirtualTerminalOptions
-{
- public int Width { get; set; } = 80;
- public int Height { get; set; } = 24;
- public ColorSystem ColorDepth { get; set; } = ColorSystem.TrueColor; // determinístico p/ estilo
- public bool SupportsUnicode { get; set; } = true;
- public bool Interactive { get; set; } = true;
- public Color DefaultForeground { get; set; } = new(192, 192, 192); // VERIFY: cor default desejada
- public Color DefaultBackground { get; set; } = new(0, 0, 0);
-}
-```
-
-### A.2 `Cell` + `VirtualScreen`
-
-```csharp
-using System.Text;
-
-namespace ConsolePlusLibrary.Testing;
-
-public readonly record struct Cell(Rune Glyph, Style Style)
-{
- public static Cell Blank(Style style) => new(new Rune(' '), style);
-}
-
-public sealed class VirtualScreen
-{
- private readonly Cell[,] _cells; // [row, col]
- public int Width { get; }
- public int Height { get; }
- public int CursorLeft { get; private set; }
- public int CursorTop { get; private set; }
- public Style Current { get; set; }
-
- public VirtualScreen(int width, int height, Style initial)
- {
- Width = width; Height = height; Current = initial;
- _cells = new Cell[height, width];
- Fill(initial);
- }
-
- public void Fill(Style style)
- {
- for (int r = 0; r < Height; r++)
- for (int c = 0; c < Width; c++)
- _cells[r, c] = Cell.Blank(style);
- }
-
- public void SetCursor(int left, int top)
- {
- CursorLeft = Math.Clamp(left, 0, Width - 1);
- CursorTop = Math.Clamp(top, 0, Height - 1);
- }
-
- /// Stamps one rune at the cursor with and advances (wrap + scroll).
- public void Put(Rune g)
- {
- if (CursorTop >= Height) ScrollUp(1);
- _cells[CursorTop, CursorLeft] = new Cell(g, Current);
- // NOTE: largura de glifo tratada como 1 aqui; para wide-char (CJK/emoji) avançar 2 e marcar
- // a 2ª célula como continuação. Cobrir na Fase 1 se o piloto exercitar wide-chars. // VERIFY
- if (++CursorLeft >= Width) { CursorLeft = 0; if (++CursorTop >= Height) ScrollUp(1); }
- }
-
- public void NewLine() { CursorLeft = 0; if (++CursorTop >= Height) { CursorTop = Height - 1; ScrollUp(1); } }
- public void CarriageReturn() => CursorLeft = 0;
-
- public void EraseInLine(int mode) // 0=cursor->eol, 1=bol->cursor, 2=linha toda
- {
- int from = mode == 0 ? CursorLeft : 0;
- int to = mode == 1 ? CursorLeft : Width - 1;
- for (int c = from; c <= to && c < Width; c++) _cells[CursorTop, c] = Cell.Blank(Current);
- }
-
- public void EraseInDisplay(int mode) // 0=cursor->fim, 1=início->cursor, 2/3=tudo
- {
- if (mode >= 2) { Fill(Current); return; }
- // 0 e 1: limpa a linha do cursor + linhas abaixo/acima
- EraseInLine(mode);
- if (mode == 0) for (int r = CursorTop + 1; r < Height; r++) BlankRow(r);
- else for (int r = 0; r < CursorTop; r++) BlankRow(r);
- }
-
- private void BlankRow(int r) { for (int c = 0; c < Width; c++) _cells[r, c] = Cell.Blank(Current); }
-
- private void ScrollUp(int n)
- {
- for (int i = 0; i < n; i++)
- {
- for (int r = 1; r < Height; r++)
- for (int c = 0; c < Width; c++) _cells[r - 1, c] = _cells[r, c];
- BlankRow(Height - 1);
- }
- CursorTop = Math.Min(CursorTop, Height - 1);
- }
-
- // ---- leitura para asserções ----
- public Style StyleAt(int row, int col) => _cells[row, col].Style;
- public Rune GlyphAt(int row, int col) => _cells[row, col].Glyph;
-
- public string TextAt(int row, int col, int len)
- {
- var sb = new StringBuilder();
- for (int c = col; c < col + len && c < Width; c++) sb.Append(_cells[row, c].Glyph.ToString());
- return sb.ToString();
- }
-
- public string Snapshot() // grid inteiro como texto (trim trailing spaces por linha)
- {
- var sb = new StringBuilder();
- for (int r = 0; r < Height; r++)
- {
- var line = new StringBuilder();
- for (int c = 0; c < Width; c++) line.Append(_cells[r, c].Glyph.ToString());
- sb.AppendLine(line.ToString().TrimEnd());
- }
- return sb.ToString();
- }
-}
-```
-
-### A.3 `AnsiScreenInterpreter` (o `TextWriter` que vira células)
-
-```csharp
-using System.Globalization;
-using System.Text;
-
-namespace ConsolePlusLibrary.Testing;
-
-/// Interpreta o subconjunto de ANSI emitido pelo ConsolePlus e escreve no grid.
-internal sealed class AnsiScreenInterpreter(VirtualScreen screen, VirtualTerminal owner) : TextWriter
-{
- private enum State { Normal, Esc, Csi }
- private State _state = State.Normal;
- private bool _private; // sequência DEC-private (ESC[?)
- private readonly StringBuilder _params = new();
- private char _pendingHighSurrogate = '\0';
-
- public override Encoding Encoding => Encoding.UTF8;
-
- public override void Write(char value) => Feed(value);
- public override void Write(string? value) { if (value != null) foreach (char ch in value) Feed(ch); }
- public override void Write(char[] buffer, int index, int count)
- { for (int i = 0; i < count; i++) Feed(buffer[index + i]); }
- public override void WriteLine() => Feed('\n');
-
- private void Feed(char ch)
- {
- switch (_state)
- {
- case State.Normal:
- if (ch == '\x1b') { _state = State.Esc; return; }
- if (ch == '\n') { screen.NewLine(); return; }
- if (ch == '\r') { screen.CarriageReturn(); return; }
- PutChar(ch);
- return;
-
- case State.Esc:
- if (ch == '[') { _state = State.Csi; _private = false; _params.Clear(); return; }
- throw new NotSupportedException($"ESC {ch} não suportado pelo interpretador de teste.");
-
- case State.Csi:
- if (ch == '?' && _params.Length == 0) { _private = true; return; }
- if ((ch >= '0' && ch <= '9') || ch == ';') { _params.Append(ch); return; }
- Dispatch(ch, _params.ToString());
- _state = State.Normal;
- return;
- }
- }
-
- private void PutChar(char ch)
- {
- if (char.IsHighSurrogate(ch)) { _pendingHighSurrogate = ch; return; }
- if (_pendingHighSurrogate != '\0')
- {
- screen.Put(new Rune(_pendingHighSurrogate, ch));
- _pendingHighSurrogate = '\0';
- return;
- }
- screen.Put(new Rune(ch));
- }
-
- private void Dispatch(char final, string prm)
- {
- switch (final)
- {
- case 'H': // CUP row;col (1-indexed)
- {
- var (a, b) = TwoParams(prm, 1, 1);
- screen.SetCursor(left: b - 1, top: a - 1);
- break;
- }
- case 'K': screen.EraseInLine(FirstParam(prm, 0)); break; // EL
- case 'J': screen.EraseInDisplay(FirstParam(prm, 0)); break; // ED
- case 'm': ApplySgr(prm); break; // SGR
- case 'h': case 'l':
- if (_private) HandlePrivateMode(prm, set: final == 'h');
- else throw new NotSupportedException($"CSI {prm}{final} não suportado.");
- break;
- default:
- throw new NotSupportedException($"CSI {prm}{final} não suportado (surface no piloto).");
- }
- }
-
- private void HandlePrivateMode(string prm, bool set)
- {
- switch (prm)
- {
- case "25": owner.SetCursorVisibleInternal(set); break; // show/hide cursor
- case "1049": owner.SetAltScreenInternal(set); break; // alt screen
- default: /* ignora outros modos privados irrelevantes p/ posição/estilo */ break;
- }
- }
-
- private void ApplySgr(string prm)
- {
- // Suporte primário: reset + truecolor (fg 38;2;r;g;b, bg 48;2;r;g;b).
- // TODO Fase 2: 4-bit (30-37/90-97, 40-47/100-107) e 256 (38;5;n / 48;5;n) para testes dessas profundidades.
- string[] p = prm.Length == 0 ? ["0"] : prm.Split(';');
- for (int i = 0; i < p.Length; i++)
- {
- int code = int.Parse(p[i], CultureInfo.InvariantCulture);
- if (code == 0) { owner.ResetColorInternal(); }
- else if ((code == 38 || code == 48) && i + 4 < p.Length && p[i + 1] == "2")
- {
- var color = new Color(
- (byte)int.Parse(p[i + 2], CultureInfo.InvariantCulture),
- (byte)int.Parse(p[i + 3], CultureInfo.InvariantCulture),
- (byte)int.Parse(p[i + 4], CultureInfo.InvariantCulture));
- if (code == 38) owner.SetForegroundInternal(color); else owner.SetBackgroundInternal(color);
- i += 4;
- }
- // else: código SGR não modelado (bold/underline/…) — ignorado até D4 decidir modelar atributos.
- }
- screen.Current = new Style(owner.ForegroundRgbColor, owner.BackgroundRgbColor);
- }
-
- private static int FirstParam(string prm, int def)
- => prm.Length == 0 ? def : int.Parse(prm.Split(';')[0], CultureInfo.InvariantCulture);
-
- private static (int, int) TwoParams(string prm, int d1, int d2)
- {
- if (prm.Length == 0) return (d1, d2);
- string[] p = prm.Split(';');
- int a = p.Length > 0 && p[0].Length > 0 ? int.Parse(p[0], CultureInfo.InvariantCulture) : d1;
- int b = p.Length > 1 && p[1].Length > 0 ? int.Parse(p[1], CultureInfo.InvariantCulture) : d2;
- return (a, b);
- }
-}
-```
-
-### A.4 `InputQueue`
-
-```csharp
-using System.Collections.Generic;
-
-namespace ConsolePlusLibrary.Testing;
-
-public sealed class InputQueue
-{
- private readonly Queue _keys = new();
-
- public bool HasNext => _keys.Count > 0;
-
- public InputQueue Enqueue(ConsoleKeyInfo key) { _keys.Enqueue(key); return this; }
-
- public InputQueue Enqueue(ConsoleKey key, bool shift = false, bool alt = false, bool ctrl = false)
- => Enqueue(new ConsoleKeyInfo('\0', key, shift, alt, ctrl));
-
- public InputQueue Type(string text)
- {
- foreach (char ch in text)
- _keys.Enqueue(new ConsoleKeyInfo(ch, CharToKey(ch), shift: char.IsUpper(ch), alt: false, control: false));
- return this;
- }
-
- public ConsoleKeyInfo Next() => _keys.Dequeue();
-
- private static ConsoleKey CharToKey(char ch)
- => char.IsLetter(ch) ? Enum.Parse(char.ToUpperInvariant(ch).ToString())
- : char.IsDigit(ch) ? (ConsoleKey)('0' + (ch - '0'))
- : ConsoleKey.Oem1; // VERIFY: mapeamento de pontuação conforme necessidade dos testes
-}
-```
-
-### A.5 `VirtualTerminal : IConsole, IConsolePlus`
-
-```csharp
-using System.Text;
-using ConsolePlusLibrary.ConsoleAbstractions; // ConsoleWriter (internal) — requer InternalsVisibleTo
-using ConsolePlusLibrary.Core; // ProfileConsole (internal)
-
-namespace ConsolePlusLibrary.Testing;
-
-public sealed class VirtualTerminal : IConsole, IConsolePlus
-{
- private readonly VirtualScreen _screen;
- private readonly AnsiScreenInterpreter _interpreter;
- private readonly ConsoleWriter _writer; // WRITER DE PRODUÇÃO
- private readonly ProfileConsole _profile; // profile interno reutilizado
- private readonly InputQueue _input;
- private Color _fg, _bg;
- private bool _cursorVisible = true;
- private bool _writeToError;
- private TargetScreen _buffer = TargetScreen.Primary;
-
- public InputQueue Keys => _input;
- public VirtualScreen Screen => _screen;
-
- private VirtualTerminal(VirtualTerminalOptions o)
- {
- _fg = o.DefaultForeground; _bg = o.DefaultBackground;
- _profile = new ProfileConsole
- {
- ProfileName = "VirtualTerminal",
- IsTerminal = true,
- Interactive = o.Interactive,
- SupportsAnsi = AutoDetect.Yes,
- SupportUnicode = o.SupportsUnicode ? AutoDetect.Yes : AutoDetect.No,
- ColorDepth = o.ColorDepth,
- DefaultForegroundColor = o.DefaultForeground,
- DefaultBackgroundColor = o.DefaultBackground,
- DetectedAnsiSupport = true,
- DetectedUnicodeSupport = o.SupportsUnicode,
- };
- _screen = new VirtualScreen(o.Width, o.Height, new Style(_fg, _bg));
- _interpreter = new AnsiScreenInterpreter(_screen, this);
- _input = new InputQueue();
- _writer = new ConsoleWriter(this); // lê this.Profile.ColorDepth no ctor -> profile já pronto
- }
-
- public static VirtualTerminal Create(Action? configure = null)
- {
- var o = new VirtualTerminalOptions();
- configure?.Invoke(o);
- return new VirtualTerminal(o);
- }
-
- // ---- callbacks internos do interpretador ----
- internal void SetForegroundInternal(Color c) => _fg = c;
- internal void SetBackgroundInternal(Color c) => _bg = c;
- internal void ResetColorInternal() { _fg = _profile.DefaultForegroundColor; _bg = _profile.DefaultBackgroundColor; }
- internal void SetCursorVisibleInternal(bool v) => _cursorVisible = v;
- internal void SetAltScreenInternal(bool alt) => _buffer = alt ? TargetScreen.Secondary : TargetScreen.Primary;
-
- // ---- IConsolePlus ----
- public bool WriteToErrorOutput { get => _writeToError; set => _writeToError = value; }
- public ConsoleWriter Writer => _writer;
-
- // ---- capacidades / perfil ----
- public IProfileReadOnly Profile => _profile;
- public IAnsiCommands Ansi => _writer.Ansi;
- public bool SupportsAnsi => true; // força caminho ANSI determinístico
- public bool SupportsUnicode => _profile.SupportUnicode == AutoDetect.Yes;
- public ColorSystem ColorDepth => _profile.ColorDepth;
- public int Width => _screen.Width; // fixo (não lê System.Console)
- public int Height => _screen.Height;
- public CancellationToken CancelToken => CancellationToken.None;
- public bool EnabledEmacs { get; set; }
- public event EventHandler? SizeChanged;
-
- /// Simula um resize para exercitar o relayout (Fase 3).
- public void RaiseResize(int newWidth, int newHeight)
- {
- // Estratégia p/ largura/altura variáveis: recriar/rebindar o grid é uma opção;
- // no mínimo, disparar o evento que BaseControlPrompt escuta.
- SizeChanged?.Invoke(this, new ConsoleSizeChangedEventArgs
- {
- Width = newWidth, Height = newHeight,
- PreviousWidth = _screen.Width, PreviousHeight = _screen.Height
- });
- // NOTE: p/ variar dimensões de verdade, tornar Width/Height mutáveis e realocar o grid. // VERIFY (Fase 3)
- }
-
- // ---- estilo / cor corrente ----
- public Style CurrentStyle => new(_fg, _bg);
- public ConsoleColor ForegroundColor { get => Color.ToConsoleColor(_fg); set => _fg = value; } // VERIFY ToConsoleColor
- public ConsoleColor BackgroundColor { get => Color.ToConsoleColor(_bg); set => _bg = value; }
- public Color ForegroundRgbColor { get => _fg; set { _fg = value; _writer.ApplyStyle(new Style(_fg, _bg)); } }
- public Color BackgroundRgbColor { get => _bg; set { _bg = value; _writer.ApplyStyle(new Style(_fg, _bg)); } }
- public void ResetColor() { ResetColorInternal(); }
-
- // ---- I/O streams: AQUI o ANSI vira células ----
- public TextWriter Out => _interpreter;
- public TextWriter Error => _interpreter; // erros no mesmo grid (ou um 2º grid, se necessário)
- public TextReader In => TextReader.Null;
- public void SetOut(TextWriter value) { /* no-op: saída é sempre o grid */ }
- public void SetError(TextWriter value) { }
- public void SetIn(TextReader value) { }
- public bool IsInputRedirected => !_profile.Interactive;
- public bool IsOutputRedirected => false;
- public bool IsErrorRedirected => false;
- public Encoding InputEncoding { get; set; } = Encoding.UTF8;
- public Encoding OutputEncoding { get; set; } = Encoding.UTF8;
-
- // ---- núcleo de escrita (delegado ao writer REAL) ----
- public void Write(string? value, Style style, bool clearrestofline = false)
- { if (value != null) _writer.WriteMarkupOutput(value, style, clearrestofline); }
-
- public void WriteRaw(string? value, Style style, bool clearrestofline = false)
- { if (value != null) _writer.WriteOutput(value, style, clearrestofline); }
-
- public void WriteLine(string? value, Style style, bool clearrestofline = false)
- {
- if (value == null) return;
- Write(value, style, clearrestofline);
- _writer.WriteOutput([new Fragment("", null, FragmentKind.LineBreak)]); // VERIFY: Fragment é internal
- }
-
- // ---- cursor: SEMPRE via grid (read-back fiel) ----
- public void SetCursorPosition(int left, int top) => _writer.Ansi.CursorPosition(top, left); // emite CUP -> grid
- public int CursorLeft { get => _screen.CursorLeft; set => _writer.Ansi.CursorPosition(_screen.CursorTop, value); }
- public int CursorTop { get => _screen.CursorTop; set => _writer.Ansi.CursorPosition(value, _screen.CursorLeft); }
- public (int Left, int Top) GetCursorPosition() => (_screen.CursorLeft, _screen.CursorTop);
- public bool CursorVisible { get => _cursorVisible; set => _cursorVisible = value; }
- public bool HideCursor() { _cursorVisible = false; return true; }
- public bool ShowCursor() { _cursorVisible = true; return true; }
-
- public void Clear(Color? backgroundcolor = null)
- {
- if (backgroundcolor.HasValue) _bg = backgroundcolor.Value;
- _writer.Ansi.EraseInDisplay(2);
- SetCursorPosition(0, 0);
- }
-
- // ---- input (fila própria; NÃO usa System.Console) ----
- public bool KeyAvailable => _profile.Interactive && _input.HasNext;
- public ConsoleKeyInfo ReadKey(bool intercept = false) => _input.Next();
- public Task ReadKeyAsync(bool intercept, CancellationToken ct)
- => Task.FromResult(_input.HasNext ? _input.Next() : null);
- public string? ReadLine() => null;
- public int Read() => -1;
- public void Beep() { }
-
- // ---- buffers ----
- public TargetScreen CurrentBuffer => _buffer;
- public bool SwapBuffer(TargetScreen value)
- { if (value == TargetScreen.Secondary) _writer.Ansi.EnterAltScreen(); else _writer.Ansi.ExitAltScreen(); return true; }
-
- // ================= Overloads tipados (mecânicos) =================
- // Os overloads Write/WriteLine/WriteFormat/WriteLineFormat para char, char[], object, bool,
- // double, float, decimal, int, long e string (sem Style) NÃO têm lógica própria: apenas roteiam
- // para o núcleo Write(string?,Style,bool) / WriteLine(string?,Style,bool) usando CurrentStyle,
- // exatamente como em AnsiConsoleAdapter.cs. COPIAR VERBATIM daquele arquivo (blocos ~357–995 e
- // ~1490–1557) — não requerem substituição, pois só dependem do núcleo já implementado acima.
-}
-```
-
-### A.6 `ScreenAssertions` (helpers de asserção)
-
-```csharp
-namespace ConsolePlusLibrary.Testing;
-
-public static class ScreenAssertions
-{
- public static string TextAt(this VirtualTerminal vt, int row, int col, int len)
- => vt.Screen.TextAt(row, col, len);
- public static Style StyleAt(this VirtualTerminal vt, int row, int col)
- => vt.Screen.StyleAt(row, col);
- public static string Snapshot(this VirtualTerminal vt) => vt.Screen.Snapshot();
-
- /// Primeira ocorrência de no grid, como (row, col) ou null.
- public static (int Row, int Col)? Find(this VirtualTerminal vt, string text)
- {
- for (int r = 0; r < vt.Height; r++)
- {
- string line = vt.Screen.TextAt(r, 0, vt.Width);
- int idx = line.IndexOf(text, StringComparison.Ordinal);
- if (idx >= 0) return (r, idx);
- }
- return null;
- }
-}
-```
-
-### A.7 Projetos de teste (`.csproj`)
-
-`tests/ConsolePlus.Tests/ConsolePlus.Tests.csproj`:
-
-```xml
-
-
- net8.0;net9.0;net10.0
- false
- enable
- latest
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-```
-
-`tests/PromptPlus.Tests/PromptPlus.Tests.csproj`: idêntico, trocando o `ProjectReference` para
-`..\..\PromptPlus\src\PromptPlus.csproj` (que já traz o ConsolePlus transitivamente).
-
-### A.8 `InternalsVisibleTo` (no `src` de produção)
-
-`ConsolePlus/src/Properties/InternalsVisibleTo.cs` (ou no `.csproj`):
-
-```csharp
-using System.Runtime.CompilerServices;
-[assembly: InternalsVisibleTo("ConsolePlus.Tests")]
-[assembly: InternalsVisibleTo("PromptPlus.Tests")] // embeda o driver que toca internos do ConsolePlus
-```
-
-`PromptPlus/src/...`:
-
-```csharp
-[assembly: InternalsVisibleTo("PromptPlus.Tests")]
-```
-
-### A.9 Testes de exemplo
-
-ConsolePlus — unidade + render de estilo (camada 1/2):
-
-```csharp
-public class StyleRenderingTests
-{
- [Fact]
- public void Escreve_texto_com_cor_no_grid()
- {
- var vt = VirtualTerminal.Create(o => { o.ColorDepth = ColorSystem.TrueColor; });
- vt.WriteRaw("Ola", new Style(new Color(255, 0, 0), new Color(0, 0, 0)));
-
- vt.TextAt(0, 0, 3).Should().Be("Ola");
- vt.StyleAt(0, 0).Foreground.Should().Be(new Color(255, 0, 0));
- vt.CursorLeft.Should().Be(3);
- }
-}
-```
-
-PromptPlus — controle via driver injetado (camada 2):
-
-```csharp
-public class SelectControlTests
-{
- [Fact]
- public void Select_confirma_item_navegado()
- {
- var vt = VirtualTerminal.Create(o => { o.Width = 80; o.Height = 24; });
- var cfg = new PromptConfig(); // internal -> InternalsVisibleTo
- var controls = new PromptPlusControls(vt, cfg); // internal ctor
-
- vt.Keys.Enqueue(ConsoleKey.DownArrow).Enqueue(ConsoleKey.Enter);
-
- ISelectControl control = controls.Select("Escolha").AddItems(["A", "B", "C"]);
- ResultPrompt result = control.Run();
-
- result.IsAborted.Should().BeFalse();
- result.Content.Should().Be("B");
- vt.Find("B").Should().NotBeNull();
- }
-}
-```
-
-> **D5 fechado:** `Select()`/`Input()` retornam interfaces públicas (`ISelectControl`, `IInputControl`);
-> `Run(CancellationToken token = default)` retorna `ResultPrompt` com `Content`/`IsAborted`. Sem `dynamic`,
-> sem cast para tipo concreto interno.
-
-### A.10 CI (GitHub Actions)
-
-`.github/workflows/tests.yml`:
-
-```yaml
-name: tests
-on: [push, pull_request]
-jobs:
- test:
- strategy:
- fail-fast: false
- matrix:
- os: [windows-latest, ubuntu-latest]
- runs-on: ${{ matrix.os }}
- steps:
- - uses: actions/checkout@v4
- - uses: actions/setup-dotnet@v4
- with:
- dotnet-version: |
- 8.0.x
- 9.0.x
- 10.0.x
- - run: dotnet test tests/ConsolePlus.Tests/ConsolePlus.Tests.csproj -c Release
- - run: dotnet test tests/PromptPlus.Tests/PromptPlus.Tests.csproj -c Release
-```
-
diff --git a/tests/_driver-src/AnsiScreenInterpreter.cs b/tests/_driver-src/AnsiScreenInterpreter.cs
index c0728d2..eb07e98 100644
--- a/tests/_driver-src/AnsiScreenInterpreter.cs
+++ b/tests/_driver-src/AnsiScreenInterpreter.cs
@@ -1,6 +1,6 @@
// ***************************************************************************************
// MIT LICENCE
-// Headless test driver shared by ConsolePlus.Tests and PromptPlus.Tests (linked source, see tests/TEST-PLAN.md)
+// Headless test driver shared by ConsolePlus.Tests and PromptPlus.Tests (linked source)
// ***************************************************************************************
using System;
@@ -153,11 +153,11 @@ private void ApplySgr(string prm)
// 8-bit indexed SGR (38;5;n / 48;5;n) — AnsiColorBuilder.GetEightBit emits this
// whenever a Color has .Number set (named ConsoleColor constants, and anything
// produced by Style.FindStyle's contrast adjustment, which snaps the foreground to
- // the "nearest palette color" — confirmed hit by SwitchStyles.Slider's default
- // Style(ConsoleColor.White, ConsoleColor.DarkGray) during the Fase 2 Grupo 1 pass,
- // 2026-07-23). Resolve the index against ConsolePlus's own already-tested 256-color
- // table (ColorPalette.EightBit) instead of re-deriving the xterm cube/grayscale
- // formula here, so the driver can never drift from what production actually uses.
+ // the "nearest palette color" — confirmed hit by SliderStyles.Slider's default
+ // Style(ConsoleColor.White, ConsoleColor.DarkGray)). Resolve the index against
+ // ConsolePlus's own already-tested 256-color table (ColorPalette.EightBit) instead
+ // of re-deriving the xterm cube/grayscale formula here, so the driver can never
+ // drift from what production actually uses.
int index = int.Parse(p[i + 2], CultureInfo.InvariantCulture);
Color palette = ColorPalette.EightBit[index];
var color = new Color(palette.R, palette.G, palette.B);
diff --git a/tests/_driver-src/Cell.cs b/tests/_driver-src/Cell.cs
index 151ade3..b5cba37 100644
--- a/tests/_driver-src/Cell.cs
+++ b/tests/_driver-src/Cell.cs
@@ -1,6 +1,6 @@
// ***************************************************************************************
// MIT LICENCE
-// Headless test driver shared by ConsolePlus.Tests and PromptPlus.Tests (linked source, see tests/TEST-PLAN.md)
+// Headless test driver shared by ConsolePlus.Tests and PromptPlus.Tests (linked source)
// ***************************************************************************************
using System.Text;
diff --git a/tests/_driver-src/InputQueue.cs b/tests/_driver-src/InputQueue.cs
index 3b1aa32..93975c8 100644
--- a/tests/_driver-src/InputQueue.cs
+++ b/tests/_driver-src/InputQueue.cs
@@ -1,6 +1,6 @@
// ***************************************************************************************
// MIT LICENCE
-// Headless test driver shared by ConsolePlus.Tests and PromptPlus.Tests (linked source, see tests/TEST-PLAN.md)
+// Headless test driver shared by ConsolePlus.Tests and PromptPlus.Tests (linked source)
// ***************************************************************************************
using System;
@@ -71,7 +71,7 @@ private static ConsoleKey CharToKey(char ch)
'-' => ConsoleKey.OemMinus,
'.' => ConsoleKey.OemPeriod,
',' => ConsoleKey.OemComma,
- _ => ConsoleKey.Oem1, // extend as pilot tests need specific punctuation
+ _ => ConsoleKey.Oem1, // extend as tests need specific punctuation
};
}
}
diff --git a/tests/_driver-src/ScreenAssertions.cs b/tests/_driver-src/ScreenAssertions.cs
index 4271de8..0b916f5 100644
--- a/tests/_driver-src/ScreenAssertions.cs
+++ b/tests/_driver-src/ScreenAssertions.cs
@@ -1,6 +1,6 @@
// ***************************************************************************************
// MIT LICENCE
-// Headless test driver shared by ConsolePlus.Tests and PromptPlus.Tests (linked source, see tests/TEST-PLAN.md)
+// Headless test driver shared by ConsolePlus.Tests and PromptPlus.Tests (linked source)
// ***************************************************************************************
using System;
diff --git a/tests/_driver-src/VirtualScreen.cs b/tests/_driver-src/VirtualScreen.cs
index cf564b4..22b789c 100644
--- a/tests/_driver-src/VirtualScreen.cs
+++ b/tests/_driver-src/VirtualScreen.cs
@@ -1,6 +1,6 @@
// ***************************************************************************************
// MIT LICENCE
-// Headless test driver shared by ConsolePlus.Tests and PromptPlus.Tests (linked source, see tests/TEST-PLAN.md)
+// Headless test driver shared by ConsolePlus.Tests and PromptPlus.Tests (linked source)
// ***************************************************************************************
using System;
@@ -94,8 +94,8 @@ public void Put(Rune g)
ScrollUp(1);
}
_cells[CursorTop, CursorLeft] = new Cell(g, Current);
- // Glyph width is treated as 1 here; wide glyphs (CJK/emoji) are out of scope for the
- // Fase 1 pilot (Input+Select) — revisit if a later control's tests need it (TEST-PLAN.md D4).
+ // Glyph width is treated as 1 here; wide glyphs (CJK/emoji) are out of scope for
+ // now — revisit if a later control's tests need it.
if (++CursorLeft >= Width)
{
CursorLeft = 0;
diff --git a/tests/_driver-src/VirtualTerminal.Overloads.cs b/tests/_driver-src/VirtualTerminal.Overloads.cs
index dc8f504..b37fe12 100644
--- a/tests/_driver-src/VirtualTerminal.Overloads.cs
+++ b/tests/_driver-src/VirtualTerminal.Overloads.cs
@@ -1,6 +1,6 @@
// ***************************************************************************************
// MIT LICENCE
-// Headless test driver shared by ConsolePlus.Tests and PromptPlus.Tests (linked source, see tests/TEST-PLAN.md)
+// Headless test driver shared by ConsolePlus.Tests and PromptPlus.Tests (linked source)
// ***************************************************************************************
// Typed Write/WriteLine/WriteFormat/WriteLineFormat overloads for char, char[], object, bool, double,
diff --git a/tests/_driver-src/VirtualTerminal.cs b/tests/_driver-src/VirtualTerminal.cs
index ba5769c..d42c63e 100644
--- a/tests/_driver-src/VirtualTerminal.cs
+++ b/tests/_driver-src/VirtualTerminal.cs
@@ -1,6 +1,6 @@
// ***************************************************************************************
// MIT LICENCE
-// Headless test driver shared by ConsolePlus.Tests and PromptPlus.Tests (linked source, see tests/TEST-PLAN.md)
+// Headless test driver shared by ConsolePlus.Tests and PromptPlus.Tests (linked source)
// ***************************************************************************************
using ConsolePlusLibrary.ConsoleAbstractions;
@@ -16,7 +16,7 @@ namespace ConsolePlusLibrary.Testing
///
/// In-memory / that hosts the real
/// and interprets its ANSI output into a , giving deterministic, headless
- /// read-back of cursor position and style. See tests/TEST-PLAN.md sections 3 and 5.
+ /// read-back of cursor position and style.
///
public sealed partial class VirtualTerminal : IConsole, IConsolePlus
{
diff --git a/tests/_driver-src/VirtualTerminalOptions.cs b/tests/_driver-src/VirtualTerminalOptions.cs
index ec41e66..c0ed1c4 100644
--- a/tests/_driver-src/VirtualTerminalOptions.cs
+++ b/tests/_driver-src/VirtualTerminalOptions.cs
@@ -1,6 +1,6 @@
// ***************************************************************************************
// MIT LICENCE
-// Headless test driver shared by ConsolePlus.Tests and PromptPlus.Tests (linked source, see tests/TEST-PLAN.md)
+// Headless test driver shared by ConsolePlus.Tests and PromptPlus.Tests (linked source)
// ***************************************************************************************
namespace ConsolePlusLibrary.Testing
@@ -13,7 +13,7 @@ namespace ConsolePlusLibrary.Testing
/// constructor (new Color(r, g, b)), never from a named palette constant (e.g. Color.White).
/// Named constants carry an internal palette Number, which makes
/// fall back to 8-bit SGR (38;5;n/48;5;n) instead of truecolor (38;2;r;g;b) — a sequence
- /// does not model yet (Fase 2, see TEST-PLAN.md D4/A.3).
+ /// does not model yet (Fase 2).
///
public sealed class VirtualTerminalOptions
{