Skip to content

Commit

Permalink
add ECH, update EID for EIL also
Browse files Browse the repository at this point in the history
  • Loading branch information
doubleyewdee committed Nov 22, 2018
1 parent 95839d7 commit 1c5900c
Show file tree
Hide file tree
Showing 7 changed files with 162 additions and 48 deletions.
110 changes: 81 additions & 29 deletions src/ConsoleBuffer/Buffer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,20 +65,7 @@ public void Append(byte[] bytes, int length)
switch (this.parser.Append(this.currentChar))
{
case ParserAppendResult.Render:
this.lines[this.CurrentLine].Set(this.cursorX, new Character { Glyph = this.currentChar });
++this.cursorX;
if (this.cursorX == this.Width)
{
this.cursorX = 0;
if (this.cursorY == this.Height - 1)
{
this.ScrollDown();
}
else
{
++this.cursorY;
}
}
this.RenderAtCursor(this.currentChar);
break;
case ParserAppendResult.Complete:
this.ExecuteParserCommand();
Expand All @@ -96,6 +83,28 @@ public void Append(byte[] bytes, int length)
}
}

/// <summary>
/// Renders the current character at the cursor, advances the cursor, and proceeds to the next line if necessary while scrolling the buffer.
/// </summary>
/// <param name="ch"></param>
private void RenderAtCursor(int ch)
{
this.lines[this.CurrentLine].Set(this.cursorX, new Character { Glyph = ch });
++this.cursorX;
if (this.cursorX == this.Width)
{
this.cursorX = 0;
if (this.cursorY == this.Height - 1)
{
this.ScrollDown();
}
else
{
++this.cursorY;
}
}
}

/// <summary>
/// Append a single byte to the current character.
/// </summary>
Expand Down Expand Up @@ -171,27 +180,70 @@ private void HandleControlSequence(Commands.ControlSequence cmd)
{
switch (cmd)
{
case Commands.EraseInDisplay eid:
switch (eid.Direction)
case Commands.EraseCharacter ec:
for (var c = 0; c < ec.Count; ++c)
{
case Commands.EraseInDisplay.Parameter.All:
for (var y = this.topVisibleLine; y <= this.bottomVisibleLine; ++y)
this.lines[this.CurrentLine].SetGlyph(this.cursorX, 0x20);
++this.cursorX;
if (this.cursorX == this.Width)
{
this.lines[y].Clear();
// we won't advance beyond the end of viewable space when erasing.
if (this.cursorY < this.Height - 1)
{
++this.cursorY;
this.cursorX = 0;
}
}
}
break;
case Commands.EraseIn eid when eid.Type == Commands.EraseIn.EraseType.Display:
int startY, endY;
switch (eid.Direction)
{
case Commands.EraseIn.Parameter.All:
startY = this.topVisibleLine;
endY = this.bottomVisibleLine;
break;
case Commands.EraseInDisplay.Parameter.Above:
for (var y = this.topVisibleLine; y < this.CurrentLine; ++y)
{
this.lines[y].Clear();
}
case Commands.EraseIn.Parameter.Before:
startY = this.topVisibleLine;
endY = this.CurrentLine;
break;
case Commands.EraseInDisplay.Parameter.Below:
for (var y = this.CurrentLine; y < this.bottomVisibleLine; ++y)
{
this.lines[y].Clear();
}
case Commands.EraseIn.Parameter.After:
startY = this.CurrentLine;
endY = this.bottomVisibleLine;
break;
default:
return;
}

for (var y = startY; y <= endY; ++y)
{
this.lines[y].Clear();
}
break;
case Commands.EraseIn eil when eil.Type == Commands.EraseIn.EraseType.Line:
int startX, endX;
switch (eil.Direction)
{
case Commands.EraseIn.Parameter.All:
startX = 0;
endX = this.Width - 1;
break;
case Commands.EraseIn.Parameter.Before:
startX = 0;
endX = this.cursorX;
break;
case Commands.EraseIn.Parameter.After:
startX = this.cursorX;
endX = this.Width - 1;
break;
default:
return;
}

for (var x = startX; x <= endX; ++x)
{
this.lines[this.CurrentLine].SetGlyph(x, 0x20);
}
break;
case Commands.SetMode sm:
Expand Down
3 changes: 1 addition & 2 deletions src/ConsoleBuffer/Character.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace ConsoleBuffer
namespace ConsoleBuffer
{
// XXX: Gonna end up with a lot of these and they're really freakin' big.
// could consider a morphable type with different sizes to avoid the (currently) 12 bytes-per-character issue.
Expand All @@ -12,7 +12,6 @@ public struct ColorInfo
public byte R;
public byte G;
public byte B;
public byte A;
}

public ColorInfo Foreground { get; set; }
Expand Down
6 changes: 5 additions & 1 deletion src/ConsoleBuffer/Commands/ControlSequence.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,12 @@ public static Base Create(char command, string bufferData)
case 'h':
case 'l':
return new SetMode(bufferData, command == 'h');
case 'H':
return new EraseCharacter(bufferData);
case 'J':
return new EraseInDisplay(bufferData);
return new EraseIn(bufferData, EraseIn.EraseType.Display);
case 'K':
return new EraseIn(bufferData, EraseIn.EraseType.Line);
}
return new Unsupported($"^[[{bufferData}{command}");
}
Expand Down
19 changes: 19 additions & 0 deletions src/ConsoleBuffer/Commands/EraseCharacter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using System;

