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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions experiments/core/edit_csi_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,28 @@ int main() {
parser.feed("\x1b[1;1H");
parser.feed("\x1b[99@");
require(row_text(parser.screen, 0).empty(), "over-wide ICH should clear the row");

// IL opens blank rows at the cursor and lets the bottom rows fall off.
Parser lines(4, 10, 10, termforge::ParseMode::recovery);
lines.feed("a\r\nb\r\nc\r\nd");
lines.feed("\x1b[1;1H");
lines.feed("\x1b[2L");
require(row_text(lines.screen, 0).empty() && row_text(lines.screen, 1).empty() &&
row_text(lines.screen, 2) == "a" && row_text(lines.screen, 3) == "b",
"IL did not open blank rows");
require(lines.screen.row == 0 && lines.screen.column == 0, "IL moved the cursor");

// DL closes rows from the cursor and appends blanks at the bottom.
lines.feed("\x1b[M");
require(row_text(lines.screen, 0).empty() && row_text(lines.screen, 1) == "a" &&
row_text(lines.screen, 2) == "b" && row_text(lines.screen, 3).empty(),
"DL did not close a row");

// A count past the bottom only removes what remains.
lines.feed("\x1b[99L");
require(row_text(lines.screen, 0).empty() && row_text(lines.screen, 1).empty() &&
row_text(lines.screen, 2).empty() && row_text(lines.screen, 3).empty(),
"over-large IL should clear the screen");
std::puts("edit csi contracts passed");
return 0;
}
20 changes: 20 additions & 0 deletions experiments/core/screen.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -438,6 +438,26 @@ class Screen {
// Microsoft Terminal ECH contract explicitly cancels delayed wrapping.
wrap_pending = false; invalidate_attachment();
}
void insert_lines(int count) {
if (count < 1) throw InputError("invalid insert line count");
const int insert = (std::min)(count, height() - row);
if (insert <= 0) { wrap_pending = false; return; }
// Rows pushed past the bottom leave the screen; their identities stay
// spent. Inserted rows are new lines and get fresh identities.
for (int r = height() - 1; r >= row + insert; --r) lines[r] = std::move(lines[r - insert]);
for (int r = row; r < row + insert; ++r) lines[r] = make_line(width(), Cell{}, next_id_);
wrap_pending = false;
invalidate_attachment();
}
void delete_lines(int count) {
if (count < 1) throw InputError("invalid delete line count");
const int remove = (std::min)(count, height() - row);
if (remove <= 0) { wrap_pending = false; return; }
for (int r = row; r < height() - remove; ++r) lines[r] = std::move(lines[r + remove]);
for (int r = height() - remove; r < height(); ++r) lines[r] = make_line(width(), Cell{}, next_id_);
wrap_pending = false;
invalidate_attachment();
}
void insert_characters(int count) {
if (count < 1) throw InputError("invalid insert character count");
const int shift = (std::min)(count, width() - column);
Expand Down
2 changes: 2 additions & 0 deletions experiments/core/terminal.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ class Parser {
case 'H': case 'f': screen.move(n - 1, params.size() < 2 || params[1] == 0 ? 0 : params[1] - 1); break;
case 'J': screen.erase_display(params[0]); break;
case 'K': screen.erase_line(params[0]); break;
case 'L': screen.insert_lines(n); break;
case 'M': screen.delete_lines(n); break;
case 'X': screen.erase_characters(n); break;
case '@': screen.insert_characters(n); break;
case 'P': screen.delete_characters(n); break;
Expand Down
Loading