Skip to content

Commit 3142460

Browse files
authored
Merge pull request #4335 from jerch/cleanup_buffer
Cleanup Buffer.ts
2 parents b97b1b6 + 49ec719 commit 3142460

File tree

4 files changed

+3
-354
lines changed

4 files changed

+3
-354
lines changed

src/browser/Terminal.test.ts

Lines changed: 0 additions & 235 deletions
Original file line numberDiff line numberDiff line change
@@ -1041,241 +1041,6 @@ describe('Terminal', () => {
10411041
});
10421042
});
10431043

1044-
describe('Buffer.stringIndexToBufferIndex', () => {
1045-
let terminal: TestTerminal;
1046-
1047-
beforeEach(() => {
1048-
terminal = new TestTerminal({ rows: 5, cols: 10, scrollback: 5 });
1049-
});
1050-
1051-
it('multiline ascii', async () => {
1052-
const input = 'This is ASCII text spanning multiple lines.';
1053-
await terminal.writeP(input);
1054-
const s = terminal.buffer.iterator(true).next().content;
1055-
assert.equal(input, s);
1056-
for (let i = 0; i < input.length; ++i) {
1057-
const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, i);
1058-
assert.deepEqual([(i / terminal.cols) | 0, i % terminal.cols], bufferIndex);
1059-
}
1060-
});
1061-
1062-
it('combining e\u0301 in a sentence', async () => {
1063-
const input = 'Sitting in the cafe\u0301 drinking coffee.';
1064-
await terminal.writeP(input);
1065-
const s = terminal.buffer.iterator(true).next().content;
1066-
assert.equal(input, s);
1067-
for (let i = 0; i < 19; ++i) {
1068-
const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, i);
1069-
assert.deepEqual([(i / terminal.cols) | 0, i % terminal.cols], bufferIndex);
1070-
}
1071-
// string index 18 & 19 point to combining char e\u0301 ---> same buffer Index
1072-
assert.deepEqual(
1073-
terminal.buffer.stringIndexToBufferIndex(0, 18),
1074-
terminal.buffer.stringIndexToBufferIndex(0, 19));
1075-
// after the combining char every string index has an offset of -1
1076-
for (let i = 19; i < input.length; ++i) {
1077-
const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, i);
1078-
assert.deepEqual([((i - 1) / terminal.cols) | 0, (i - 1) % terminal.cols], bufferIndex);
1079-
}
1080-
});
1081-
1082-
it('multiline combining e\u0301', async () => {
1083-
const input = 'e\u0301e\u0301e\u0301e\u0301e\u0301e\u0301e\u0301e\u0301e\u0301e\u0301e\u0301e\u0301e\u0301e\u0301e\u0301';
1084-
await terminal.writeP(input);
1085-
const s = terminal.buffer.iterator(true).next().content;
1086-
assert.equal(input, s);
1087-
// every buffer cell index contains 2 string indices
1088-
for (let i = 0; i < input.length; ++i) {
1089-
const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, i);
1090-
assert.deepEqual([((i >> 1) / terminal.cols) | 0, (i >> 1) % terminal.cols], bufferIndex);
1091-
}
1092-
});
1093-
1094-
it('surrogate char in a sentence', async () => {
1095-
const input = 'The 𝄞 is a clef widely used in modern notation.';
1096-
await terminal.writeP(input);
1097-
const s = terminal.buffer.iterator(true).next().content;
1098-
assert.equal(input, s);
1099-
for (let i = 0; i < 5; ++i) {
1100-
const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, i);
1101-
assert.deepEqual([(i / terminal.cols) | 0, i % terminal.cols], bufferIndex);
1102-
}
1103-
// string index 4 & 5 point to surrogate char 𝄞 ---> same buffer Index
1104-
assert.deepEqual(
1105-
terminal.buffer.stringIndexToBufferIndex(0, 4),
1106-
terminal.buffer.stringIndexToBufferIndex(0, 5));
1107-
// after the combining char every string index has an offset of -1
1108-
for (let i = 5; i < input.length; ++i) {
1109-
const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, i);
1110-
assert.deepEqual([((i - 1) / terminal.cols) | 0, (i - 1) % terminal.cols], bufferIndex);
1111-
}
1112-
});
1113-
1114-
it('multiline surrogate char', async () => {
1115-
const input = '𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞';
1116-
await terminal.writeP(input);
1117-
const s = terminal.buffer.iterator(true).next().content;
1118-
assert.equal(input, s);
1119-
// every buffer cell index contains 2 string indices
1120-
for (let i = 0; i < input.length; ++i) {
1121-
const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, i);
1122-
assert.deepEqual([((i >> 1) / terminal.cols) | 0, (i >> 1) % terminal.cols], bufferIndex);
1123-
}
1124-
});
1125-
1126-
it('surrogate char with combining', async () => {
1127-
// eye of Ra with acute accent - string length of 3
1128-
const input = '𓂀\u0301 - the eye hiroglyph with an acute accent.';
1129-
await terminal.writeP(input);
1130-
const s = terminal.buffer.iterator(true).next().content;
1131-
assert.equal(input, s);
1132-
// index 0..2 should map to 0
1133-
assert.deepEqual([0, 0], terminal.buffer.stringIndexToBufferIndex(0, 1));
1134-
assert.deepEqual([0, 0], terminal.buffer.stringIndexToBufferIndex(0, 2));
1135-
for (let i = 2; i < input.length; ++i) {
1136-
const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, i);
1137-
assert.deepEqual([((i - 2) / terminal.cols) | 0, (i - 2) % terminal.cols], bufferIndex);
1138-
}
1139-
});
1140-
1141-
it('multiline surrogate with combining', async () => {
1142-
const input = '𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301';
1143-
await terminal.writeP(input);
1144-
const s = terminal.buffer.iterator(true).next().content;
1145-
assert.equal(input, s);
1146-
// every buffer cell index contains 3 string indices
1147-
for (let i = 0; i < input.length; ++i) {
1148-
const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, i);
1149-
assert.deepEqual([(((i / 3) | 0) / terminal.cols) | 0, ((i / 3) | 0) % terminal.cols], bufferIndex);
1150-
}
1151-
});
1152-
1153-
it('fullwidth chars', async () => {
1154-
const input = 'These 123 are some fat numbers.';
1155-
await terminal.writeP(input);
1156-
const s = terminal.buffer.iterator(true).next().content;
1157-
assert.equal(input, s);
1158-
for (let i = 0; i < 6; ++i) {
1159-
const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, i);
1160-
assert.deepEqual([(i / terminal.cols) | 0, i % terminal.cols], bufferIndex);
1161-
}
1162-
// string index 6, 7, 8 take 2 cells
1163-
assert.deepEqual([0, 8], terminal.buffer.stringIndexToBufferIndex(0, 7));
1164-
assert.deepEqual([1, 0], terminal.buffer.stringIndexToBufferIndex(0, 8));
1165-
// rest of the string has offset of +3
1166-
for (let i = 9; i < input.length; ++i) {
1167-
const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, i);
1168-
assert.deepEqual([((i + 3) / terminal.cols) | 0, (i + 3) % terminal.cols], bufferIndex);
1169-
}
1170-
});
1171-
1172-
it('multiline fullwidth chars', async () => {
1173-
const input = '12345678901234567890';
1174-
await terminal.writeP(input);
1175-
const s = terminal.buffer.iterator(true).next().content;
1176-
assert.equal(input, s);
1177-
for (let i = 9; i < input.length; ++i) {
1178-
const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, i);
1179-
assert.deepEqual([((i << 1) / terminal.cols) | 0, (i << 1) % terminal.cols], bufferIndex);
1180-
}
1181-
});
1182-
1183-
it('fullwidth combining with emoji - match emoji cell', async () => {
1184-
const input = 'Lots of ¥\u0301 make me 😃.';
1185-
await terminal.writeP(input);
1186-
const s = terminal.buffer.iterator(true).next().content;
1187-
assert.equal(input, s);
1188-
const stringIndex = s.match(/😃/)!.index!;
1189-
const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, stringIndex);
1190-
assert(terminal.buffer.lines.get(bufferIndex[0])!.loadCell(bufferIndex[1], new CellData()).getChars(), '😃');
1191-
});
1192-
1193-
it('multiline fullwidth chars with offset 1 (currently tests for broken behavior)', async () => {
1194-
const input = 'a12345678901234567890';
1195-
// the 'a' at the beginning moves all fullwidth chars one to the right
1196-
// now the end of the line contains a dangling empty cell since
1197-
// the next fullwidth char has to wrap early
1198-
// the dangling last cell is wrongly added in the string
1199-
// --> fixable after resolving #1685
1200-
await terminal.writeP(input);
1201-
const s = terminal.buffer.iterator(true).next().content;
1202-
assert.equal(input, s);
1203-
for (let i = 10; i < input.length; ++i) {
1204-
const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, i, true);
1205-
const j = (i - 0) << 1;
1206-
assert.deepEqual([(j / terminal.cols) | 0, j % terminal.cols], bufferIndex);
1207-
}
1208-
});
1209-
1210-
it('test fully wrapped buffer up to last char', async () => {
1211-
const input = Array(6).join('1234567890');
1212-
await terminal.writeP(input);
1213-
const s = terminal.buffer.iterator(true).next().content;
1214-
assert.equal(input, s);
1215-
for (let i = 0; i < input.length; ++i) {
1216-
const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, i, true);
1217-
assert.equal(input[i], terminal.buffer.lines.get(bufferIndex[0])!.loadCell(bufferIndex[1], new CellData()).getChars());
1218-
}
1219-
});
1220-
1221-
it('test fully wrapped buffer up to last char with full width odd', async () => {
1222-
const input = 'a¥\u0301a¥\u0301a¥\u0301a¥\u0301a¥\u0301a¥\u0301a¥\u0301a¥\u0301'
1223-
+ 'a¥\u0301a¥\u0301a¥\u0301a¥\u0301a¥\u0301a¥\u0301a¥\u0301';
1224-
await terminal.writeP(input);
1225-
const s = terminal.buffer.iterator(true).next().content;
1226-
assert.equal(input, s);
1227-
for (let i = 0; i < input.length; ++i) {
1228-
const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, i, true);
1229-
assert.equal(
1230-
(!(i % 3))
1231-
? input[i]
1232-
: (i % 3 === 1)
1233-
? input.slice(i, i + 2)
1234-
: input.slice(i - 1, i + 1),
1235-
terminal.buffer.lines.get(bufferIndex[0])!.loadCell(bufferIndex[1], new CellData()).getChars());
1236-
}
1237-
});
1238-
1239-
it('should handle \t in lines correctly', async () => {
1240-
const input = '\thttps://google.de';
1241-
await terminal.writeP(input);
1242-
const s = terminal.buffer.iterator(true).next().content;
1243-
assert.equal(s, Array(terminal.optionsService.options.tabStopWidth + 1).join(' ') + 'https://google.de');
1244-
});
1245-
});
1246-
1247-
describe('BufferStringIterator', function (): void {
1248-
it('iterator does not overflow buffer limits', async () => {
1249-
const terminal = new TestTerminal({ rows: 5, cols: 10, scrollback: 5 });
1250-
const data = [
1251-
'aaaaaaaaaa',
1252-
'aaaaaaaaa\n',
1253-
'aaaaaaaaaa',
1254-
'aaaaaaaaa\n',
1255-
'aaaaaaaaaa',
1256-
'aaaaaaaaaa',
1257-
'aaaaaaaaaa',
1258-
'aaaaaaaaa\n',
1259-
'aaaaaaaaaa',
1260-
'aaaaaaaaaa'
1261-
];
1262-
await terminal.writeP(data.join(''));
1263-
// brute force test with insane values
1264-
assert.doesNotThrow(() => {
1265-
for (let overscan = 0; overscan < 20; ++overscan) {
1266-
for (let start = -10; start < 20; ++start) {
1267-
for (let end = -10; end < 20; ++end) {
1268-
const it = terminal.buffer.iterator(false, start, end, overscan, overscan);
1269-
while (it.hasNext()) {
1270-
it.next();
1271-
}
1272-
}
1273-
}
1274-
}
1275-
});
1276-
});
1277-
});
1278-
12791044
describe('Windows Mode', () => {
12801045
it('should mark lines as wrapped when the line ends in a non-null character after a LF', async () => {
12811046
const data = [

src/browser/TestUtils.test.ts

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { IEvent, EventEmitter } from 'common/EventEmitter';
88
import { ICharacterJoinerService, ICharSizeService, ICoreBrowserService, IMouseService, IRenderService, ISelectionService, IThemeService } from 'browser/services/Services';
99
import { IRenderDimensions, IRenderer, IRequestRedrawEvent } from 'browser/renderer/shared/Types';
1010
import { IColorSet, ITerminal, ILinkifier2, IBrowser, IViewport, ICompositionHelper, CharacterJoinerHandler, IBufferRange, ReadonlyColorSet } from 'browser/Types';
11-
import { IBuffer, IBufferStringIterator, IBufferSet } from 'common/buffer/Types';
11+
import { IBuffer, IBufferSet } from 'common/buffer/Types';
1212
import { IBufferLine, ICellData, IAttributeData, ICircularList, XtermListener, ICharset, ITerminalOptions, ColorIndex } from 'common/Types';
1313
import { Buffer } from 'common/buffer/Buffer';
1414
import * as Browser from 'common/Platform';
@@ -240,12 +240,6 @@ export class MockBuffer implements IBuffer {
240240
public getBlankLine(attr: IAttributeData, isWrapped?: boolean): IBufferLine {
241241
return Buffer.prototype.getBlankLine.apply(this, arguments as any);
242242
}
243-
public stringIndexToBufferIndex(lineIndex: number, stringIndex: number): number[] {
244-
return Buffer.prototype.stringIndexToBufferIndex.apply(this, arguments as any);
245-
}
246-
public iterator(trimRight: boolean, startIndex?: number, endIndex?: number): IBufferStringIterator {
247-
return Buffer.prototype.iterator.apply(this, arguments as any);
248-
}
249243
public getNullCell(attr?: IAttributeData): ICellData {
250244
throw new Error('Method not implemented.');
251245
}

src/common/buffer/Buffer.ts

Lines changed: 2 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
*/
55

66
import { CircularList, IInsertEvent } from 'common/CircularList';
7-
import { IBuffer, BufferIndex, IBufferStringIterator, IBufferStringIteratorResult } from 'common/buffer/Types';
7+
import { IBuffer } from 'common/buffer/Types';
88
import { IBufferLine, ICellData, IAttributeData, ICharset } from 'common/Types';
99
import { BufferLine, DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine';
1010
import { CellData } from 'common/buffer/CellData';
@@ -14,7 +14,7 @@ import { Marker } from 'common/buffer/Marker';
1414
import { IOptionsService, IBufferService } from 'common/services/Services';
1515
import { DEFAULT_CHARSET } from 'common/data/Charsets';
1616
import { ExtendedAttrs } from 'common/buffer/AttributeData';
17-
import { DebouncedIdleTask, IdleTaskQueue } from 'common/TaskQueue';
17+
import { IdleTaskQueue } from 'common/TaskQueue';
1818

1919
export const MAX_BUFFER_SIZE = 4294967295; // 2^32 - 1
2020

@@ -510,43 +510,6 @@ export class Buffer implements IBuffer {
510510
}
511511
}
512512

513-
// private _reflowSmallerGetLinesNeeded()
514-
515-
/**
516-
* Translates a string index back to a BufferIndex.
517-
* To get the correct buffer position the string must start at `startCol` 0
518-
* (default in translateBufferLineToString).
519-
* The method also works on wrapped line strings given rows were not trimmed.
520-
* The method operates on the CharData string length, there are no
521-
* additional content or boundary checks. Therefore the string and the buffer
522-
* should not be altered in between.
523-
* TODO: respect trim flag after fixing #1685
524-
* @param lineIndex line index the string was retrieved from
525-
* @param stringIndex index within the string
526-
* @param trimRight Whether to trim whitespace to the right.
527-
*/
528-
public stringIndexToBufferIndex(lineIndex: number, stringIndex: number, trimRight: boolean = false): BufferIndex {
529-
while (stringIndex) {
530-
const line = this.lines.get(lineIndex);
531-
if (!line) {
532-
return [-1, -1];
533-
}
534-
const length = (trimRight) ? line.getTrimmedLength() : line.length;
535-
for (let i = 0; i < length; ++i) {
536-
if (line.get(i)[CHAR_DATA_WIDTH_INDEX]) {
537-
// empty cells report a string length of 0, but get replaced
538-
// with a whitespace in translateToString, thus replace with 1
539-
stringIndex -= line.get(i)[CHAR_DATA_CHAR_INDEX].length || 1;
540-
}
541-
if (stringIndex < 0) {
542-
return [lineIndex, i];
543-
}
544-
}
545-
lineIndex++;
546-
}
547-
return [lineIndex, 0];
548-
}
549-
550513
/**
551514
* Translates a buffer line to a string, with optional start and end columns.
552515
* Wide characters will count as two columns in the resulting string. This
@@ -684,65 +647,4 @@ export class Buffer implements IBuffer {
684647
this.markers.splice(this.markers.indexOf(marker), 1);
685648
}
686649
}
687-
688-
public iterator(trimRight: boolean, startIndex?: number, endIndex?: number, startOverscan?: number, endOverscan?: number): IBufferStringIterator {
689-
return new BufferStringIterator(this, trimRight, startIndex, endIndex, startOverscan, endOverscan);
690-
}
691-
}
692-
693-
/**
694-
* Iterator to get unwrapped content strings from the buffer.
695-
* The iterator returns at least the string data between the borders
696-
* `startIndex` and `endIndex` (exclusive) and will expand the lines
697-
* by `startOverscan` to the top and by `endOverscan` to the bottom,
698-
* if no new line was found in between.
699-
* It will never read/return string data beyond `startIndex - startOverscan`
700-
* or `endIndex + endOverscan`. Therefore the first and last line might be truncated.
701-
* It is possible to always get the full string for the first and last line as well
702-
* by setting the overscan values to the actual buffer length. This not recommended
703-
* since it might return the whole buffer within a single string in a worst case scenario.
704-
*/
705-
export class BufferStringIterator implements IBufferStringIterator {
706-
private _current: number;
707-
708-
constructor(
709-
private _buffer: IBuffer,
710-
private _trimRight: boolean,
711-
private _startIndex: number = 0,
712-
private _endIndex: number = _buffer.lines.length,
713-
private _startOverscan: number = 0,
714-
private _endOverscan: number = 0
715-
) {
716-
if (this._startIndex < 0) {
717-
this._startIndex = 0;
718-
}
719-
if (this._endIndex > this._buffer.lines.length) {
720-
this._endIndex = this._buffer.lines.length;
721-
}
722-
this._current = this._startIndex;
723-
}
724-
725-
public hasNext(): boolean {
726-
return this._current < this._endIndex;
727-
}
728-
729-
public next(): IBufferStringIteratorResult {
730-
const range = this._buffer.getWrappedRangeForLine(this._current);
731-
// limit search window to overscan value at both borders
732-
if (range.first < this._startIndex - this._startOverscan) {
733-
range.first = this._startIndex - this._startOverscan;
734-
}
735-
if (range.last > this._endIndex + this._endOverscan) {
736-
range.last = this._endIndex + this._endOverscan;
737-
}
738-
// limit to current buffer length
739-
range.first = Math.max(range.first, 0);
740-
range.last = Math.min(range.last, this._buffer.lines.length);
741-
let content = '';
742-
for (let i = range.first; i <= range.last; ++i) {
743-
content += this._buffer.translateBufferLineToString(i, this._trimRight);
744-
}
745-
this._current = range.last + 1;
746-
return { range, content };
747-
}
748650
}

0 commit comments

Comments
 (0)