Skip to content
Merged
Show file tree
Hide file tree
Changes from 59 commits
Commits
Show all changes
62 commits
Select commit Hold shift + click to select a range
97a5069
Refactor so isWrapped isn't public writeable.
PerBothner Mar 31, 2026
1c58f1f
Merge branch 'master' into setWrapped
PerBothner Apr 6, 2026
e9ea22f
Fix MockBuffer.setWrapped.
PerBothner Apr 6, 2026
8773207
Merge branch 'master' into setWrapped
PerBothner Apr 25, 2026
a88bd17
Bump axios from 1.13.2 to 1.15.0
dependabot[bot] Apr 12, 2026
1d562d1
Fix image layer resize jitter
SergioChan Mar 11, 2026
7e7dbc9
Update ImageRenderer.ts
Tyriar Mar 12, 2026
8315000
add missing separator for test arguments
jerch Apr 28, 2026
46fcebb
fix web-fonts tests
jerch Apr 28, 2026
918c5f0
newer safari promotes different loading state
jerch Apr 28, 2026
8653029
remove broken test condition
jerch Apr 28, 2026
0780cfc
fix safari
jerch Apr 28, 2026
cf799ac
fix safari
jerch Apr 28, 2026
10d2b1d
fix safari
jerch Apr 28, 2026
1a06b92
disable wrong webgl tests
jerch Apr 28, 2026
9c5e1d7
disable firefox webgl
jerch Apr 28, 2026
26d1ddd
Bump follow-redirects from 1.15.11 to 1.16.0
dependabot[bot] Apr 28, 2026
673db7c
Make agent instructions harness agnostic
Tyriar May 1, 2026
2998cf7
Add Orca to real-world uses
nwparker Apr 29, 2026
5c0434f
fix #5849
jerch Apr 28, 2026
fe7611a
adjust image addon defaults
jerch Apr 26, 2026
b54f918
multiple changes in APC handling:
jerch Apr 24, 2026
7ecb43d
make linter happy
jerch Apr 24, 2026
91f816c
add state and transition tests
jerch Apr 24, 2026
b4c9024
basic read ahead optimization
jerch Apr 24, 2026
bdea328
APC perf test
jerch Apr 24, 2026
05b8c14
better read ahead condition
jerch Apr 24, 2026
ffd194a
make linter happy
jerch Apr 24, 2026
4dc3aff
register APC fallback handler
jerch Apr 25, 2026
648779a
handler registration tests, fix mulitple immediates
jerch Apr 25, 2026
658ce2b
APC async tests
jerch Apr 25, 2026
9088d1e
fix ApcParser tests
jerch Apr 25, 2026
6b00ba6
fix vt feature listing
jerch Apr 26, 2026
d8f504f
fix vt feature listing
jerch Apr 26, 2026
bf35a21
fix vt feature listing
jerch Apr 26, 2026
d99c173
fix vt feature listing
jerch Apr 26, 2026
d7129e5
qoi support for IIP
jerch Apr 26, 2026
7205b06
add iip resize & qoi tests
jerch Apr 29, 2026
553d0dc
activate tests for webkit
jerch Apr 29, 2026
31e2fe7
IIP resizing tests for QOI
jerch Apr 29, 2026
552b7a0
merge resize tests for PNG & QOI into one block
jerch Apr 29, 2026
eb3c7e7
Add instructions around MutableDisposables and const enums
Tyriar May 2, 2026
97cb26a
Convert a lot of top level consts to const enums
Tyriar May 2, 2026
5107158
Cache translateToString calls inside BufferLine
Tyriar May 1, 2026
ca70702
Fix test/startCol handling
Tyriar May 1, 2026
227ea0c
Fix integration test defaults
Tyriar May 1, 2026
8757f76
Support caching of trimRight
Tyriar May 2, 2026
17e7ec5
Store a boolean on whether the cache is trimmed
Tyriar May 2, 2026
d92a1a9
Move cache into Buffer owned object, use WeakRefs
Tyriar May 2, 2026
1b9c9c4
Cache/buffer lifecycle
Tyriar May 2, 2026
424722a
Convert cache to Disposable
Tyriar May 2, 2026
6adfde4
Avoid any in test
Tyriar May 2, 2026
36a9f5d
Avoid thrashing timeouts
Tyriar May 2, 2026
d027be4
Fix build, lint
Tyriar May 2, 2026
34dd3ca
Ignore cache keys in serialize addon
Tyriar May 2, 2026
874825e
Fix lint
Tyriar May 2, 2026
fe302d0
Merge remote-tracking branch 'upstream/master' into setWrapped
PerBothner Oct 10, 2026
a11f91a
More merge fixes.
PerBothner Oct 10, 2026
0117604
More merge cleanup.
PerBothner Oct 10, 2026
5f2f03b
Remove BufferLine setWrapped method
PerBothner Oct 11, 2026
ef936ed
Remove cast to any for accessing internal _isWrapped property
PerBothner Oct 13, 2026
18dc877
Merge branch 'master' into setWrapped
PerBothner Oct 13, 2026
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
6 changes: 3 additions & 3 deletions src/browser/OscLinkProvider.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ describe('OscLinkProvider', () => {
setUrl(line1, 2, 'bbb', 1);
setUrl(line2, 0, 'cccc', 1);
setText(line2, 4, 'x');
line2!.isWrapped = true;
bufferService.buffer.setWrapped(1, true);

const links = await getLinks(provider, 2);
assert.lengthOf(links, 1);
Expand All @@ -78,7 +78,7 @@ describe('OscLinkProvider', () => {
setUrl(line1, 0, 'aaaaa', 1);
setUrl(line2, 0, 'bb', 1);
setText(line2, 2, 'ccc');
line2!.isWrapped = true;
bufferService.buffer.setWrapped(1, true);

const links = await getLinks(provider, 1);
assert.lengthOf(links, 1);
Expand All @@ -94,7 +94,7 @@ describe('OscLinkProvider', () => {
setUrl(line1, 0, 'aaaaa', 1);
setUrl(line2, 0, 'bbb', 2);
setText(line2, 3, 'cc');
line2!.isWrapped = true;
bufferService.buffer.setWrapped(1, true);

const links = await getLinks(provider, 1);
assert.lengthOf(links, 1);
Expand Down
3 changes: 3 additions & 0 deletions src/browser/TestUtils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,9 @@ export class MockBuffer implements IBuffer {
public clearAllMarkers(): void {
throw new Error('Method not implemented.');
}
public setWrapped(row: number, value: boolean): void {
throw new Error('Method not implemented.');
}
}

export class MockRenderer implements IRenderer {
Expand Down
12 changes: 6 additions & 6 deletions src/browser/services/SelectionService.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ describe('SelectionService', () => {
it('should expand upwards or downards for wrapped lines', () => {
buffer.lines.set(0, stringToRow(' foo'));
buffer.lines.set(1, stringToRow('bar '));
buffer.lines.get(1)!.isWrapped = true;
buffer.setWrapped(1, true);
selectionService.selectWordAt([1, 1]);
assert.equal(selectionService.selectionText, 'foobar');
selectionService.model.clearSelection();
Expand All @@ -210,10 +210,10 @@ describe('SelectionService', () => {
buffer.lines.set(2, stringToRow('bbbbbbbbbbbbbbbbbbbb'));
buffer.lines.set(3, stringToRow('cccccccccccccccccccc'));
buffer.lines.set(4, stringToRow('bar '));
buffer.lines.get(1)!.isWrapped = true;
buffer.lines.get(2)!.isWrapped = true;
buffer.lines.get(3)!.isWrapped = true;
buffer.lines.get(4)!.isWrapped = true;
buffer.setWrapped(1, true);
buffer.setWrapped(2, true);
buffer.setWrapped(3, true);
buffer.setWrapped(4, true);
selectionService.selectWordAt([18, 0]);
assert.equal(selectionService.selectionText, expectedText);
selectionService.model.clearSelection();
Expand Down Expand Up @@ -352,8 +352,8 @@ describe('SelectionService', () => {
it('should select the entire wrapped line', () => {
buffer.lines.set(0, stringToRow('foo'));
const line2 = stringToRow('bar');
line2.isWrapped = true;
buffer.lines.set(1, line2);
buffer.setWrapped(1, true);
selectionService.selectLineAt(0);
assert.equal(selectionService.selectionText, 'foobar', 'The selected text is correct');
assert.deepEqual(selectionService.model.selectionStart, [0, 0]);
Expand Down
42 changes: 20 additions & 22 deletions src/common/InputHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -592,7 +592,7 @@ export class InputHandler extends Disposable implements IInputHandler {
}
// The line already exists (eg. the initial viewport), mark it as a
// wrapped line
this._activeBuffer.lines.get(this._activeBuffer.ybase + this._activeBuffer.y)!.isWrapped = true;
this._activeBuffer.setWrapped(this._activeBuffer.ybase + this._activeBuffer.y, true);
}
// row changed, get it again
bufferRow = this._activeBuffer.lines.get(this._activeBuffer.ybase + this._activeBuffer.y);
Expand Down Expand Up @@ -756,7 +756,7 @@ export class InputHandler extends Disposable implements IInputHandler {
// reprint is common, especially on resize. Note that the windowsMode wrapped line heuristics
// can mess with this so windowsMode should be disabled, which is recommended on Windows build
// 21376 and above.
this._activeBuffer.lines.get(this._activeBuffer.ybase + this._activeBuffer.y)!.isWrapped = false;
this._activeBuffer.setWrapped(this._activeBuffer.ybase + this._activeBuffer.y, false);
}
// If the end of the line is hit, prevent this action from wrapping around to the next line.
if (this._activeBuffer.x >= this._bufferService.cols) {
Expand Down Expand Up @@ -820,7 +820,7 @@ export class InputHandler extends Disposable implements IInputHandler {
&& this._activeBuffer.y > this._activeBuffer.scrollTop
&& this._activeBuffer.y <= this._activeBuffer.scrollBottom
&& this._activeBuffer.lines.get(this._activeBuffer.ybase + this._activeBuffer.y)?.isWrapped) {
this._activeBuffer.lines.get(this._activeBuffer.ybase + this._activeBuffer.y)!.isWrapped = false;
this._activeBuffer.setWrapped(this._activeBuffer.ybase + this._activeBuffer.y, false);
this._activeBuffer.y--;
this._activeBuffer.x = this._bufferService.cols - 1;
// find last taken cell - last cell can have 3 different states:
Expand Down Expand Up @@ -1184,7 +1184,7 @@ export class InputHandler extends Disposable implements IInputHandler {
respectProtect
);
if (clearWrap) {
line.isWrapped = false;
this._activeBuffer.setWrapped(this._activeBuffer.ybase + y, false);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason for using absolute addressing here now, why not on the line?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure I understand the question. Buffer.setWrapped takes an absolute line number. We don't want to use BufferLine.setWrapped (now removed) because it is internal.

Note one reason for removing BufferLine.setWrapped (which is the same reason for making BufferLine.isWrapped read-only) is because some implementations (including the proposed LogicalLine implementation) need more state, and modifying isWrapped can't be done without access to at least the previous line.

}
}

Expand All @@ -1198,7 +1198,7 @@ export class InputHandler extends Disposable implements IInputHandler {
if (line) {
line.fill(this._activeBuffer.getNullCell(this._eraseAttrData()), respectProtect);
this._bufferService.buffer.clearMarkers(this._activeBuffer.ybase + y);
line.isWrapped = false;
this._activeBuffer.setWrapped(this._activeBuffer.ybase + y, false);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same question as above - why the absolute addressing now?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same reply.

}
}

Expand Down Expand Up @@ -1246,10 +1246,7 @@ export class InputHandler extends Disposable implements IInputHandler {
this._eraseInBufferLine(j, 0, this._activeBuffer.x + 1, true, respectProtect);
if (this._activeBuffer.x + 1 >= this._bufferService.cols) {
// Deleted entire previous line. This next line can no longer be wrapped.
const nextLine = this._activeBuffer.lines.get(j + 1);
if (nextLine) {
nextLine.isWrapped = false;
}
this._activeBuffer.setWrapped(j + 1, false);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These n+1 lookups for wrapped state are really annoying. I think I already addressed it in some comment elsewhere - we basically have a slightly off handling of wrapped state:
We set the flag on the next line to indicate, that it continues from the previous one. This introduces a lot of nasty edge cases, where we have to fix wrapped on n+1.
A more logical place would be to store it on the n-th line to indicate, that it continues into the next line. This plays much nicer with left-right and top-down progressing of the default cursor movement.

But that is a major rewrite and we are in a state, where our more complicated abstraction works "good enough" - haha.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FWIW, the LogicalLine re-write makes it trivial to implement a isWrapping (read-only) property, which is true when a line continues into the next line:

public get isWrapping(): boolean {
    return !!this.nextBufferLine;
}

}
while (j--) {
this._resetBufferLine(j, respectProtect);
Expand Down Expand Up @@ -1516,9 +1513,10 @@ export class InputHandler extends Disposable implements IInputHandler {
}
const param = params.params[0] || 1;
for (let y = this._activeBuffer.scrollTop; y <= this._activeBuffer.scrollBottom; ++y) {
const line = this._activeBuffer.lines.get(this._activeBuffer.ybase + y)!;
const row = this._activeBuffer.ybase + y;
const line = this._activeBuffer.lines.get(row)!;
line.deleteCells(0, param, this._activeBuffer.getNullCell(this._eraseAttrData()));
line.isWrapped = false;
this._activeBuffer.setWrapped(row, false);
}
this._dirtyRowTracker.markRangeDirty(this._activeBuffer.scrollTop, this._activeBuffer.scrollBottom);
return true;
Expand Down Expand Up @@ -1549,9 +1547,10 @@ export class InputHandler extends Disposable implements IInputHandler {
}
const param = params.params[0] || 1;
for (let y = this._activeBuffer.scrollTop; y <= this._activeBuffer.scrollBottom; ++y) {
const line = this._activeBuffer.lines.get(this._activeBuffer.ybase + y)!;
const row = this._activeBuffer.ybase + y;
const line = this._activeBuffer.lines.get(row)!;
this._activeBuffer.setWrapped(row, false);
line.insertCells(0, param, this._activeBuffer.getNullCell(this._eraseAttrData()));
line.isWrapped = false;
}
this._dirtyRowTracker.markRangeDirty(this._activeBuffer.scrollTop, this._activeBuffer.scrollBottom);
return true;
Expand All @@ -1572,9 +1571,10 @@ export class InputHandler extends Disposable implements IInputHandler {
}
const param = params.params[0] || 1;
for (let y = this._activeBuffer.scrollTop; y <= this._activeBuffer.scrollBottom; ++y) {
const line = this._activeBuffer.lines.get(this._activeBuffer.ybase + y)!;
const row = this._activeBuffer.ybase + y;
this._activeBuffer.setWrapped(row, false);
const line = this._activeBuffer.lines.get(row)!;
line.insertCells(this._activeBuffer.x, param, this._activeBuffer.getNullCell(this._eraseAttrData()));
line.isWrapped = false;
}
this._dirtyRowTracker.markRangeDirty(this._activeBuffer.scrollTop, this._activeBuffer.scrollBottom);
return true;
Expand All @@ -1595,9 +1595,10 @@ export class InputHandler extends Disposable implements IInputHandler {
}
const param = params.params[0] || 1;
for (let y = this._activeBuffer.scrollTop; y <= this._activeBuffer.scrollBottom; ++y) {
const line = this._activeBuffer.lines.get(this._activeBuffer.ybase + y)!;
const row = this._activeBuffer.ybase + y;
const line = this._activeBuffer.lines.get(row)!;
this._activeBuffer.setWrapped(row, false);
line.deleteCells(this._activeBuffer.x, param, this._activeBuffer.getNullCell(this._eraseAttrData()));
line.isWrapped = false;
}
this._dirtyRowTracker.markRangeDirty(this._activeBuffer.scrollTop, this._activeBuffer.scrollBottom);
return true;
Expand Down Expand Up @@ -3478,11 +3479,8 @@ export class InputHandler extends Disposable implements IInputHandler {
this._setCursor(0, 0);
for (let yOffset = 0; yOffset < this._bufferService.rows; ++yOffset) {
const row = this._activeBuffer.ybase + this._activeBuffer.y + yOffset;
const line = this._activeBuffer.lines.get(row);
if (line) {
line.fill(cell);
line.isWrapped = false;
}
this._activeBuffer.setWrapped(row, false);
this._activeBuffer.lines.get(row)?.fill(cell);
}
this._dirtyRowTracker.markAllDirty();
this._setCursor(0, 0);
Expand Down
6 changes: 2 additions & 4 deletions src/common/WindowsMode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,7 @@ export function updateWindowsModeWrappedState(bufferService: IBufferService): vo
// wrapped.
const line = bufferService.buffer.lines.get(bufferService.buffer.ybase + bufferService.buffer.y - 1);
const lastChar = line?.get(bufferService.cols - 1);

const nextLine = bufferService.buffer.lines.get(bufferService.buffer.ybase + bufferService.buffer.y);
if (nextLine && lastChar) {
nextLine.isWrapped = (lastChar[CHAR_DATA_CODE_INDEX] !== NULL_CELL_CODE && lastChar[CHAR_DATA_CODE_INDEX] !== WHITESPACE_CELL_CODE);
if (lastChar) {
bufferService.buffer.setWrapped(bufferService.buffer.ybase + bufferService.buffer.y, lastChar[CHAR_DATA_CODE_INDEX] !== NULL_CELL_CODE && lastChar[CHAR_DATA_CODE_INDEX] !== WHITESPACE_CELL_CODE);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks like a really hackish assumption over data state and def. will wrongly merge lines, that happened to end on the last cell and had a hard break following. (no critic on your code, I see that you just transformed it)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I haven't really thought about this, but I followed the existing logic.

}
}
34 changes: 17 additions & 17 deletions src/common/buffer/Buffer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,40 +70,40 @@ describe('Buffer', () => {
describe('wrapped', () => {
it('should return a range for the first row', () => {
buffer.fillViewportRows();
buffer.lines.get(1)!.isWrapped = true;
buffer.setWrapped(1, true);
assert.deepEqual(buffer.getWrappedRangeForLine(0), { first: 0, last: 1 });
});
it('should return a range for a middle row wrapping upwards', () => {
buffer.fillViewportRows();
buffer.lines.get(12)!.isWrapped = true;
buffer.setWrapped(12, true);
assert.deepEqual(buffer.getWrappedRangeForLine(12), { first: 11, last: 12 });
});
it('should return a range for a middle row wrapping downwards', () => {
buffer.fillViewportRows();
buffer.lines.get(13)!.isWrapped = true;
buffer.setWrapped(13, true);
assert.deepEqual(buffer.getWrappedRangeForLine(12), { first: 12, last: 13 });
});
it('should return a range for a middle row wrapping both ways', () => {
buffer.fillViewportRows();
buffer.lines.get(11)!.isWrapped = true;
buffer.lines.get(12)!.isWrapped = true;
buffer.lines.get(13)!.isWrapped = true;
buffer.lines.get(14)!.isWrapped = true;
buffer.setWrapped(11, true);
buffer.setWrapped(12, true);
buffer.setWrapped(13, true);
buffer.setWrapped(14, true);
assert.deepEqual(buffer.getWrappedRangeForLine(12), { first: 10, last: 14 });
});
it('should return a range for the last row', () => {
buffer.fillViewportRows();
buffer.lines.get(23)!.isWrapped = true;
buffer.setWrapped(23, true);
assert.deepEqual(buffer.getWrappedRangeForLine(buffer.lines.length - 1), { first: 22, last: 23 });
});
it('should return a range for a row that wraps upward to first row', () => {
buffer.fillViewportRows();
buffer.lines.get(1)!.isWrapped = true;
buffer.setWrapped(1, true);
assert.deepEqual(buffer.getWrappedRangeForLine(1), { first: 0, last: 1 });
});
it('should return a range for a row that wraps downward to last row', () => {
buffer.fillViewportRows();
buffer.lines.get(buffer.lines.length - 1)!.isWrapped = true;
buffer.setWrapped(buffer.lines.length - 1, true);
assert.deepEqual(buffer.getWrappedRangeForLine(buffer.lines.length - 2), { first: 22, last: 23 });
});
});
Expand Down Expand Up @@ -614,7 +614,7 @@ describe('Buffer', () => {
buffer.lines.get(0)!.set(1, [0, 'b', 1, 'b'.charCodeAt(0)]);
buffer.lines.get(1)!.set(0, [0, 'c', 1, 'c'.charCodeAt(0)]);
buffer.lines.get(1)!.set(1, [0, 'd', 1, 'd'.charCodeAt(0)]);
buffer.lines.get(1)!.isWrapped = true;
buffer.setWrapped(1, true);
// Buffer:
// "ab " (wrapped)
// "cd"
Expand Down Expand Up @@ -645,7 +645,7 @@ describe('Buffer', () => {
buffer.lines.get(0)!.set(i, [0, '', 0, 0]);
buffer.lines.get(1)!.set(i, [0, '', 0, 0]);
}
buffer.lines.get(1)!.isWrapped = true;
buffer.setWrapped(1, true);
// Buffer:
// 汉语汉语汉语 (wrapped)
// 汉语汉语汉语
Expand All @@ -672,7 +672,7 @@ describe('Buffer', () => {
buffer.lines.get(0)!.set(1, [0, 'b', 1, 'b'.charCodeAt(0)]);
buffer.lines.get(1)!.set(0, [0, 'c', 1, 'c'.charCodeAt(0)]);
buffer.lines.get(1)!.set(1, [0, 'd', 1, 'd'.charCodeAt(0)]);
buffer.lines.get(1)!.isWrapped = true;
buffer.setWrapped(1, true);
// Buffer:
// "ab " (wrapped)
// "cd"
Expand Down Expand Up @@ -706,7 +706,7 @@ describe('Buffer', () => {
buffer.lines.get(0)!.set(i, [0, '', 0, 0]);
buffer.lines.get(1)!.set(i, [0, '', 0, 0]);
}
buffer.lines.get(1)!.isWrapped = true;
buffer.setWrapped(1, true);
// Buffer:
// 汉语汉语汉语 (wrapped)
// 汉语汉语汉语
Expand Down Expand Up @@ -761,17 +761,17 @@ describe('Buffer', () => {
buffer.lines.get(0)!.set(1, [0, 'b', 1, 'b'.charCodeAt(0)]);
buffer.lines.get(1)!.set(0, [0, 'c', 1, 'c'.charCodeAt(0)]);
buffer.lines.get(1)!.set(1, [0, 'd', 1, 'd'.charCodeAt(0)]);
buffer.lines.get(1)!.isWrapped = true;
buffer.setWrapped(1, true);
buffer.lines.get(2)!.set(0, [0, 'e', 1, 'e'.charCodeAt(0)]);
buffer.lines.get(2)!.set(1, [0, 'f', 1, 'f'.charCodeAt(0)]);
buffer.lines.get(3)!.set(0, [0, 'g', 1, 'g'.charCodeAt(0)]);
buffer.lines.get(3)!.set(1, [0, 'h', 1, 'h'.charCodeAt(0)]);
buffer.lines.get(3)!.isWrapped = true;
buffer.setWrapped(3, true);
buffer.lines.get(4)!.set(0, [0, 'i', 1, 'i'.charCodeAt(0)]);
buffer.lines.get(4)!.set(1, [0, 'j', 1, 'j'.charCodeAt(0)]);
buffer.lines.get(5)!.set(0, [0, 'k', 1, 'k'.charCodeAt(0)]);
buffer.lines.get(5)!.set(1, [0, 'l', 1, 'l'.charCodeAt(0)]);
buffer.lines.get(5)!.isWrapped = true;
buffer.setWrapped(5, true);
});
describe('viewport not yet filled', () => {
it('should move the cursor up and add empty lines', () => {
Expand Down
5 changes: 5 additions & 0 deletions src/common/buffer/Buffer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,11 @@ export class Buffer extends Disposable implements IBuffer {
return correctBufferLength > MAX_BUFFER_SIZE ? MAX_BUFFER_SIZE : correctBufferLength;
}

public setWrapped(absrow: number, value: boolean): void {
const line = this.lines.get(absrow);
line instanceof BufferLine && line.setWrapped(value);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The && invocation is an interesting pattern here, I guess you did it for speed reasons. I also found && to be much faster on v8 compared to ordinary branching.

Minor perf suggestion - if the instanceof check is not needed (because .get() will always return either something nullish or a bufferline), then we should prolly just test for truthiness.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIRC I used the X && Y pattern because lint would complain about if (X) Y, though it did allow if (X) {Y}, which I though a bit inelegant. I actually think if (X) Y is slightly for readable - but if X && Y is faster we might as well keep it.

The instanceof check is because IBufferLine specifically does not provide setWrapped, which is supposed to used only to implement Buffer.setWrapped. I made a change to get rid of BufferLine.setWrapped completely - instead you have to do (line as any)._isWrapped. That should make it clear(er) you're mucking about with internals.

}

/**
* Fills the buffer's viewport with blank lines.
*/
Expand Down
18 changes: 15 additions & 3 deletions src/common/buffer/BufferLine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,17 @@ export class BufferLine implements IBufferLine {
/** Sparse cache; only read when `HAS_EXTENDED` is set in `_data`. */
protected _extendedAttrs: {[index: number]: IExtendedAttrs | undefined} = {};
public length: number;
private _isWrapped: boolean;

public get isWrapped(): boolean {
return this._isWrapped;
}
/**
* @internal
*/
public setWrapped(isWrapped: boolean): void {
this._isWrapped = isWrapped;
}

/** line text cache */
protected _cacheValid = false;
Expand All @@ -82,14 +93,15 @@ export class BufferLine implements IBufferLine {
constructor(
cols: number,
fillCellData?: ICellData,
public isWrapped: boolean = false
isWrapped: boolean = false
) {
this._data = new Uint32Array(cols * Constants.CELL_INDICIES);
const cell = fillCellData ?? CellData.fromCharData([0, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]);
for (let i = 0; i < cols; ++i) {
this.setCell(i, cell);
}
this.length = cols;
this._isWrapped = isWrapped;
}

/**
Expand Down Expand Up @@ -484,7 +496,7 @@ export class BufferLine implements IBufferLine {
}
this._cache = '';
this._cacheValid = false;
this.isWrapped = line.isWrapped;
this._isWrapped = line._isWrapped;
}

/** create a new clone */
Expand All @@ -497,7 +509,7 @@ export class BufferLine implements IBufferLine {
// thus we can skip handling them
newLine._copySparseMapsFrom(this);
}
newLine.isWrapped = this.isWrapped;
newLine._isWrapped = this._isWrapped;
return newLine;
}

Expand Down
3 changes: 1 addition & 2 deletions src/common/buffer/BufferReflow.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,9 +100,8 @@ describe('BufferReflow', () => {
function createWrappedLines(chars: string): CircularList<IBufferLine> {
const lines = new CircularList<IBufferLine>(chars.length);
for (let i = 0; i < chars.length; i++) {
const line = new BufferLine(1);
const line = new BufferLine(1, undefined, i > 0);
line.set(0, [0, chars[i], 1, chars.charCodeAt(i)]);
line.isWrapped = i > 0;
lines.push(line);
}
return lines;
Expand Down
Loading
Loading