Forge
csharpdeeb25a2
1using CascadeIDE.Contracts;
2
3namespace CascadeIDE.Features.Terminal.DataAcquisition;
4
5/// <summary>
6/// Не шлёт подряд много Backspace без ответа shell (PSReadLine рисует строку после каждого удаления).
7/// На пустой строке stdout молчит — лимит защищает от FailFast legacy ConsoleHost pwsh.
8/// </summary>
9[IoBoundary]
10internal sealed class IntegratedShellBackspaceBurstGuard
11{
12 private const int MaxConsecutiveBackspaceWithoutOutput = 48;
13
14 private int _consecutiveBackspaceWithoutOutput;
15
16 public void Reset() => _consecutiveBackspaceWithoutOutput = 0;
17
18 public void NotifyShellOutput() => _consecutiveBackspaceWithoutOutput = 0;
19
20 public byte[] FilterUserInput(ReadOnlySpan<byte> input)
21 {
22 if (input.IsEmpty)
23 return [];
24
25 input = IntegratedShellStreamSanitizer.StripLeadingUtf8Bom(input);
26 if (input.IsEmpty)
27 return [];
28
29 if (!ContainsBackspace(input))
30 {
31 _consecutiveBackspaceWithoutOutput = 0;
32 return input.ToArray();
33 }
34
35 if (IntegratedShellLaunch.IsPureBackspaceInput(input))
36 {
37 if (_consecutiveBackspaceWithoutOutput >= MaxConsecutiveBackspaceWithoutOutput)
38 return [];
39
40 _consecutiveBackspaceWithoutOutput++;
41 return input.ToArray();
42 }
43
44 var output = new List<byte>(input.Length);
45 var index = 0;
46 while (index < input.Length)
47 {
48 var current = input[index];
49 if (current is 0x7F or 0x08)
50 {
51 if (_consecutiveBackspaceWithoutOutput >= MaxConsecutiveBackspaceWithoutOutput)
52 {
53 index++;
54 continue;
55 }
56
57 output.Add(current);
58 _consecutiveBackspaceWithoutOutput++;
59 index++;
60 continue;
61 }
62
63 _consecutiveBackspaceWithoutOutput = 0;
64 output.Add(current);
65 index++;
66 }
67
68 return [.. output];
69 }
70
71 private static bool ContainsBackspace(ReadOnlySpan<byte> input)
72 {
73 foreach (var b in input)
74 {
75 if (b is 0x7F or 0x08)
76 return true;
77 }
78
79 return false;
80 }
81}
82
View only · write via MCP/CIDE