|
@@ -1,3 +1,4 @@
|
|
|
|
|
+import 'dart:math';
|
|
|
import 'dart:typed_data';
|
|
import 'dart:typed_data';
|
|
|
|
|
|
|
|
import 'package:xterm/terminal/cursor.dart';
|
|
import 'package:xterm/terminal/cursor.dart';
|
|
@@ -15,6 +16,7 @@ import 'package:xterm/terminal/cursor.dart';
|
|
|
/// 1byte 1byte 1byte 1byte
|
|
/// 1byte 1byte 1byte 1byte
|
|
|
|
|
|
|
|
const _cellSize = 16;
|
|
const _cellSize = 16;
|
|
|
|
|
+const _cellSize64Bit = _cellSize >> 3;
|
|
|
|
|
|
|
|
const _cellContent = 0;
|
|
const _cellContent = 0;
|
|
|
const _cellFgColor = 4;
|
|
const _cellFgColor = 4;
|
|
@@ -24,75 +26,106 @@ const _cellBgColor = 8;
|
|
|
const _cellWidth = 12;
|
|
const _cellWidth = 12;
|
|
|
const _cellFlags = 13;
|
|
const _cellFlags = 13;
|
|
|
|
|
|
|
|
|
|
+int _nextLength(int lengthRequirement) {
|
|
|
|
|
+ var nextLength = 2;
|
|
|
|
|
+ while (nextLength < lengthRequirement) {
|
|
|
|
|
+ nextLength *= 2;
|
|
|
|
|
+ }
|
|
|
|
|
+ return nextLength;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
class BufferLine {
|
|
class BufferLine {
|
|
|
- BufferLine() {
|
|
|
|
|
- const initLength = 64;
|
|
|
|
|
- _cells = ByteData(initLength * _cellSize);
|
|
|
|
|
|
|
+ BufferLine({int length = 64, this.isWrapped = false}) {
|
|
|
|
|
+ _maxCols = _nextLength(length);
|
|
|
|
|
+ _cells = ByteData(_maxCols * _cellSize);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
late ByteData _cells;
|
|
late ByteData _cells;
|
|
|
|
|
|
|
|
- bool get isWrapped => _isWrapped;
|
|
|
|
|
- bool _isWrapped = false;
|
|
|
|
|
|
|
+ bool isWrapped;
|
|
|
|
|
|
|
|
- void ensure(int length) {
|
|
|
|
|
- final expectedLengthInBytes = length * _cellSize;
|
|
|
|
|
|
|
+ int _maxCols = 64;
|
|
|
|
|
|
|
|
- if (expectedLengthInBytes < _cells.lengthInBytes) {
|
|
|
|
|
|
|
+ void ensure(int length) {
|
|
|
|
|
+ if (length <= _maxCols) {
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- var newLengthInBytes = _cells.lengthInBytes;
|
|
|
|
|
- while (newLengthInBytes < expectedLengthInBytes) {
|
|
|
|
|
- newLengthInBytes *= 2;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- final newCells = ByteData(newLengthInBytes);
|
|
|
|
|
|
|
+ final nextLength = _nextLength(length);
|
|
|
|
|
+ final newCells = ByteData(nextLength * _cellSize);
|
|
|
newCells.buffer.asInt64List().setAll(0, _cells.buffer.asInt64List());
|
|
newCells.buffer.asInt64List().setAll(0, _cells.buffer.asInt64List());
|
|
|
_cells = newCells;
|
|
_cells = newCells;
|
|
|
|
|
+ _maxCols = nextLength;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void insert(int index) {
|
|
void insert(int index) {
|
|
|
insertN(index, 1);
|
|
insertN(index, 1);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ void removeN(int index, int count) {
|
|
|
|
|
+ final moveStart = index * _cellSize64Bit;
|
|
|
|
|
+ final moveOffset = count * _cellSize64Bit;
|
|
|
|
|
+ final moveEnd = (_maxCols - count) * _cellSize64Bit;
|
|
|
|
|
+ final bufferEnd = _maxCols * _cellSize64Bit;
|
|
|
|
|
+
|
|
|
|
|
+ // move data backward
|
|
|
|
|
+ final cells = _cells.buffer.asInt64List();
|
|
|
|
|
+ for (var i = moveStart; i < moveEnd; i++) {
|
|
|
|
|
+ cells[i] = cells[i + moveOffset];
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // set empty cells to 0
|
|
|
|
|
+ for (var i = moveEnd; i < bufferEnd; i++) {
|
|
|
|
|
+ cells[i] = 0x00;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
void insertN(int index, int count) {
|
|
void insertN(int index, int count) {
|
|
|
// start
|
|
// start
|
|
|
// +--------------------------|-----------------------------------+
|
|
// +--------------------------|-----------------------------------+
|
|
|
// | | |
|
|
// | | |
|
|
|
- // +--------------------------\--\--------------------------------+
|
|
|
|
|
|
|
+ // +--------------------------\--\--------------------------------+ end
|
|
|
// \ \
|
|
// \ \
|
|
|
// \ \
|
|
// \ \
|
|
|
// v v
|
|
// v v
|
|
|
// +--------------------------|--|--------------------------------+
|
|
// +--------------------------|--|--------------------------------+
|
|
|
// | | | |
|
|
// | | | |
|
|
|
- // +--------------------------|--|--------------------------------+
|
|
|
|
|
|
|
+ // +--------------------------|--|--------------------------------+ end
|
|
|
// start start+offset
|
|
// start start+offset
|
|
|
|
|
|
|
|
- final start = (index * _cellSize).clamp(0, _cells.lengthInBytes);
|
|
|
|
|
- final offset = (count * _cellSize).clamp(0, _cells.lengthInBytes - start);
|
|
|
|
|
|
|
+ final moveStart = index * _cellSize64Bit;
|
|
|
|
|
+ final moveOffset = count * _cellSize64Bit;
|
|
|
|
|
+ final bufferEnd = _maxCols * _cellSize64Bit;
|
|
|
|
|
|
|
|
// move data forward
|
|
// move data forward
|
|
|
- final cells = _cells.buffer.asInt8List();
|
|
|
|
|
- for (var i = _cells.lengthInBytes - offset - 1; i >= start; i++) {
|
|
|
|
|
- cells[i + offset] = cells[i];
|
|
|
|
|
|
|
+ final cells = _cells.buffer.asInt64List();
|
|
|
|
|
+ for (var i = bufferEnd - moveOffset - 1; i >= moveStart; i--) {
|
|
|
|
|
+ cells[i + moveOffset] = cells[i];
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// set inserted cells to 0
|
|
// set inserted cells to 0
|
|
|
- for (var i = start; i < start + offset; i++) {
|
|
|
|
|
|
|
+ for (var i = moveStart; i < moveStart + moveOffset; i++) {
|
|
|
cells[i] = 0x00;
|
|
cells[i] = 0x00;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void clear() {
|
|
void clear() {
|
|
|
- removeRange(0, _cells.lengthInBytes ~/ _cellSize);
|
|
|
|
|
|
|
+ clearRange(0, _cells.lengthInBytes ~/ _cellSize);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- void erase(Cursor cursor, int start, int end) {
|
|
|
|
|
|
|
+ void erase(Cursor cursor, int start, int end, [bool resetIsWrapped = false]) {
|
|
|
ensure(end);
|
|
ensure(end);
|
|
|
for (var i = start; i < end; i++) {
|
|
for (var i = start; i < end; i++) {
|
|
|
cellErase(i, cursor);
|
|
cellErase(i, cursor);
|
|
|
}
|
|
}
|
|
|
|
|
+ if (resetIsWrapped) {
|
|
|
|
|
+ isWrapped = false;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ void cellClear(int index) {
|
|
|
|
|
+ _cells.setInt64(index * _cellSize, 0x00);
|
|
|
|
|
+ _cells.setInt64(index * _cellSize + 8, 0x00);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void cellInitialize(
|
|
void cellInitialize(
|
|
@@ -109,15 +142,22 @@ class BufferLine {
|
|
|
_cells.setInt8(cell + _cellFlags, cursor.flags);
|
|
_cells.setInt8(cell + _cellFlags, cursor.flags);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ bool cellHasContent(int index) {
|
|
|
|
|
+ return cellGetContent(index) != 0;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
int cellGetContent(int index) {
|
|
int cellGetContent(int index) {
|
|
|
- return _cells.getInt32(index * _cellSize + _cellContent);
|
|
|
|
|
|
|
+ return _cells.getUint32(index * _cellSize + _cellContent);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void cellSetContent(int index, int content) {
|
|
void cellSetContent(int index, int content) {
|
|
|
- return _cells.setInt32(index * _cellSize + _cellContent, content);
|
|
|
|
|
|
|
+ _cells.setInt32(index * _cellSize + _cellContent, content);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
int cellGetFgColor(int index) {
|
|
int cellGetFgColor(int index) {
|
|
|
|
|
+ if (index >= _maxCols) {
|
|
|
|
|
+ return 0;
|
|
|
|
|
+ }
|
|
|
return _cells.getInt32(index * _cellSize + _cellFgColor);
|
|
return _cells.getInt32(index * _cellSize + _cellFgColor);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -126,6 +166,9 @@ class BufferLine {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
int cellGetBgColor(int index) {
|
|
int cellGetBgColor(int index) {
|
|
|
|
|
+ if (index >= _maxCols) {
|
|
|
|
|
+ return 0;
|
|
|
|
|
+ }
|
|
|
return _cells.getInt32(index * _cellSize + _cellBgColor);
|
|
return _cells.getInt32(index * _cellSize + _cellBgColor);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -134,6 +177,9 @@ class BufferLine {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
int cellGetFlags(int index) {
|
|
int cellGetFlags(int index) {
|
|
|
|
|
+ if (index >= _maxCols) {
|
|
|
|
|
+ return 0;
|
|
|
|
|
+ }
|
|
|
return _cells.getInt8(index * _cellSize + _cellFlags);
|
|
return _cells.getInt8(index * _cellSize + _cellFlags);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -142,6 +188,9 @@ class BufferLine {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
int cellGetWidth(int index) {
|
|
int cellGetWidth(int index) {
|
|
|
|
|
+ if (index >= _maxCols) {
|
|
|
|
|
+ return 1;
|
|
|
|
|
+ }
|
|
|
return _cells.getInt8(index * _cellSize + _cellWidth);
|
|
return _cells.getInt8(index * _cellSize + _cellWidth);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -154,6 +203,9 @@ class BufferLine {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
bool cellHasFlag(int index, int flag) {
|
|
bool cellHasFlag(int index, int flag) {
|
|
|
|
|
+ if (index >= _maxCols) {
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
return cellGetFlags(index) & flag != 0;
|
|
return cellGetFlags(index) & flag != 0;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -166,6 +218,38 @@ class BufferLine {
|
|
|
cellSetFgColor(index, cursor.fg);
|
|
cellSetFgColor(index, cursor.fg);
|
|
|
cellSetBgColor(index, cursor.bg);
|
|
cellSetBgColor(index, cursor.bg);
|
|
|
cellSetFlags(index, cursor.flags);
|
|
cellSetFlags(index, cursor.flags);
|
|
|
|
|
+ cellSetWidth(index, 0);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ int getTrimmedLength([int? cols]) {
|
|
|
|
|
+ if (cols == null) {
|
|
|
|
|
+ cols = _maxCols;
|
|
|
|
|
+ }
|
|
|
|
|
+ for (var i = cols - 1; i >= 0; i--) {
|
|
|
|
|
+ if (cellGetContent(i) != 0) {
|
|
|
|
|
+ // we are at the last cell in this line that has content.
|
|
|
|
|
+ // the length of this line is the index of this cell + 1
|
|
|
|
|
+ // the only exception is that if that last cell is wider
|
|
|
|
|
+ // than 1 then we have to add the diff
|
|
|
|
|
+ final lastCellWidth = cellGetWidth(i);
|
|
|
|
|
+ return i + lastCellWidth;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return 0;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ void copyCellsFrom(BufferLine src, int srcCol, int dstCol, int len) {
|
|
|
|
|
+ ensure(dstCol + len);
|
|
|
|
|
+
|
|
|
|
|
+ final intsToCopy = len * _cellSize64Bit;
|
|
|
|
|
+ final srcStart = srcCol * _cellSize64Bit;
|
|
|
|
|
+ final dstStart = dstCol * _cellSize64Bit;
|
|
|
|
|
+
|
|
|
|
|
+ final cells = _cells.buffer.asInt64List();
|
|
|
|
|
+ final srcCells = src._cells.buffer.asInt64List();
|
|
|
|
|
+ for (var i = 0; i < intsToCopy; i++) {
|
|
|
|
|
+ cells[dstStart + i] = srcCells[srcStart + i];
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// int cellGetHash(int index) {
|
|
// int cellGetHash(int index) {
|
|
@@ -175,13 +259,44 @@ class BufferLine {
|
|
|
// return a ^ b;
|
|
// return a ^ b;
|
|
|
// }
|
|
// }
|
|
|
|
|
|
|
|
- void removeRange(int start, int end) {
|
|
|
|
|
|
|
+ void clearRange(int start, int end) {
|
|
|
|
|
+ end = min(end, _maxCols);
|
|
|
// start = start.clamp(0, _cells.length);
|
|
// start = start.clamp(0, _cells.length);
|
|
|
// end ??= _cells.length;
|
|
// end ??= _cells.length;
|
|
|
// end = end.clamp(start, _cells.length);
|
|
// end = end.clamp(start, _cells.length);
|
|
|
// _cells.removeRange(start, end);
|
|
// _cells.removeRange(start, end);
|
|
|
for (var index = start; index < end; index++) {
|
|
for (var index = start; index < end; index++) {
|
|
|
- cellSetContent(index, 0x00);
|
|
|
|
|
|
|
+ cellClear(index);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @override
|
|
|
|
|
+ String toString() {
|
|
|
|
|
+ final result = StringBuffer();
|
|
|
|
|
+ for (int i = 0; i < _maxCols; i++) {
|
|
|
|
|
+ final code = cellGetContent(i);
|
|
|
|
|
+ if (code == 0) {
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+ result.writeCharCode(code);
|
|
|
|
|
+ }
|
|
|
|
|
+ return result.toString();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ String toDebugString(int cols) {
|
|
|
|
|
+ final result = StringBuffer();
|
|
|
|
|
+ final length = getTrimmedLength();
|
|
|
|
|
+ for (int i = 0; i < max(cols, length); i++) {
|
|
|
|
|
+ var code = cellGetContent(i);
|
|
|
|
|
+ if (code == 0) {
|
|
|
|
|
+ if (cellGetWidth(i) == 0) {
|
|
|
|
|
+ code = '_'.runes.first;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ code = cellGetWidth(i).toString().runes.first;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ result.writeCharCode(code);
|
|
|
}
|
|
}
|
|
|
|
|
+ return result.toString();
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|