namespace ConsoleBuffer.Commands
{
public sealed class EraseCharacter : ControlSequence
{
public int Count;

public EraseCharacter(string bufferData) : base(bufferData)
{
this.Count = 1;
// NB: if the user provides an invalid parameter we still choose to erase one character.
if (this.Parameters.Count == 1 && ushort.TryParse(this.Parameters[0], out var count))
{
this.Count = Math.Max(1, (int)count); // 0 means scroll 1.
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,30 +1,38 @@
using System;

namespace ConsoleBuffer.Commands
{
public sealed class EraseInDisplay : ControlSequence
using System;

public sealed class EraseIn : ControlSequence
{
public enum Parameter
{
Below = 0,
Above,
Before = 0,
After,
All,
// xterm supports '3' for saved lines.
Unknown,
}

public enum EraseType
{
Line = 0,
Display,
}

public Parameter Direction { get; private set; }
public EraseType Type { get; private set; }

public EraseInDisplay(string bufferData) : base(bufferData)
public EraseIn(string bufferData, EraseType type) : base(bufferData)
{
this.Direction = Parameter.Unknown;
this.Type = type;
if (this.IsExtended)
{
return; // extended means selective erase which we don't support.
}
if (this.Parameters.Count == 0)
{
this.Direction = Parameter.Below;
this.Direction = Parameter.Before;
}
if (this.Parameters.Count == 1 && uint.TryParse(this.Parameters[0], out var param))
{
Expand Down
18 changes: 17 additions & 1 deletion src/ConsoleBuffer/Line.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,23 @@ public void Set(int pos, Character ch)
this.chars[pos] = ch;
}

/// <summary>
/// Set the glyph value at the given position while retaining existing properties.
/// </summary>
/// <param name="pos">Position in line.</param>
/// <param name="glyph">Value to store.</param>
public void SetGlyph(int pos, int glyph)
{
if (this.chars.Count <= pos)
{
this.Extend(pos);
}

var current = this.chars[pos];
current.Glyph = glyph;
this.chars[pos] = current;
}

public void Clear()
{
this.chars.Clear();
Expand All @@ -42,7 +59,6 @@ private void Extend(int pos)
}
}


public IEnumerator<Character> GetEnumerator()
{
return this.chars.GetEnumerator();
Expand Down
32 changes: 24 additions & 8 deletions test/ConsoleBufferTests/SequenceParserTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,17 +62,17 @@ public void OSCommands()
}

[TestMethod]
[DataRow("", ConsoleBuffer.Commands.EraseInDisplay.Parameter.Below)]
[DataRow("0", ConsoleBuffer.Commands.EraseInDisplay.Parameter.Below)]
[DataRow("1", ConsoleBuffer.Commands.EraseInDisplay.Parameter.Above)]
[DataRow("2", ConsoleBuffer.Commands.EraseInDisplay.Parameter.All)]
[DataRow("?2", ConsoleBuffer.Commands.EraseInDisplay.Parameter.Unknown)]
[DataRow("-50", ConsoleBuffer.Commands.EraseInDisplay.Parameter.Unknown)]
public void EraseInDisplay(string direction, ConsoleBuffer.Commands.EraseInDisplay.Parameter expectedDirection)
[DataRow("", ConsoleBuffer.Commands.EraseIn.Parameter.Before)]
[DataRow("0", ConsoleBuffer.Commands.EraseIn.Parameter.Before)]
[DataRow("1", ConsoleBuffer.Commands.EraseIn.Parameter.After)]
[DataRow("2", ConsoleBuffer.Commands.EraseIn.Parameter.All)]
[DataRow("?2", ConsoleBuffer.Commands.EraseIn.Parameter.Unknown)]
[DataRow("-50", ConsoleBuffer.Commands.EraseIn.Parameter.Unknown)]
public void EraseInDisplay(string direction, ConsoleBuffer.Commands.EraseIn.Parameter expectedDirection)
{
var command = $"\x1b[{direction}J";
var parser = this.EnsureCommandParses(command);
var cmd = parser.Command as ConsoleBuffer.Commands.EraseInDisplay;
var cmd = parser.Command as ConsoleBuffer.Commands.EraseIn;
Assert.IsNotNull(cmd);
Assert.AreEqual(expectedDirection, cmd.Direction);
}
Expand All @@ -98,6 +98,22 @@ public void SetMode(string mode, ConsoleBuffer.Commands.SetMode.Parameter expect
}
}

[TestMethod]
[DataRow("", 1)]
[DataRow("1", 1)]
[DataRow("8675309", 1)]
[DataRow(";;;", 1)]
[DataRow("42", 42)]
[DataRow("0", 1)]
public void EraseCharacter(string data, int count)
{
var command = $"\x1b[{data}H";
var parser = this.EnsureCommandParses(command);
var cmd = parser.Command as ConsoleBuffer.Commands.EraseCharacter;
Assert.IsNotNull(cmd);
Assert.AreEqual(count, cmd.Count);
}

private SequenceParser EnsureCommandParses(string command)
{
var parser = new SequenceParser();
Expand Down

0 comments on commit 1c5900c

Please sign in to comment.