Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dec5452fc0 | ||
|
|
81b3b1ec46 | ||
|
|
f57b8e1534 | ||
|
|
c8f11d63b5 | ||
|
|
8ab575621d | ||
|
|
3b08b7dcef | ||
|
|
3d929a1db0 | ||
|
|
2fe3fd13a1 | ||
|
|
f33f502267 | ||
|
|
7c24c58007 | ||
|
|
596634ac41 | ||
|
|
0c8271db49 | ||
|
|
b44d036524 | ||
|
|
9872be0c29 | ||
|
|
f3f4dcaf9f | ||
|
|
64718444e2 | ||
|
|
ea75b99e2b |
+19
-27
@@ -11,9 +11,9 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
timeout-minutes: 10
|
timeout-minutes: 10
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v5
|
||||||
- name: Use Node.js 22.x
|
- name: Use Node.js 22.x
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v5
|
||||||
with:
|
with:
|
||||||
node-version: 22.x
|
node-version: 22.x
|
||||||
cache: 'npm'
|
cache: 'npm'
|
||||||
@@ -69,7 +69,7 @@ jobs:
|
|||||||
./addons/addon-webgl/out/* \
|
./addons/addon-webgl/out/* \
|
||||||
./addons/addon-webgl/out-*st/*
|
./addons/addon-webgl/out-*st/*
|
||||||
- name: Upload artifacts
|
- name: Upload artifacts
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v6
|
||||||
with:
|
with:
|
||||||
name: build-artifacts
|
name: build-artifacts
|
||||||
path: compressed-build.zip
|
path: compressed-build.zip
|
||||||
@@ -79,9 +79,9 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
timeout-minutes: 10
|
timeout-minutes: 10
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v5
|
||||||
- name: Use Node.js 22.x
|
- name: Use Node.js 22.x
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v5
|
||||||
with:
|
with:
|
||||||
node-version: 22.x
|
node-version: 22.x
|
||||||
cache: 'npm'
|
cache: 'npm'
|
||||||
@@ -100,16 +100,16 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
timeout-minutes: 10
|
timeout-minutes: 10
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v5
|
||||||
- name: Use Node.js 22.x
|
- name: Use Node.js 22.x
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v5
|
||||||
with:
|
with:
|
||||||
node-version: 22.x
|
node-version: 22.x
|
||||||
cache: 'npm'
|
cache: 'npm'
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: |
|
run: |
|
||||||
npm ci
|
npm ci
|
||||||
- uses: actions/download-artifact@v4
|
- uses: actions/download-artifact@v7
|
||||||
with:
|
with:
|
||||||
name: build-artifacts
|
name: build-artifacts
|
||||||
- name: Unzip artifacts
|
- name: Unzip artifacts
|
||||||
@@ -129,6 +129,7 @@ jobs:
|
|||||||
exit $EXIT_CODE
|
exit $EXIT_CODE
|
||||||
|
|
||||||
test-unit:
|
test-unit:
|
||||||
|
needs: build
|
||||||
timeout-minutes: 20
|
timeout-minutes: 20
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
@@ -136,21 +137,16 @@ jobs:
|
|||||||
runs-on: [ubuntu, macos, windows]
|
runs-on: [ubuntu, macos, windows]
|
||||||
runs-on: ${{ matrix.runs-on }}-latest
|
runs-on: ${{ matrix.runs-on }}-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v5
|
||||||
- name: Use Node.js ${{ matrix.node-version }}.x
|
- name: Use Node.js ${{ matrix.node-version }}.x
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v5
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node-version }}.x
|
node-version: ${{ matrix.node-version }}.x
|
||||||
cache: 'npm'
|
cache: 'npm'
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: |
|
run: |
|
||||||
npm ci
|
npm ci
|
||||||
- name: Wait for build job
|
- uses: actions/download-artifact@v7
|
||||||
uses: NathanFirmo/wait-for-other-job@v1.1.1
|
|
||||||
with:
|
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
job: build
|
|
||||||
- uses: actions/download-artifact@v4
|
|
||||||
with:
|
with:
|
||||||
name: build-artifacts
|
name: build-artifacts
|
||||||
- name: Unzip artifacts
|
- name: Unzip artifacts
|
||||||
@@ -166,6 +162,7 @@ jobs:
|
|||||||
run: npm run test-unit --forbid-only
|
run: npm run test-unit --forbid-only
|
||||||
|
|
||||||
test-integration:
|
test-integration:
|
||||||
|
needs: build
|
||||||
timeout-minutes: 20
|
timeout-minutes: 20
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
@@ -174,9 +171,9 @@ jobs:
|
|||||||
browser: [chromium, firefox, webkit]
|
browser: [chromium, firefox, webkit]
|
||||||
runs-on: ${{ matrix.runs-on }}
|
runs-on: ${{ matrix.runs-on }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v5
|
||||||
- name: Use Node.js ${{ matrix.node-version }}.x
|
- name: Use Node.js ${{ matrix.node-version }}.x
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v5
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node-version }}.x
|
node-version: ${{ matrix.node-version }}.x
|
||||||
cache: 'npm'
|
cache: 'npm'
|
||||||
@@ -185,12 +182,7 @@ jobs:
|
|||||||
npm ci
|
npm ci
|
||||||
- name: Install playwright
|
- name: Install playwright
|
||||||
run: npx playwright install --with-deps ${{ matrix.browser }}
|
run: npx playwright install --with-deps ${{ matrix.browser }}
|
||||||
- name: Wait for build job
|
- uses: actions/download-artifact@v7
|
||||||
uses: NathanFirmo/wait-for-other-job@v1.1.1
|
|
||||||
with:
|
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
job: build
|
|
||||||
- uses: actions/download-artifact@v4
|
|
||||||
with:
|
with:
|
||||||
name: build-artifacts
|
name: build-artifacts
|
||||||
- name: Unzip artifacts
|
- name: Unzip artifacts
|
||||||
@@ -240,9 +232,9 @@ jobs:
|
|||||||
matrix:
|
matrix:
|
||||||
node-version: [22]
|
node-version: [22]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v5
|
||||||
- name: Use Node.js ${{ matrix.node-version }}.x
|
- name: Use Node.js ${{ matrix.node-version }}.x
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v5
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node-version }}.x
|
node-version: ${{ matrix.node-version }}.x
|
||||||
cache: 'npm'
|
cache: 'npm'
|
||||||
@@ -251,7 +243,7 @@ jobs:
|
|||||||
npm ci
|
npm ci
|
||||||
- name: Install playwright
|
- name: Install playwright
|
||||||
run: npx playwright install
|
run: npx playwright install
|
||||||
- uses: actions/download-artifact@v4
|
- uses: actions/download-artifact@v7
|
||||||
with:
|
with:
|
||||||
name: build-artifacts
|
name: build-artifacts
|
||||||
- name: Unzip artifacts
|
- name: Unzip artifacts
|
||||||
|
|||||||
@@ -24,12 +24,12 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v5
|
||||||
- name: Initialize CodeQL
|
- name: Initialize CodeQL
|
||||||
uses: github/codeql-action/init@v2
|
uses: github/codeql-action/init@v4
|
||||||
with:
|
with:
|
||||||
languages: ${{ matrix.language }}
|
languages: ${{ matrix.language }}
|
||||||
- name: Autobuild
|
- name: Autobuild
|
||||||
uses: github/codeql-action/autobuild@v2
|
uses: github/codeql-action/autobuild@v4
|
||||||
- name: Perform CodeQL Analysis
|
- name: Perform CodeQL Analysis
|
||||||
uses: github/codeql-action/analyze@v2
|
uses: github/codeql-action/analyze@v4
|
||||||
|
|||||||
@@ -18,9 +18,9 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v5
|
||||||
- name: Use Node.js 22.x
|
- name: Use Node.js 22.x
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v5
|
||||||
with:
|
with:
|
||||||
node-version: 22.x
|
node-version: 22.x
|
||||||
cache: 'npm'
|
cache: 'npm'
|
||||||
|
|||||||
@@ -10,9 +10,9 @@ jobs:
|
|||||||
release:
|
release:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v5
|
||||||
- name: Use Node.js 22.x
|
- name: Use Node.js 22.x
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v5
|
||||||
with:
|
with:
|
||||||
node-version: 22.x
|
node-version: 22.x
|
||||||
cache: 'npm'
|
cache: 'npm'
|
||||||
|
|||||||
@@ -78,13 +78,43 @@ export class BrowserClipboardProvider implements IClipboardProvider {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO: Once the base64 handling on Uint8Arrays is more common,
|
||||||
|
* remove the btoa/atob fallbacks below.
|
||||||
|
*/
|
||||||
|
interface IUint8ArrayB64 extends Uint8Array {
|
||||||
|
toBase64(): string;
|
||||||
|
}
|
||||||
|
interface IUint8ArrayB64Ctor extends Uint8ArrayConstructor {
|
||||||
|
fromBase64(s: string): IUint8ArrayB64;
|
||||||
|
}
|
||||||
|
|
||||||
export class Base64 implements IBase64 {
|
export class Base64 implements IBase64 {
|
||||||
public encodeText(data: string): string {
|
public encodeText(data: string): string {
|
||||||
return btoa(data);
|
const bytes = new TextEncoder().encode(data) as IUint8ArrayB64;
|
||||||
|
if (bytes.toBase64 !== undefined) {
|
||||||
|
return bytes.toBase64();
|
||||||
|
}
|
||||||
|
let bin = '';
|
||||||
|
for (let i = 0; i < bytes.length; i++) {
|
||||||
|
bin += String.fromCharCode(bytes[i]);
|
||||||
|
}
|
||||||
|
return btoa(bin);
|
||||||
}
|
}
|
||||||
public decodeText(data: string): string {
|
public decodeText(data: string): string {
|
||||||
|
if ((Uint8Array as IUint8ArrayB64Ctor).fromBase64 !== undefined) {
|
||||||
|
try {
|
||||||
|
return new TextDecoder().decode((Uint8Array as IUint8ArrayB64Ctor).fromBase64(data));
|
||||||
|
} catch {}
|
||||||
|
return '';
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
return atob(data);
|
const bin = atob(data);
|
||||||
|
const bytes = new Uint8Array(bin.length);
|
||||||
|
for (let i = 0; i < bin.length; ++i) {
|
||||||
|
bytes[i] = bin.charCodeAt(i);
|
||||||
|
}
|
||||||
|
return new TextDecoder().decode(bytes);
|
||||||
} catch {}
|
} catch {}
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ test.describe('ClipboardAddon', () => {
|
|||||||
await ctx.page.context().grantPermissions(['clipboard-read', 'clipboard-write']);
|
await ctx.page.context().grantPermissions(['clipboard-read', 'clipboard-write']);
|
||||||
}
|
}
|
||||||
await ctx.page.evaluate(`
|
await ctx.page.evaluate(`
|
||||||
window.term.reset()
|
window.term.reset();
|
||||||
window.clipboard?.dispose();
|
window.clipboard?.dispose();
|
||||||
window.clipboard = new ClipboardAddon();
|
window.clipboard = new ClipboardAddon();
|
||||||
window.term.loadAddon(window.clipboard);
|
window.term.loadAddon(window.clipboard);
|
||||||
@@ -92,4 +92,22 @@ test.describe('ClipboardAddon', () => {
|
|||||||
deepEqual(await ctx.page.evaluate(() => window.navigator.clipboard.readText()), '');
|
deepEqual(await ctx.page.evaluate(() => window.navigator.clipboard.readText()), '');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test.describe('non-ASCII', () => {
|
||||||
|
const testDataEncoded = '4oKsbWzDpMO8dMOf';
|
||||||
|
const testDataDecoded = '€mläütß';
|
||||||
|
test('write simple string', async () => {
|
||||||
|
await ctx.proxy.write(`\x1b]52;c;${testDataEncoded}\x07`);
|
||||||
|
deepEqual(await ctx.page.evaluate(() => window.navigator.clipboard.readText()), testDataDecoded);
|
||||||
|
});
|
||||||
|
test('read simple string', async () => {
|
||||||
|
await ctx.page.evaluate(`
|
||||||
|
window.data2 = [];
|
||||||
|
window.term.onData(e => data2.push(e));
|
||||||
|
`);
|
||||||
|
await ctx.page.evaluate((d) => window.navigator.clipboard.writeText(d), testDataDecoded);
|
||||||
|
await ctx.proxy.write(`\x1b]52;c;?\x07`);
|
||||||
|
deepEqual(await ctx.page.evaluate('window.data2'), [`\x1b]52;c;${testDataEncoded}\x07`]);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -436,9 +436,9 @@ export class ImageStorage implements IDisposable {
|
|||||||
for (let col = 0; col < cols; ++col) {
|
for (let col = 0; col < cols; ++col) {
|
||||||
let e: IExtendedAttrsImage;
|
let e: IExtendedAttrsImage;
|
||||||
if (line.getBg(col) & BgFlags.HAS_EXTENDED) {
|
if (line.getBg(col) & BgFlags.HAS_EXTENDED) {
|
||||||
e = line._extendedAttrs[col] ?? EMPTY_ATTRS;
|
e = line._extendedAttrs?.[col] ?? EMPTY_ATTRS;
|
||||||
} else {
|
} else {
|
||||||
const maybeImg = line._extendedAttrs[col] as IExtendedAttrsImage | undefined;
|
const maybeImg = line._extendedAttrs?.[col] as IExtendedAttrsImage | undefined;
|
||||||
if (!maybeImg || maybeImg.imageId === undefined || maybeImg.imageId === -1) {
|
if (!maybeImg || maybeImg.imageId === undefined || maybeImg.imageId === -1) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -461,7 +461,7 @@ export class ImageStorage implements IDisposable {
|
|||||||
* Also check _extendedAttrs directly for cells where text cleared HAS_EXTENDED.
|
* Also check _extendedAttrs directly for cells where text cleared HAS_EXTENDED.
|
||||||
*/
|
*/
|
||||||
while (++col < cols) {
|
while (++col < cols) {
|
||||||
const nextE = line._extendedAttrs[col] as IExtendedAttrsImage | undefined;
|
const nextE = line._extendedAttrs?.[col] as IExtendedAttrsImage | undefined;
|
||||||
if (!nextE || nextE.imageId !== imageId || nextE.tileId !== startTile + count) {
|
if (!nextE || nextE.imageId !== imageId || nextE.tileId !== startTile + count) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -515,7 +515,7 @@ export class ImageStorage implements IDisposable {
|
|||||||
for (let row = 0; row < rows; ++row) {
|
for (let row = 0; row < rows; ++row) {
|
||||||
const line = buffer.lines.get(row) as IBufferLineExt;
|
const line = buffer.lines.get(row) as IBufferLineExt;
|
||||||
if (line.getBg(oldCol) & BgFlags.HAS_EXTENDED) {
|
if (line.getBg(oldCol) & BgFlags.HAS_EXTENDED) {
|
||||||
const e: IExtendedAttrsImage = line._extendedAttrs[oldCol] ?? EMPTY_ATTRS;
|
const e: IExtendedAttrsImage = line._extendedAttrs?.[oldCol] ?? EMPTY_ATTRS;
|
||||||
const imageId = e.imageId;
|
const imageId = e.imageId;
|
||||||
if (imageId === undefined || imageId === -1) {
|
if (imageId === undefined || imageId === -1) {
|
||||||
continue;
|
continue;
|
||||||
@@ -560,7 +560,7 @@ export class ImageStorage implements IDisposable {
|
|||||||
const buffer = this._terminal._core.buffer;
|
const buffer = this._terminal._core.buffer;
|
||||||
const line = buffer.lines.get(y) as IBufferLineExt;
|
const line = buffer.lines.get(y) as IBufferLineExt;
|
||||||
if (line && line.getBg(x) & BgFlags.HAS_EXTENDED) {
|
if (line && line.getBg(x) & BgFlags.HAS_EXTENDED) {
|
||||||
const e: IExtendedAttrsImage = line._extendedAttrs[x] ?? EMPTY_ATTRS;
|
const e: IExtendedAttrsImage = line._extendedAttrs?.[x] ?? EMPTY_ATTRS;
|
||||||
if (e.imageId && e.imageId !== -1) {
|
if (e.imageId && e.imageId !== -1) {
|
||||||
const orig = this._images.get(e.imageId)?.orig;
|
const orig = this._images.get(e.imageId)?.orig;
|
||||||
if (window.ImageBitmap && orig instanceof ImageBitmap) {
|
if (window.ImageBitmap && orig instanceof ImageBitmap) {
|
||||||
@@ -580,7 +580,7 @@ export class ImageStorage implements IDisposable {
|
|||||||
const buffer = this._terminal._core.buffer;
|
const buffer = this._terminal._core.buffer;
|
||||||
const line = buffer.lines.get(y) as IBufferLineExt;
|
const line = buffer.lines.get(y) as IBufferLineExt;
|
||||||
if (line && line.getBg(x) & BgFlags.HAS_EXTENDED) {
|
if (line && line.getBg(x) & BgFlags.HAS_EXTENDED) {
|
||||||
const e: IExtendedAttrsImage = line._extendedAttrs[x] ?? EMPTY_ATTRS;
|
const e: IExtendedAttrsImage = line._extendedAttrs?.[x] ?? EMPTY_ATTRS;
|
||||||
if (e.imageId && e.imageId !== -1 && e.tileId !== -1) {
|
if (e.imageId && e.imageId !== -1 && e.tileId !== -1) {
|
||||||
const spec = this._images.get(e.imageId);
|
const spec = this._images.get(e.imageId);
|
||||||
if (spec) {
|
if (spec) {
|
||||||
@@ -611,7 +611,7 @@ export class ImageStorage implements IDisposable {
|
|||||||
|
|
||||||
private _writeToCell(line: IBufferLineExt, x: number, imageId: number, tileId: number): void {
|
private _writeToCell(line: IBufferLineExt, x: number, imageId: number, tileId: number): void {
|
||||||
if (line._data[x * Cell.SIZE + Cell.BG] & BgFlags.HAS_EXTENDED) {
|
if (line._data[x * Cell.SIZE + Cell.BG] & BgFlags.HAS_EXTENDED) {
|
||||||
const old = line._extendedAttrs[x];
|
const old = line._extendedAttrs?.[x];
|
||||||
if (old) {
|
if (old) {
|
||||||
if (old.imageId !== undefined) {
|
if (old.imageId !== undefined) {
|
||||||
// found an old ExtendedAttrsImage, since we know that
|
// found an old ExtendedAttrsImage, since we know that
|
||||||
@@ -627,13 +627,13 @@ export class ImageStorage implements IDisposable {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// found a plain ExtendedAttrs instance, clone it to new entry
|
// found a plain ExtendedAttrs instance, clone it to new entry
|
||||||
line._extendedAttrs[x] = new ExtendedAttrsImage(old.ext, old.urlId, imageId, tileId);
|
(line._extendedAttrs ??= {})[x] = new ExtendedAttrsImage(old.ext, old.urlId, imageId, tileId);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// fall-through: always create new ExtendedAttrsImage entry
|
// fall-through: always create new ExtendedAttrsImage entry
|
||||||
line._data[x * Cell.SIZE + Cell.BG] |= BgFlags.HAS_EXTENDED;
|
line._data[x * Cell.SIZE + Cell.BG] |= BgFlags.HAS_EXTENDED;
|
||||||
line._extendedAttrs[x] = new ExtendedAttrsImage(0, 0, imageId, tileId);
|
(line._extendedAttrs ??= {})[x] = new ExtendedAttrsImage(0, 0, imageId, tileId);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _evictOnAlternate(): void {
|
private _evictOnAlternate(): void {
|
||||||
@@ -652,7 +652,7 @@ export class ImageStorage implements IDisposable {
|
|||||||
}
|
}
|
||||||
for (let x = 0; x < this._terminal.cols; ++x) {
|
for (let x = 0; x < this._terminal.cols; ++x) {
|
||||||
if (line._data[x * Cell.SIZE + Cell.BG] & BgFlags.HAS_EXTENDED) {
|
if (line._data[x * Cell.SIZE + Cell.BG] & BgFlags.HAS_EXTENDED) {
|
||||||
const imgId = line._extendedAttrs[x]?.imageId;
|
const imgId = line._extendedAttrs?.[x]?.imageId;
|
||||||
if (imgId) {
|
if (imgId) {
|
||||||
const spec = this._images.get(imgId);
|
const spec = this._images.get(imgId);
|
||||||
if (spec) {
|
if (spec) {
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ export interface IExtendedAttrsImage extends IExtendedAttrs {
|
|||||||
|
|
||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
export interface IBufferLineExt extends IBufferLine {
|
export interface IBufferLineExt extends IBufferLine {
|
||||||
_extendedAttrs: {[index: number]: IExtendedAttrsImage | undefined};
|
_extendedAttrs: {[index: number]: IExtendedAttrsImage | undefined} | undefined;
|
||||||
_data: Uint32Array;
|
_data: Uint32Array;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -118,7 +118,10 @@ test.describe('ImageAddon', () => {
|
|||||||
}
|
}
|
||||||
// bufferline privates
|
// bufferline privates
|
||||||
strictEqual(await ctx.page.evaluate('window.term._core.buffer.lines.get(0)._data instanceof Uint32Array'), true);
|
strictEqual(await ctx.page.evaluate('window.term._core.buffer.lines.get(0)._data instanceof Uint32Array'), true);
|
||||||
strictEqual(await ctx.page.evaluate('window.term._core.buffer.lines.get(0)._extendedAttrs instanceof Object'), true);
|
strictEqual(await ctx.page.evaluate(`(() => {
|
||||||
|
const ea = window.term._core.buffer.lines.get(0)._extendedAttrs;
|
||||||
|
return ea === undefined || (typeof ea === 'object' && ea !== null);
|
||||||
|
})()`), true);
|
||||||
// inputhandler privates
|
// inputhandler privates
|
||||||
strictEqual(await ctx.page.evaluate('window.term._core._inputHandler._curAttrData.constructor.name'), '_AttributeData');
|
strictEqual(await ctx.page.evaluate('window.term._core._inputHandler._curAttrData.constructor.name'), '_AttributeData');
|
||||||
strictEqual(await ctx.page.evaluate('window.term._core._inputHandler._parser.constructor.name'), 'EscapeSequenceParser');
|
strictEqual(await ctx.page.evaluate('window.term._core._inputHandler._parser.constructor.name'), 'EscapeSequenceParser');
|
||||||
|
|||||||
@@ -389,8 +389,9 @@ export class GlyphRenderer extends Disposable {
|
|||||||
gl.bindTexture(gl.TEXTURE_2D, this._atlasTextures[i].texture);
|
gl.bindTexture(gl.TEXTURE_2D, this._atlasTextures[i].texture);
|
||||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
||||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
||||||
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
|
||||||
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
|
||||||
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, atlas.pages[i].canvas);
|
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, atlas.pages[i].canvas);
|
||||||
gl.generateMipmap(gl.TEXTURE_2D);
|
|
||||||
this._atlasTextures[i].version = atlas.pages[i].version;
|
this._atlasTextures[i].version = atlas.pages[i].version;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1080,17 +1080,33 @@ class AtlasPage {
|
|||||||
size: number,
|
size: number,
|
||||||
sourcePages?: AtlasPage[]
|
sourcePages?: AtlasPage[]
|
||||||
) {
|
) {
|
||||||
if (sourcePages) {
|
|
||||||
for (const p of sourcePages) {
|
|
||||||
this._glyphs.push(...p.glyphs);
|
|
||||||
this._usedPixels += p._usedPixels;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.canvas = createCanvas(document, size, size);
|
this.canvas = createCanvas(document, size, size);
|
||||||
// The canvas needs alpha because we use clearColor to convert the background color to alpha.
|
// The canvas needs alpha because we use clearColor to convert the background color to alpha.
|
||||||
// It might also contain some characters with transparent backgrounds if allowTransparency is
|
// It might also contain some characters with transparent backgrounds if allowTransparency is
|
||||||
// set.
|
// set.
|
||||||
this.ctx = throwIfFalsy(this.canvas.getContext('2d', { alpha: true }));
|
this.ctx = throwIfFalsy(this.canvas.getContext('2d', { alpha: true }));
|
||||||
|
if (sourcePages) {
|
||||||
|
if (sourcePages.length === 4) {
|
||||||
|
// optimized for quadmerge
|
||||||
|
this._glyphs = this._glyphs.concat(
|
||||||
|
sourcePages[0].glyphs,
|
||||||
|
sourcePages[1].glyphs,
|
||||||
|
sourcePages[2].glyphs,
|
||||||
|
sourcePages[3].glyphs
|
||||||
|
);
|
||||||
|
this._usedPixels = sourcePages[0]._usedPixels +
|
||||||
|
sourcePages[1]._usedPixels +
|
||||||
|
sourcePages[2]._usedPixels +
|
||||||
|
sourcePages[3]._usedPixels;
|
||||||
|
} else {
|
||||||
|
// fallback for non quadmerges (should never be used)
|
||||||
|
for (let i = 0; i < sourcePages.length; ++i) {
|
||||||
|
this._glyphs = this._glyphs.concat(sourcePages[i].glyphs);
|
||||||
|
this._usedPixels += sourcePages[i]._usedPixels;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public clear(): void {
|
public clear(): void {
|
||||||
|
|||||||
@@ -456,7 +456,18 @@ export class WebglRenderer extends Disposable implements IRenderer {
|
|||||||
|
|
||||||
for (y = start; y <= end; y++) {
|
for (y = start; y <= end; y++) {
|
||||||
row = y + terminal.buffer.ydisp;
|
row = y + terminal.buffer.ydisp;
|
||||||
line = terminal.buffer.lines.get(row)!;
|
const bufferLine = terminal.buffer.lines.get(row);
|
||||||
|
if (!bufferLine) {
|
||||||
|
this._model.lineLengths[y] = 0;
|
||||||
|
for (x = 0; x < terminal.cols; x++) {
|
||||||
|
j = ((y * terminal.cols) + x) * RenderModelConstants.INDICIES_PER_CELL;
|
||||||
|
modelUpdated = true;
|
||||||
|
this._nullModelCell(x, y, j, 0, 0, 0);
|
||||||
|
}
|
||||||
|
this._setRowBlinkState(y, false);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
line = bufferLine;
|
||||||
let rowHasBlinkingCells = false;
|
let rowHasBlinkingCells = false;
|
||||||
this._model.lineLengths[y] = 0;
|
this._model.lineLengths[y] = 0;
|
||||||
isCursorRow = cursorY === row;
|
isCursorRow = cursorY === row;
|
||||||
@@ -587,13 +598,9 @@ export class WebglRenderer extends Disposable implements IRenderer {
|
|||||||
// Null out non-first cells
|
// Null out non-first cells
|
||||||
for (x++; x <= lastCharX; x++) {
|
for (x++; x <= lastCharX; x++) {
|
||||||
j = ((y * terminal.cols) + x) * RenderModelConstants.INDICIES_PER_CELL;
|
j = ((y * terminal.cols) + x) * RenderModelConstants.INDICIES_PER_CELL;
|
||||||
this._glyphRenderer.value!.updateCell(x, y, NULL_CELL_CODE, 0, 0, 0, NULL_CELL_CHAR, 0, 0);
|
|
||||||
this._model.cells[j] = NULL_CELL_CODE;
|
|
||||||
// Don't re-resolve the cell color since multi-colored ligature backgrounds are not
|
// Don't re-resolve the cell color since multi-colored ligature backgrounds are not
|
||||||
// supported
|
// supported
|
||||||
this._model.cells[j + RenderModelConstants.BG_OFFSET] = this._cellColorResolver.result.bg;
|
this._nullModelCell(x, y, j, this._cellColorResolver.result.bg, this._cellColorResolver.result.fg, this._cellColorResolver.result.ext);
|
||||||
this._model.cells[j + RenderModelConstants.FG_OFFSET] = this._cellColorResolver.result.fg;
|
|
||||||
this._model.cells[j + RenderModelConstants.EXT_OFFSET] = this._cellColorResolver.result.ext;
|
|
||||||
}
|
}
|
||||||
x--; // Go back to the previous update cell for next iteration
|
x--; // Go back to the previous update cell for next iteration
|
||||||
}
|
}
|
||||||
@@ -607,6 +614,14 @@ export class WebglRenderer extends Disposable implements IRenderer {
|
|||||||
this._updateTextBlinkState();
|
this._updateTextBlinkState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _nullModelCell(x: number, y: number, cellIndex: number, bg: number, fg: number, ext: number): void {
|
||||||
|
this._glyphRenderer.value!.updateCell(x, y, NULL_CELL_CODE, bg, fg, ext, NULL_CELL_CHAR, 0, 0);
|
||||||
|
this._model.cells[cellIndex] = NULL_CELL_CODE;
|
||||||
|
this._model.cells[cellIndex + RenderModelConstants.BG_OFFSET] = bg;
|
||||||
|
this._model.cells[cellIndex + RenderModelConstants.FG_OFFSET] = fg;
|
||||||
|
this._model.cells[cellIndex + RenderModelConstants.EXT_OFFSET] = ext;
|
||||||
|
}
|
||||||
|
|
||||||
private _resetBlinkingRowState(): void {
|
private _resetBlinkingRowState(): void {
|
||||||
this._rowHasBlinkingCells = new Array(this._terminal.rows).fill(false);
|
this._rowHasBlinkingCells = new Array(this._terminal.rows).fill(false);
|
||||||
this._rowHasBlinkingCellsCount = 0;
|
this._rowHasBlinkingCellsCount = 0;
|
||||||
|
|||||||
@@ -11,8 +11,33 @@ export class WebglWindow extends BaseWindow implements IControlWindow {
|
|||||||
public readonly label = 'webgl';
|
public readonly label = 'webgl';
|
||||||
|
|
||||||
private _textureAtlasContainer!: HTMLElement;
|
private _textureAtlasContainer!: HTMLElement;
|
||||||
|
private _stressRunning = false;
|
||||||
|
private _stressScrolling: HTMLInputElement | undefined;
|
||||||
|
|
||||||
public build(container: HTMLElement): void {
|
public build(container: HTMLElement): void {
|
||||||
|
const stressDiv = document.createElement('div');
|
||||||
|
const stressStart = document.createElement('button');
|
||||||
|
stressStart.id = 'stress-start';
|
||||||
|
stressStart.textContent = 'atlas stress start';
|
||||||
|
stressStart.addEventListener('click', () => this._stress());
|
||||||
|
stressDiv.appendChild(stressStart);
|
||||||
|
|
||||||
|
const stressStop = document.createElement('button');
|
||||||
|
stressStop.id = 'stress-stop';
|
||||||
|
stressStop.textContent = 'atlas stress stop';
|
||||||
|
stressStop.addEventListener('click', () => this._stressRunning = false);
|
||||||
|
stressDiv.appendChild(stressStop);
|
||||||
|
|
||||||
|
this._stressScrolling = document.createElement('input');
|
||||||
|
this._stressScrolling.type = 'checkbox';
|
||||||
|
this._stressScrolling.id = 'stress-scrolling';
|
||||||
|
stressDiv.appendChild(this._stressScrolling);
|
||||||
|
const stressScrollingLabel = document.createElement('label');
|
||||||
|
stressScrollingLabel.htmlFor = 'stress-scrolling';
|
||||||
|
stressScrollingLabel.textContent = 'stress scrolling';
|
||||||
|
stressDiv.appendChild(stressScrollingLabel);
|
||||||
|
container.appendChild(stressDiv);
|
||||||
|
|
||||||
const zoomCheckbox = document.createElement('input');
|
const zoomCheckbox = document.createElement('input');
|
||||||
zoomCheckbox.type = 'checkbox';
|
zoomCheckbox.type = 'checkbox';
|
||||||
zoomCheckbox.id = 'texture-atlas-zoom';
|
zoomCheckbox.id = 'texture-atlas-zoom';
|
||||||
@@ -48,4 +73,33 @@ export class WebglWindow extends BaseWindow implements IControlWindow {
|
|||||||
canvas.style.width = `${canvas.width / dpr}px`;
|
canvas.style.width = `${canvas.width / dpr}px`;
|
||||||
canvas.style.height = `${canvas.height / dpr}px`;
|
canvas.style.height = `${canvas.height / dpr}px`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async _stress(): Promise<void> {
|
||||||
|
const TEXT = '!"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~';
|
||||||
|
if (this._stressRunning) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this._stressRunning = true;
|
||||||
|
const scrolling = !!(this._stressScrolling?.checked);
|
||||||
|
for (let r = 0; r < 256; r += 2) {
|
||||||
|
for (let g = 0; g < 256; g += 2) {
|
||||||
|
let s: string[] = [];
|
||||||
|
for (let b = 0; b < 256; b += 2) {
|
||||||
|
if (!this._stressRunning) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const rbg = `RGB: ${[r, g, b]}`;
|
||||||
|
s.push(`\r\x1b[38;2;${r};${g};${b}m${rbg.padEnd(18, ' ')}${TEXT}`);
|
||||||
|
if (s.length >= 16) {
|
||||||
|
if (scrolling) {
|
||||||
|
await new Promise<void>(r => this._terminal.write(s.join('\r\n') + '\r\n', r));
|
||||||
|
} else {
|
||||||
|
await new Promise<void>(r => this._terminal.write('\x1b[H' + s.join('\r\n'), r));
|
||||||
|
}
|
||||||
|
s = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Generated
+167
-64
@@ -4700,18 +4700,22 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/glob": {
|
"node_modules/glob": {
|
||||||
"version": "13.0.6",
|
"version": "10.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz",
|
||||||
"integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==",
|
"integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==",
|
||||||
|
"deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "BlueOak-1.0.0",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"minimatch": "^10.2.2",
|
"foreground-child": "^3.1.0",
|
||||||
"minipass": "^7.1.3",
|
"jackspeak": "^3.1.2",
|
||||||
"path-scurry": "^2.0.2"
|
"minimatch": "^9.0.4",
|
||||||
|
"minipass": "^7.1.2",
|
||||||
|
"package-json-from-dist": "^1.0.0",
|
||||||
|
"path-scurry": "^1.11.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"bin": {
|
||||||
"node": "18 || 20 || >=22"
|
"glob": "dist/esm/bin.mjs"
|
||||||
},
|
},
|
||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://github.com/sponsors/isaacs"
|
"url": "https://github.com/sponsors/isaacs"
|
||||||
@@ -4737,6 +4741,39 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "BSD-2-Clause"
|
"license": "BSD-2-Clause"
|
||||||
},
|
},
|
||||||
|
"node_modules/glob/node_modules/balanced-match": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/glob/node_modules/brace-expansion": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"balanced-match": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/glob/node_modules/minimatch": {
|
||||||
|
"version": "9.0.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz",
|
||||||
|
"integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"brace-expansion": "^2.0.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16 || 14 >=14.17"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/isaacs"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/gopd": {
|
"node_modules/gopd": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
|
||||||
@@ -5758,44 +5795,15 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/mocha/node_modules/brace-expansion": {
|
"node_modules/mocha/node_modules/brace-expansion": {
|
||||||
"version": "2.1.1",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.0.tgz",
|
||||||
"integrity": "sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==",
|
"integrity": "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"balanced-match": "^1.0.0"
|
"balanced-match": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/mocha/node_modules/glob": {
|
|
||||||
"version": "10.5.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz",
|
|
||||||
"integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==",
|
|
||||||
"deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me",
|
|
||||||
"dev": true,
|
|
||||||
"license": "ISC",
|
|
||||||
"dependencies": {
|
|
||||||
"foreground-child": "^3.1.0",
|
|
||||||
"jackspeak": "^3.1.2",
|
|
||||||
"minimatch": "^9.0.4",
|
|
||||||
"minipass": "^7.1.2",
|
|
||||||
"package-json-from-dist": "^1.0.0",
|
|
||||||
"path-scurry": "^1.11.1"
|
|
||||||
},
|
|
||||||
"bin": {
|
|
||||||
"glob": "dist/esm/bin.mjs"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/isaacs"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/mocha/node_modules/lru-cache": {
|
|
||||||
"version": "10.4.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
|
|
||||||
"integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "ISC"
|
|
||||||
},
|
|
||||||
"node_modules/mocha/node_modules/minimatch": {
|
"node_modules/mocha/node_modules/minimatch": {
|
||||||
"version": "9.0.9",
|
"version": "9.0.9",
|
||||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz",
|
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz",
|
||||||
@@ -5812,23 +5820,6 @@
|
|||||||
"url": "https://github.com/sponsors/isaacs"
|
"url": "https://github.com/sponsors/isaacs"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/mocha/node_modules/path-scurry": {
|
|
||||||
"version": "1.11.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz",
|
|
||||||
"integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "BlueOak-1.0.0",
|
|
||||||
"dependencies": {
|
|
||||||
"lru-cache": "^10.2.0",
|
|
||||||
"minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=16 || 14 >=14.18"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/isaacs"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/ms": {
|
"node_modules/ms": {
|
||||||
"version": "2.1.3",
|
"version": "2.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||||
@@ -5996,6 +5987,24 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/nyc/node_modules/glob": {
|
||||||
|
"version": "13.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz",
|
||||||
|
"integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "BlueOak-1.0.0",
|
||||||
|
"dependencies": {
|
||||||
|
"minimatch": "^10.2.2",
|
||||||
|
"minipass": "^7.1.3",
|
||||||
|
"path-scurry": "^2.0.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "18 || 20 || >=22"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/isaacs"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/nyc/node_modules/locate-path": {
|
"node_modules/nyc/node_modules/locate-path": {
|
||||||
"version": "5.0.0",
|
"version": "5.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
|
||||||
@@ -6038,6 +6047,23 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/nyc/node_modules/path-scurry": {
|
||||||
|
"version": "2.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz",
|
||||||
|
"integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "BlueOak-1.0.0",
|
||||||
|
"dependencies": {
|
||||||
|
"lru-cache": "^11.0.0",
|
||||||
|
"minipass": "^7.1.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "18 || 20 || >=22"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/isaacs"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/nyc/node_modules/signal-exit": {
|
"node_modules/nyc/node_modules/signal-exit": {
|
||||||
"version": "3.0.7",
|
"version": "3.0.7",
|
||||||
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
|
||||||
@@ -6435,22 +6461,29 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/path-scurry": {
|
"node_modules/path-scurry": {
|
||||||
"version": "2.0.2",
|
"version": "1.11.1",
|
||||||
"resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz",
|
||||||
"integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==",
|
"integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "BlueOak-1.0.0",
|
"license": "BlueOak-1.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"lru-cache": "^11.0.0",
|
"lru-cache": "^10.2.0",
|
||||||
"minipass": "^7.1.2"
|
"minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "18 || 20 || >=22"
|
"node": ">=16 || 14 >=14.18"
|
||||||
},
|
},
|
||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://github.com/sponsors/isaacs"
|
"url": "https://github.com/sponsors/isaacs"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/path-scurry/node_modules/lru-cache": {
|
||||||
|
"version": "10.4.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
|
||||||
|
"integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "ISC"
|
||||||
|
},
|
||||||
"node_modules/path-to-regexp": {
|
"node_modules/path-to-regexp": {
|
||||||
"version": "8.4.2",
|
"version": "8.4.2",
|
||||||
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.2.tgz",
|
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.2.tgz",
|
||||||
@@ -6827,6 +6860,41 @@
|
|||||||
"url": "https://github.com/sponsors/isaacs"
|
"url": "https://github.com/sponsors/isaacs"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/rimraf/node_modules/glob": {
|
||||||
|
"version": "13.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz",
|
||||||
|
"integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "BlueOak-1.0.0",
|
||||||
|
"dependencies": {
|
||||||
|
"minimatch": "^10.2.2",
|
||||||
|
"minipass": "^7.1.3",
|
||||||
|
"path-scurry": "^2.0.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "18 || 20 || >=22"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/isaacs"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/rimraf/node_modules/path-scurry": {
|
||||||
|
"version": "2.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz",
|
||||||
|
"integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "BlueOak-1.0.0",
|
||||||
|
"dependencies": {
|
||||||
|
"lru-cache": "^11.0.0",
|
||||||
|
"minipass": "^7.1.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "18 || 20 || >=22"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/isaacs"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/router": {
|
"node_modules/router": {
|
||||||
"version": "2.2.0",
|
"version": "2.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz",
|
||||||
@@ -7586,6 +7654,41 @@
|
|||||||
"node": "20 || >=22"
|
"node": "20 || >=22"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/test-exclude/node_modules/glob": {
|
||||||
|
"version": "13.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz",
|
||||||
|
"integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "BlueOak-1.0.0",
|
||||||
|
"dependencies": {
|
||||||
|
"minimatch": "^10.2.2",
|
||||||
|
"minipass": "^7.1.3",
|
||||||
|
"path-scurry": "^2.0.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "18 || 20 || >=22"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/isaacs"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/test-exclude/node_modules/path-scurry": {
|
||||||
|
"version": "2.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz",
|
||||||
|
"integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "BlueOak-1.0.0",
|
||||||
|
"dependencies": {
|
||||||
|
"lru-cache": "^11.0.0",
|
||||||
|
"minipass": "^7.1.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "18 || 20 || >=22"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/isaacs"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/tiny-emitter": {
|
"node_modules/tiny-emitter": {
|
||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz",
|
||||||
|
|||||||
+3
-3
@@ -11,6 +11,9 @@
|
|||||||
"workspaces": [
|
"workspaces": [
|
||||||
"addons/*"
|
"addons/*"
|
||||||
],
|
],
|
||||||
|
"overrides": {
|
||||||
|
"serialize-javascript": "^7.0.3"
|
||||||
|
},
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"cli",
|
"cli",
|
||||||
"command-line",
|
"command-line",
|
||||||
@@ -115,8 +118,5 @@
|
|||||||
"webpack-cli": "^7.0.2",
|
"webpack-cli": "^7.0.2",
|
||||||
"ws": "^8.20.0",
|
"ws": "^8.20.0",
|
||||||
"xterm-benchmark": "^0.3.2"
|
"xterm-benchmark": "^0.3.2"
|
||||||
},
|
|
||||||
"overrides": {
|
|
||||||
"serialize-javascript": "^7.0.5"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1072,10 +1072,6 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal {
|
|||||||
* Clear the entire buffer, making the prompt line the new first line.
|
* Clear the entire buffer, making the prompt line the new first line.
|
||||||
*/
|
*/
|
||||||
public clear(): void {
|
public clear(): void {
|
||||||
if (this.buffer.ybase === 0 && this.buffer.y === 0) {
|
|
||||||
// Don't clear if it's already clear
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.buffer.clearAllMarkers();
|
this.buffer.clearAllMarkers();
|
||||||
this.buffer.lines.set(0, this.buffer.lines.get(this.buffer.ybase + this.buffer.y)!);
|
this.buffer.lines.set(0, this.buffer.lines.get(this.buffer.ybase + this.buffer.y)!);
|
||||||
this.buffer.lines.length = 1;
|
this.buffer.lines.length = 1;
|
||||||
|
|||||||
@@ -536,9 +536,14 @@ export class DomRenderer extends Disposable implements IRenderer {
|
|||||||
for (let y = start; y <= end; y++) {
|
for (let y = start; y <= end; y++) {
|
||||||
const row = y + buffer.ydisp;
|
const row = y + buffer.ydisp;
|
||||||
const rowElement = this._rowElements[y];
|
const rowElement = this._rowElements[y];
|
||||||
|
if (!rowElement) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
const lineData = buffer.lines.get(row);
|
const lineData = buffer.lines.get(row);
|
||||||
if (!rowElement || !lineData) {
|
if (!lineData) {
|
||||||
break;
|
rowElement.replaceChildren();
|
||||||
|
this._setRowBlinkState(y, false);
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
rowElement.replaceChildren(
|
rowElement.replaceChildren(
|
||||||
...this._rowFactory.createRow(
|
...this._rowFactory.createRow(
|
||||||
@@ -610,9 +615,14 @@ export class DomRenderer extends Disposable implements IRenderer {
|
|||||||
for (let i = y; i <= y2; ++i) {
|
for (let i = y; i <= y2; ++i) {
|
||||||
const row = i + buffer.ydisp;
|
const row = i + buffer.ydisp;
|
||||||
const rowElement = this._rowElements[i];
|
const rowElement = this._rowElements[i];
|
||||||
|
if (!rowElement) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
const bufferline = buffer.lines.get(row);
|
const bufferline = buffer.lines.get(row);
|
||||||
if (!rowElement || !bufferline) {
|
if (!bufferline) {
|
||||||
break;
|
rowElement.replaceChildren();
|
||||||
|
this._setRowBlinkState(i, false);
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
rowElement.replaceChildren(
|
rowElement.replaceChildren(
|
||||||
...this._rowFactory.createRow(
|
...this._rowFactory.createRow(
|
||||||
|
|||||||
@@ -64,6 +64,13 @@ describe('SelectionModel', () => {
|
|||||||
assert.deepEqual(model.finalSelectionStart, undefined);
|
assert.deepEqual(model.finalSelectionStart, undefined);
|
||||||
assert.deepEqual(model.finalSelectionEnd, undefined);
|
assert.deepEqual(model.finalSelectionEnd, undefined);
|
||||||
});
|
});
|
||||||
|
it('should reset selection start to origin when start row is trimmed', () => {
|
||||||
|
model.selectionStart = [50, 0];
|
||||||
|
model.selectionEnd = [10, 2];
|
||||||
|
assert.equal(model.handleTrim(1), true);
|
||||||
|
assert.deepEqual(model.finalSelectionStart, [0, 0]);
|
||||||
|
assert.deepEqual(model.finalSelectionEnd, [10, 1]);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('finalSelectionStart', () => {
|
describe('finalSelectionStart', () => {
|
||||||
|
|||||||
@@ -135,9 +135,10 @@ export class SelectionModel {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the selection start is trimmed, ensure the start column is 0.
|
// If the selection start row is trimmed away, reset to the buffer origin.
|
||||||
if (this.selectionStart && this.selectionStart[1] < 0) {
|
if (this.selectionStart && this.selectionStart[1] < 0) {
|
||||||
this.selectionStart[1] = 0;
|
this.selectionStart = [0, 0];
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -100,6 +100,7 @@ export class CharacterJoinerService implements ICharacterJoinerService {
|
|||||||
|
|
||||||
const ranges: [number, number][] = [];
|
const ranges: [number, number][] = [];
|
||||||
const lineStr = line.translateToString(true);
|
const lineStr = line.translateToString(true);
|
||||||
|
const trimmedLength = line.getTrimmedLength();
|
||||||
|
|
||||||
// Because some cells can be represented by multiple javascript characters,
|
// Because some cells can be represented by multiple javascript characters,
|
||||||
// we track the cell and the string indexes separately. This allows us to
|
// we track the cell and the string indexes separately. This allows us to
|
||||||
@@ -111,7 +112,7 @@ export class CharacterJoinerService implements ICharacterJoinerService {
|
|||||||
let rangeAttrFG = line.getFg(0);
|
let rangeAttrFG = line.getFg(0);
|
||||||
let rangeAttrBG = line.getBg(0);
|
let rangeAttrBG = line.getBg(0);
|
||||||
|
|
||||||
for (let x = 0; x < line.getTrimmedLength(); x++) {
|
for (let x = 0; x < trimmedLength; x++) {
|
||||||
line.loadCell(x, this._workCell);
|
line.loadCell(x, this._workCell);
|
||||||
|
|
||||||
if (this._workCell.getWidth() === 0) {
|
if (this._workCell.getWidth() === 0) {
|
||||||
@@ -147,7 +148,7 @@ export class CharacterJoinerService implements ICharacterJoinerService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Process any trailing ranges.
|
// Process any trailing ranges.
|
||||||
if (this._bufferService.cols - rangeStartColumn > 1) {
|
if (trimmedLength - rangeStartColumn > 1) {
|
||||||
const joinedRanges = this._getJoinedRanges(
|
const joinedRanges = this._getJoinedRanges(
|
||||||
lineStr,
|
lineStr,
|
||||||
rangeStartStringIndex,
|
rangeStartStringIndex,
|
||||||
@@ -216,7 +217,8 @@ export class CharacterJoinerService implements ICharacterJoinerService {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let x = startCol; x < this._bufferService.cols; x++) {
|
const trimmedLength = line.getTrimmedLength();
|
||||||
|
for (let x = startCol; x < trimmedLength; x++) {
|
||||||
const width = line.getWidth(x);
|
const width = line.getWidth(x);
|
||||||
const length = line.getString(x).length || WHITESPACE_CELL_CHAR.length;
|
const length = line.getString(x).length || WHITESPACE_CELL_CHAR.length;
|
||||||
|
|
||||||
@@ -264,7 +266,7 @@ export class CharacterJoinerService implements ICharacterJoinerService {
|
|||||||
// If there is still a range left at the end, it must extend all the way to
|
// If there is still a range left at the end, it must extend all the way to
|
||||||
// the end of the line.
|
// the end of the line.
|
||||||
if (currentRange) {
|
if (currentRange) {
|
||||||
currentRange[1] = this._bufferService.cols;
|
currentRange[1] = trimmedLength;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -704,7 +704,7 @@ export class SelectionService extends Disposable implements ISelectionService {
|
|||||||
if (this._activeSelectionMode !== SelectionMode.COLUMN) {
|
if (this._activeSelectionMode !== SelectionMode.COLUMN) {
|
||||||
this._model.selectionEnd[0] = this._bufferService.cols;
|
this._model.selectionEnd[0] = this._bufferService.cols;
|
||||||
}
|
}
|
||||||
this._model.selectionEnd[1] = Math.min(buffer.ydisp + this._bufferService.rows, buffer.lines.length - 1);
|
this._model.selectionEnd[1] = Math.min(buffer.ydisp + this._bufferService.rows - 1, buffer.lines.length - 1);
|
||||||
} else {
|
} else {
|
||||||
if (this._activeSelectionMode !== SelectionMode.COLUMN) {
|
if (this._activeSelectionMode !== SelectionMode.COLUMN) {
|
||||||
this._model.selectionEnd[0] = 0;
|
this._model.selectionEnd[0] = 0;
|
||||||
|
|||||||
@@ -71,12 +71,20 @@ export interface IBufferLineStringCache {
|
|||||||
* (if only one particular value is needed) or `loadCell`. For `loadCell` in a loop
|
* (if only one particular value is needed) or `loadCell`. For `loadCell` in a loop
|
||||||
* memory allocs / GC pressure can be greatly reduced by reusing the CellData object.
|
* memory allocs / GC pressure can be greatly reduced by reusing the CellData object.
|
||||||
*/
|
*/
|
||||||
|
/**
|
||||||
|
* Shared empty sparse map for lines with no combining data.
|
||||||
|
* Uses a sentinel + copy-on-write so `copyFrom` can detect empty `_combined` with
|
||||||
|
* reference equality. `_extendedAttrs` uses `undefined` instead (addon-image writes
|
||||||
|
* the map directly without BufferLine APIs).
|
||||||
|
*/
|
||||||
|
const EMPTY_SPARSE_MAP: {[index: number]: string} = Object.create(null);
|
||||||
|
|
||||||
export class BufferLine implements IBufferLine {
|
export class BufferLine implements IBufferLine {
|
||||||
protected _data: Uint32Array;
|
protected _data: Uint32Array;
|
||||||
/** Sparse cache; only read when `IS_COMBINED_MASK` is set in `_data`. */
|
/** Sparse cache; only read when `IS_COMBINED_MASK` is set in `_data`. */
|
||||||
protected _combined: {[index: number]: string} = {};
|
protected _combined: {[index: number]: string} = EMPTY_SPARSE_MAP;
|
||||||
/** Sparse cache; only read when `HAS_EXTENDED` is set in `_data`. */
|
/** Sparse cache; only read when `HAS_EXTENDED` is set in `_data`. */
|
||||||
protected _extendedAttrs: {[index: number]: IExtendedAttrs | undefined} = {};
|
protected _extendedAttrs: {[index: number]: IExtendedAttrs | undefined} | undefined;
|
||||||
protected _stringCacheEntryRef: WeakRef<IBufferLineStringCacheEntry> | undefined;
|
protected _stringCacheEntryRef: WeakRef<IBufferLineStringCacheEntry> | undefined;
|
||||||
public length: number;
|
public length: number;
|
||||||
|
|
||||||
@@ -121,6 +129,7 @@ export class BufferLine implements IBufferLine {
|
|||||||
this._invalidateStringCache();
|
this._invalidateStringCache();
|
||||||
this._data[index * Constants.CELL_INDICIES + Cell.FG] = value[CHAR_DATA_ATTR_INDEX];
|
this._data[index * Constants.CELL_INDICIES + Cell.FG] = value[CHAR_DATA_ATTR_INDEX];
|
||||||
if (value[CHAR_DATA_CHAR_INDEX].length > 1) {
|
if (value[CHAR_DATA_CHAR_INDEX].length > 1) {
|
||||||
|
this._ensureCombinedMap();
|
||||||
this._combined[index] = value[1];
|
this._combined[index] = value[1];
|
||||||
this._data[index * Constants.CELL_INDICIES + Cell.CONTENT] = index | Content.IS_COMBINED_MASK | (value[CHAR_DATA_WIDTH_INDEX] << Content.WIDTH_SHIFT);
|
this._data[index * Constants.CELL_INDICIES + Cell.CONTENT] = index | Content.IS_COMBINED_MASK | (value[CHAR_DATA_WIDTH_INDEX] << Content.WIDTH_SHIFT);
|
||||||
} else {
|
} else {
|
||||||
@@ -211,7 +220,7 @@ export class BufferLine implements IBufferLine {
|
|||||||
cell.combinedData = '';
|
cell.combinedData = '';
|
||||||
}
|
}
|
||||||
if (cell.bg & BgFlags.HAS_EXTENDED) {
|
if (cell.bg & BgFlags.HAS_EXTENDED) {
|
||||||
cell.extended = this._extendedAttrs[index]!;
|
cell.extended = this._extendedAttrs![index]!;
|
||||||
} else {
|
} else {
|
||||||
// Do not mutate cell.extended in place: it may still reference this line's map entry from a
|
// Do not mutate cell.extended in place: it may still reference this line's map entry from a
|
||||||
// prior loadCell into a reused CellData (e.g. $workCell during insert/delete).
|
// prior loadCell into a reused CellData (e.g. $workCell during insert/delete).
|
||||||
@@ -226,10 +235,11 @@ export class BufferLine implements IBufferLine {
|
|||||||
public setCell(index: number, cell: ICellData): void {
|
public setCell(index: number, cell: ICellData): void {
|
||||||
this._invalidateStringCache();
|
this._invalidateStringCache();
|
||||||
if (cell.content & Content.IS_COMBINED_MASK) {
|
if (cell.content & Content.IS_COMBINED_MASK) {
|
||||||
|
this._ensureCombinedMap();
|
||||||
this._combined[index] = cell.combinedData;
|
this._combined[index] = cell.combinedData;
|
||||||
}
|
}
|
||||||
if (cell.bg & BgFlags.HAS_EXTENDED) {
|
if (cell.bg & BgFlags.HAS_EXTENDED) {
|
||||||
this._extendedAttrs[index] = cell.extended;
|
this._ensureExtendedAttrsMap()[index] = cell.extended;
|
||||||
}
|
}
|
||||||
this._data[index * Constants.CELL_INDICIES + Cell.CONTENT] = cell.content;
|
this._data[index * Constants.CELL_INDICIES + Cell.CONTENT] = cell.content;
|
||||||
this._data[index * Constants.CELL_INDICIES + Cell.FG] = cell.fg;
|
this._data[index * Constants.CELL_INDICIES + Cell.FG] = cell.fg;
|
||||||
@@ -244,7 +254,7 @@ export class BufferLine implements IBufferLine {
|
|||||||
public setCellFromCodepoint(index: number, codePoint: number, width: number, attrs: IAttributeData): void {
|
public setCellFromCodepoint(index: number, codePoint: number, width: number, attrs: IAttributeData): void {
|
||||||
this._invalidateStringCache();
|
this._invalidateStringCache();
|
||||||
if (attrs.bg & BgFlags.HAS_EXTENDED) {
|
if (attrs.bg & BgFlags.HAS_EXTENDED) {
|
||||||
this._extendedAttrs[index] = attrs.extended;
|
this._ensureExtendedAttrsMap()[index] = attrs.extended;
|
||||||
}
|
}
|
||||||
this._data[index * Constants.CELL_INDICIES + Cell.CONTENT] = codePoint | (width << Content.WIDTH_SHIFT);
|
this._data[index * Constants.CELL_INDICIES + Cell.CONTENT] = codePoint | (width << Content.WIDTH_SHIFT);
|
||||||
this._data[index * Constants.CELL_INDICIES + Cell.FG] = attrs.fg;
|
this._data[index * Constants.CELL_INDICIES + Cell.FG] = attrs.fg;
|
||||||
@@ -262,12 +272,14 @@ export class BufferLine implements IBufferLine {
|
|||||||
let content = this._data[index * Constants.CELL_INDICIES + Cell.CONTENT];
|
let content = this._data[index * Constants.CELL_INDICIES + Cell.CONTENT];
|
||||||
if (content & Content.IS_COMBINED_MASK) {
|
if (content & Content.IS_COMBINED_MASK) {
|
||||||
// we already have a combined string, simply add
|
// we already have a combined string, simply add
|
||||||
|
this._ensureCombinedMap();
|
||||||
this._combined[index] += stringFromCodePoint(codePoint);
|
this._combined[index] += stringFromCodePoint(codePoint);
|
||||||
} else {
|
} else {
|
||||||
if (content & Content.CODEPOINT_MASK) {
|
if (content & Content.CODEPOINT_MASK) {
|
||||||
// normal case for combining chars:
|
// normal case for combining chars:
|
||||||
// - move current leading char + new one into combined string
|
// - move current leading char + new one into combined string
|
||||||
// - set combined flag
|
// - set combined flag
|
||||||
|
this._ensureCombinedMap();
|
||||||
this._combined[index] = stringFromCodePoint(content & Content.CODEPOINT_MASK) + stringFromCodePoint(codePoint);
|
this._combined[index] = stringFromCodePoint(content & Content.CODEPOINT_MASK) + stringFromCodePoint(codePoint);
|
||||||
content &= ~Content.CODEPOINT_MASK; // set codepoint in buffer to 0
|
content &= ~Content.CODEPOINT_MASK; // set codepoint in buffer to 0
|
||||||
content |= Content.IS_COMBINED_MASK;
|
content |= Content.IS_COMBINED_MASK;
|
||||||
@@ -410,11 +422,13 @@ export class BufferLine implements IBufferLine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// remove any cut off extended attributes
|
// remove any cut off extended attributes
|
||||||
const extKeys = Object.keys(this._extendedAttrs);
|
if (this._extendedAttrs) {
|
||||||
for (let i = 0; i < extKeys.length; i++) {
|
const extKeys = Object.keys(this._extendedAttrs);
|
||||||
const key = parseInt(extKeys[i], 10);
|
for (let i = 0; i < extKeys.length; i++) {
|
||||||
if (key >= cols) {
|
const key = parseInt(extKeys[i], 10);
|
||||||
delete this._extendedAttrs[key];
|
if (key >= cols) {
|
||||||
|
delete this._extendedAttrs[key];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -450,8 +464,8 @@ export class BufferLine implements IBufferLine {
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this._combined = {};
|
this._combined = EMPTY_SPARSE_MAP;
|
||||||
this._extendedAttrs = {};
|
this._extendedAttrs = undefined;
|
||||||
for (let i = 0; i < this.length; ++i) {
|
for (let i = 0; i < this.length; ++i) {
|
||||||
this.setCell(i, fillCellData);
|
this.setCell(i, fillCellData);
|
||||||
}
|
}
|
||||||
@@ -608,19 +622,57 @@ export class BufferLine implements IBufferLine {
|
|||||||
private _copyCellMapsFrom(src: BufferLine, srcCol: number, destCol: number): void {
|
private _copyCellMapsFrom(src: BufferLine, srcCol: number, destCol: number): void {
|
||||||
const srcStart = srcCol * Constants.CELL_INDICIES;
|
const srcStart = srcCol * Constants.CELL_INDICIES;
|
||||||
if (src._data[srcStart + Cell.CONTENT] & Content.IS_COMBINED_MASK) {
|
if (src._data[srcStart + Cell.CONTENT] & Content.IS_COMBINED_MASK) {
|
||||||
|
this._ensureCombinedMap();
|
||||||
this._combined[destCol] = src._combined[srcCol];
|
this._combined[destCol] = src._combined[srcCol];
|
||||||
}
|
}
|
||||||
if (src._data[srcStart + Cell.BG] & BgFlags.HAS_EXTENDED) {
|
if (src._data[srcStart + Cell.BG] & BgFlags.HAS_EXTENDED) {
|
||||||
this._extendedAttrs[destCol] = src._extendedAttrs[srcCol];
|
this._ensureExtendedAttrsMap()[destCol] = src._extendedAttrs![srcCol]!;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Rebuild sparse maps from another line, keyed only by `_data` flags. */
|
/** Rebuild sparse maps from another line, keyed only by `_data` flags. */
|
||||||
private _copySparseMapsFrom(line: BufferLine): void {
|
private _copySparseMapsFrom(line: BufferLine): void {
|
||||||
|
const srcCombined = line._combined;
|
||||||
|
const srcExtended = line._extendedAttrs;
|
||||||
|
|
||||||
|
// Fast path: blank / scroll-recycle source (empty `_combined` sentinel, no extended map).
|
||||||
|
if (srcCombined === EMPTY_SPARSE_MAP && !srcExtended) {
|
||||||
|
this._combined = EMPTY_SPARSE_MAP;
|
||||||
|
this._extendedAttrs = undefined;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this._copySparseMapsFromDense(line);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Copy sparse maps by scanning `_data` flags (inlined, no per-cell function calls). */
|
||||||
|
private _copySparseMapsFromDense(line: BufferLine): void {
|
||||||
this._combined = {};
|
this._combined = {};
|
||||||
this._extendedAttrs = {};
|
this._extendedAttrs = {};
|
||||||
for (let i = 0; i < line.length; i++) {
|
const srcData = line._data;
|
||||||
this._copyCellMapsFrom(line, i, i);
|
const srcCombined = line._combined;
|
||||||
|
const srcExtended = line._extendedAttrs;
|
||||||
|
const len = line.length;
|
||||||
|
const indices = Constants.CELL_INDICIES;
|
||||||
|
const combined = this._combined;
|
||||||
|
const extendedAttrs = this._extendedAttrs;
|
||||||
|
for (let i = 0; i < len; i++) {
|
||||||
|
const start = i * indices;
|
||||||
|
if (srcData[start + Cell.CONTENT] & Content.IS_COMBINED_MASK) {
|
||||||
|
combined[i] = srcCombined[i];
|
||||||
|
}
|
||||||
|
if (srcData[start + Cell.BG] & BgFlags.HAS_EXTENDED) {
|
||||||
|
extendedAttrs[i] = srcExtended![i];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _ensureCombinedMap(): void {
|
||||||
|
if (this._combined === EMPTY_SPARSE_MAP) {
|
||||||
|
this._combined = {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _ensureExtendedAttrsMap(): {[index: number]: IExtendedAttrs | undefined} {
|
||||||
|
return this._extendedAttrs ??= {};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ function assertDecodedRange(
|
|||||||
}
|
}
|
||||||
const target = new Uint32Array(count);
|
const target = new Uint32Array(count);
|
||||||
const length = decode(input, target);
|
const length = decode(input, target);
|
||||||
assert.equal(length, count);
|
assert.strictEqual(length, count);
|
||||||
let mismatchIndex = -1;
|
let mismatchIndex = -1;
|
||||||
let index = 0;
|
let index = 0;
|
||||||
for (let i = min; i < max; ++i) {
|
for (let i = min; i < max; ++i) {
|
||||||
@@ -92,8 +92,8 @@ function assertDecodedRange(
|
|||||||
}
|
}
|
||||||
index++;
|
index++;
|
||||||
}
|
}
|
||||||
assert.equal(mismatchIndex, -1);
|
assert.strictEqual(mismatchIndex, -1);
|
||||||
assert.equal(outputToString(target, length), input);
|
assert.strictEqual(outputToString(target, length), input);
|
||||||
}
|
}
|
||||||
|
|
||||||
const BATCH_SIZE = 8192;
|
const BATCH_SIZE = 8192;
|
||||||
@@ -116,9 +116,9 @@ describe('text encodings', () => {
|
|||||||
const data = new Uint32Array(s.length);
|
const data = new Uint32Array(s.length);
|
||||||
for (let i = 0; i < s.length; ++i) {
|
for (let i = 0; i < s.length; ++i) {
|
||||||
data[i] = s.charCodeAt(i);
|
data[i] = s.charCodeAt(i);
|
||||||
assert.equal(stringFromCodePoint(data[i]), s[i]);
|
assert.strictEqual(stringFromCodePoint(data[i]), s[i]);
|
||||||
}
|
}
|
||||||
assert.equal(utf32ToString(data), s);
|
assert.strictEqual(utf32ToString(data), s);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('StringToUtf32 decoder', () => {
|
describe('StringToUtf32 decoder', () => {
|
||||||
@@ -159,7 +159,7 @@ describe('text encodings', () => {
|
|||||||
const decoder = new StringToUtf32();
|
const decoder = new StringToUtf32();
|
||||||
const target = new Uint32Array(5);
|
const target = new Uint32Array(5);
|
||||||
const length = decoder.decode(String.fromCharCode(0xFEFF), target);
|
const length = decoder.decode(String.fromCharCode(0xFEFF), target);
|
||||||
assert.equal(length, 0);
|
assert.strictEqual(length, 0);
|
||||||
decoder.clear();
|
decoder.clear();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -169,7 +169,7 @@ describe('text encodings', () => {
|
|||||||
const target = new Uint32Array(500);
|
const target = new Uint32Array(500);
|
||||||
for (let i = 0; i < TEST_STRINGS.length; ++i) {
|
for (let i = 0; i < TEST_STRINGS.length; ++i) {
|
||||||
const length = decoder.decode(TEST_STRINGS[i], target);
|
const length = decoder.decode(TEST_STRINGS[i], target);
|
||||||
assert.equal(toString(target, length), TEST_STRINGS[i]);
|
assert.strictEqual(toString(target, length), TEST_STRINGS[i]);
|
||||||
decoder.clear();
|
decoder.clear();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -184,7 +184,7 @@ describe('text encodings', () => {
|
|||||||
const written = decoder.decode(input[i], target);
|
const written = decoder.decode(input[i], target);
|
||||||
decoded += toString(target, written);
|
decoded += toString(target, written);
|
||||||
}
|
}
|
||||||
assert(decoded, 'Ä€𝄞Ö𝄞€Ü𝄞€');
|
assert.strictEqual(decoded, 'Ä€𝄞Ö𝄞€Ü𝄞€');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -226,7 +226,7 @@ describe('text encodings', () => {
|
|||||||
const target = new Uint32Array(5);
|
const target = new Uint32Array(5);
|
||||||
const utf8Data = stringToUtf8Bytes(String.fromCharCode(0xFEFF));
|
const utf8Data = stringToUtf8Bytes(String.fromCharCode(0xFEFF));
|
||||||
const length = decoder.decode(utf8Data, target);
|
const length = decoder.decode(utf8Data, target);
|
||||||
assert.equal(length, 0);
|
assert.strictEqual(length, 0);
|
||||||
decoder.clear();
|
decoder.clear();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -237,7 +237,7 @@ describe('text encodings', () => {
|
|||||||
for (let i = 0; i < TEST_STRINGS.length; ++i) {
|
for (let i = 0; i < TEST_STRINGS.length; ++i) {
|
||||||
const utf8Data = stringToUtf8Bytes(TEST_STRINGS[i]);
|
const utf8Data = stringToUtf8Bytes(TEST_STRINGS[i]);
|
||||||
const length = decoder.decode(utf8Data, target);
|
const length = decoder.decode(utf8Data, target);
|
||||||
assert.equal(toString(target, length), TEST_STRINGS[i]);
|
assert.strictEqual(toString(target, length), TEST_STRINGS[i]);
|
||||||
decoder.clear();
|
decoder.clear();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -252,7 +252,7 @@ describe('text encodings', () => {
|
|||||||
const written = decoder.decode(utf8Data.slice(i, i + 1), target);
|
const written = decoder.decode(utf8Data.slice(i, i + 1), target);
|
||||||
decoded += toString(target, written);
|
decoded += toString(target, written);
|
||||||
}
|
}
|
||||||
assert(decoded, 'ÄÖÜßöäü');
|
assert.strictEqual(decoded, 'ÄÖÜßöäü');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('2/3 byte sequences - advance by 1', () => {
|
it('2/3 byte sequences - advance by 1', () => {
|
||||||
@@ -264,7 +264,7 @@ describe('text encodings', () => {
|
|||||||
const written = decoder.decode(utf8Data.slice(i, i + 1), target);
|
const written = decoder.decode(utf8Data.slice(i, i + 1), target);
|
||||||
decoded += toString(target, written);
|
decoded += toString(target, written);
|
||||||
}
|
}
|
||||||
assert(decoded, 'Āր܀߀ö€ä€ü');
|
assert.strictEqual(decoded, 'Āր܀߀ö€ä€ü');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('2/3/4 byte sequences - advance by 1', () => {
|
it('2/3/4 byte sequences - advance by 1', () => {
|
||||||
@@ -276,7 +276,7 @@ describe('text encodings', () => {
|
|||||||
const written = decoder.decode(utf8Data.slice(i, i + 1), target);
|
const written = decoder.decode(utf8Data.slice(i, i + 1), target);
|
||||||
decoded += toString(target, written);
|
decoded += toString(target, written);
|
||||||
}
|
}
|
||||||
assert(decoded, 'Ä€𝄞Ö𝄞€Ü𝄞€');
|
assert.strictEqual(decoded, 'Ä€𝄞Ö𝄞€Ü𝄞€');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('2/3/4 byte sequences - advance by 2', () => {
|
it('2/3/4 byte sequences - advance by 2', () => {
|
||||||
@@ -288,7 +288,7 @@ describe('text encodings', () => {
|
|||||||
const written = decoder.decode(utf8Data.slice(i, i + 2), target);
|
const written = decoder.decode(utf8Data.slice(i, i + 2), target);
|
||||||
decoded += toString(target, written);
|
decoded += toString(target, written);
|
||||||
}
|
}
|
||||||
assert(decoded, 'Ä€𝄞Ö𝄞€Ü𝄞€');
|
assert.strictEqual(decoded, 'Ä€𝄞Ö𝄞€Ü𝄞€');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('2/3/4 byte sequences - advance by 3', () => {
|
it('2/3/4 byte sequences - advance by 3', () => {
|
||||||
@@ -300,7 +300,7 @@ describe('text encodings', () => {
|
|||||||
const written = decoder.decode(utf8Data.slice(i, i + 3), target);
|
const written = decoder.decode(utf8Data.slice(i, i + 3), target);
|
||||||
decoded += toString(target, written);
|
decoded += toString(target, written);
|
||||||
}
|
}
|
||||||
assert(decoded, 'Ä€𝄞Ö𝄞€Ü𝄞€');
|
assert.strictEqual(decoded, 'Ä€𝄞Ö𝄞€Ü𝄞€');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('BOMs (3 byte sequences) - advance by 2', () => {
|
it('BOMs (3 byte sequences) - advance by 2', () => {
|
||||||
@@ -312,7 +312,7 @@ describe('text encodings', () => {
|
|||||||
const written = decoder.decode(utf8Data.slice(i, i + 2), target);
|
const written = decoder.decode(utf8Data.slice(i, i + 2), target);
|
||||||
decoded += toString(target, written);
|
decoded += toString(target, written);
|
||||||
}
|
}
|
||||||
assert.equal(decoded, '');
|
assert.strictEqual(decoded, '');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('test break after 3 bytes - issue #2495', () => {
|
it('test break after 3 bytes - issue #2495', () => {
|
||||||
@@ -320,11 +320,37 @@ describe('text encodings', () => {
|
|||||||
const target = new Uint32Array(5);
|
const target = new Uint32Array(5);
|
||||||
const utf8Data = fromByteString('\xf0\xa0\x9c\x8e');
|
const utf8Data = fromByteString('\xf0\xa0\x9c\x8e');
|
||||||
let written = decoder.decode(utf8Data.slice(0, 3), target);
|
let written = decoder.decode(utf8Data.slice(0, 3), target);
|
||||||
assert.equal(written, 0);
|
assert.strictEqual(written, 0);
|
||||||
written = decoder.decode(utf8Data.slice(3), target);
|
written = decoder.decode(utf8Data.slice(3), target);
|
||||||
assert.equal(written, 1);
|
assert.strictEqual(written, 1);
|
||||||
assert(toString(target, written), '𠜎');
|
assert.strictEqual(toString(target, written), '𠜎');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('0x80 not swallowed in continuation', () => {
|
||||||
|
it('A—B', () => {
|
||||||
|
const decoder = new Utf8ToUtf32();
|
||||||
|
const target = new Uint32Array(5);
|
||||||
|
const utf8Data = new TextEncoder().encode('A—BA—BA—BA—BA—B');
|
||||||
|
let decoded = '';
|
||||||
|
for (let i = 0; i < utf8Data.length; i += 2) {
|
||||||
|
const written = decoder.decode(utf8Data.slice(i, i + 2), target);
|
||||||
|
decoded += toString(target, written);
|
||||||
|
}
|
||||||
|
assert.strictEqual(decoded, 'A—BA—BA—BA—BA—B');
|
||||||
|
});
|
||||||
|
it('A𐀀B', () => {
|
||||||
|
const decoder = new Utf8ToUtf32();
|
||||||
|
const target = new Uint32Array(5);
|
||||||
|
const utf8Data = new TextEncoder().encode('A𐀀BA𐀀BA𐀀BA𐀀BA𐀀B');
|
||||||
|
let decoded = '';
|
||||||
|
for (let i = 0; i < utf8Data.length; i += 2) {
|
||||||
|
const written = decoder.decode(utf8Data.slice(i, i + 2), target);
|
||||||
|
decoded += toString(target, written);
|
||||||
|
}
|
||||||
|
assert.strictEqual(decoded, 'A𐀀BA𐀀BA𐀀BA𐀀BA𐀀B');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -158,9 +158,9 @@ export class Utf8ToUtf32 {
|
|||||||
cp &= ((((cp & 0xE0) === 0xC0)) ? 0x1F : (((cp & 0xF0) === 0xE0)) ? 0x0F : 0x07);
|
cp &= ((((cp & 0xE0) === 0xC0)) ? 0x1F : (((cp & 0xF0) === 0xE0)) ? 0x0F : 0x07);
|
||||||
let pos = 0;
|
let pos = 0;
|
||||||
let tmp: number;
|
let tmp: number;
|
||||||
while ((tmp = this.interim[++pos] & 0x3F) && pos < 4) {
|
while ((tmp = this.interim[++pos]) && pos < 4) {
|
||||||
cp <<= 6;
|
cp <<= 6;
|
||||||
cp |= tmp;
|
cp |= tmp & 0x3F;
|
||||||
}
|
}
|
||||||
// missing bytes - read ahead from input
|
// missing bytes - read ahead from input
|
||||||
const type = (((this.interim[0] & 0xE0) === 0xC0)) ? 2 : (((this.interim[0] & 0xF0) === 0xE0)) ? 3 : 4;
|
const type = (((this.interim[0] & 0xE0) === 0xC0)) ? 2 : (((this.interim[0] & 0xF0) === 0xE0)) ? 3 : 4;
|
||||||
|
|||||||
@@ -99,10 +99,6 @@ export class Terminal extends CoreTerminal {
|
|||||||
* Clear the entire buffer, making the prompt line the new first line.
|
* Clear the entire buffer, making the prompt line the new first line.
|
||||||
*/
|
*/
|
||||||
public clear(): void {
|
public clear(): void {
|
||||||
if (this.buffer.ybase === 0 && this.buffer.y === 0) {
|
|
||||||
// Don't clear if it's already clear
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.buffer.clearAllMarkers();
|
this.buffer.clearAllMarkers();
|
||||||
this.buffer.lines.set(0, this.buffer.lines.get(this.buffer.ybase + this.buffer.y)!);
|
this.buffer.lines.set(0, this.buffer.lines.get(this.buffer.ybase + this.buffer.y)!);
|
||||||
this.buffer.lines.length = 1;
|
this.buffer.lines.length = 1;
|
||||||
|
|||||||
@@ -0,0 +1,113 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 The xterm.js authors. All rights reserved.
|
||||||
|
* @license MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { perfContext, before, RuntimeCase } from 'xterm-benchmark';
|
||||||
|
import { BufferLine, DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine';
|
||||||
|
import { BufferLineStringCache } from 'common/buffer/BufferLineStringCache';
|
||||||
|
import { CellData } from 'common/buffer/CellData';
|
||||||
|
import { BgFlags, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE, UnderlineStyle } from 'common/buffer/Constants';
|
||||||
|
|
||||||
|
const ITERATIONS = 500_000;
|
||||||
|
const stringCache = new BufferLineStringCache();
|
||||||
|
const nullCell = CellData.fromCharData([0, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]);
|
||||||
|
const yCell = CellData.fromCharData([0, 'y', 1, 'y'.charCodeAt(0)]);
|
||||||
|
|
||||||
|
function fillPlainY(line: BufferLine, cols: number): void {
|
||||||
|
for (let i = 0; i < cols; i++) {
|
||||||
|
line.setCellFromCodepoint(i, 'y'.charCodeAt(0), 1, DEFAULT_ATTR_DATA);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function setupScrollRecyclePair(cols: number): { recycled: BufferLine, blank: BufferLine } {
|
||||||
|
const blank = new BufferLine(stringCache, cols, nullCell, false);
|
||||||
|
const recycled = new BufferLine(stringCache, cols, yCell, false);
|
||||||
|
fillPlainY(recycled, cols);
|
||||||
|
return { recycled, blank };
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Source line with combining chars and extended attrs spread across columns. */
|
||||||
|
function buildSparseSourceLine(cols: number): BufferLine {
|
||||||
|
const line = new BufferLine(stringCache, cols, nullCell, false);
|
||||||
|
for (let i = 0; i < cols; i++) {
|
||||||
|
if (i % 10 === 0) {
|
||||||
|
const cell = CellData.fromCharData([0, 'a', 1, 'a'.charCodeAt(0)]);
|
||||||
|
cell.extended.underlineStyle = UnderlineStyle.CURLY;
|
||||||
|
cell.bg |= BgFlags.HAS_EXTENDED;
|
||||||
|
line.setCell(i, cell);
|
||||||
|
} else if (i % 17 === 0) {
|
||||||
|
line.setCell(i, CellData.fromCharData([1, 'e\u0301', 1, '\u0301'.charCodeAt(0)]));
|
||||||
|
} else {
|
||||||
|
line.setCellFromCodepoint(i, 'y'.charCodeAt(0), 1, DEFAULT_ATTR_DATA);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return line;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setupCopyFromSparsePair(cols: number): { dest: BufferLine, src: BufferLine } {
|
||||||
|
const src = buildSparseSourceLine(cols);
|
||||||
|
const dest = new BufferLine(stringCache, cols, yCell, false);
|
||||||
|
fillPlainY(dest, cols);
|
||||||
|
return { dest, src };
|
||||||
|
}
|
||||||
|
|
||||||
|
perfContext('BufferLine.copyFrom (scroll recycle)', () => {
|
||||||
|
perfContext('cols=80', () => {
|
||||||
|
let recycled: BufferLine;
|
||||||
|
let blank: BufferLine;
|
||||||
|
before(() => {
|
||||||
|
({ recycled, blank } = setupScrollRecyclePair(80));
|
||||||
|
});
|
||||||
|
new RuntimeCase('', () => {
|
||||||
|
for (let i = 0; i < ITERATIONS; i++) {
|
||||||
|
recycled.copyFrom(blank);
|
||||||
|
}
|
||||||
|
return { payloadSize: ITERATIONS };
|
||||||
|
}, { fork: false }).showAverageRuntime();
|
||||||
|
});
|
||||||
|
|
||||||
|
perfContext('cols=279', () => {
|
||||||
|
let recycled: BufferLine;
|
||||||
|
let blank: BufferLine;
|
||||||
|
before(() => {
|
||||||
|
({ recycled, blank } = setupScrollRecyclePair(279));
|
||||||
|
});
|
||||||
|
new RuntimeCase('', () => {
|
||||||
|
for (let i = 0; i < ITERATIONS; i++) {
|
||||||
|
recycled.copyFrom(blank);
|
||||||
|
}
|
||||||
|
return { payloadSize: ITERATIONS };
|
||||||
|
}, { fork: false }).showAverageRuntime();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
perfContext('BufferLine.copyFrom (sparse source: combined + extended attrs)', () => {
|
||||||
|
perfContext('cols=80', () => {
|
||||||
|
let dest: BufferLine;
|
||||||
|
let src: BufferLine;
|
||||||
|
before(() => {
|
||||||
|
({ dest, src } = setupCopyFromSparsePair(80));
|
||||||
|
});
|
||||||
|
new RuntimeCase('', () => {
|
||||||
|
for (let i = 0; i < ITERATIONS; i++) {
|
||||||
|
dest.copyFrom(src);
|
||||||
|
}
|
||||||
|
return { payloadSize: ITERATIONS };
|
||||||
|
}, { fork: false }).showAverageRuntime();
|
||||||
|
});
|
||||||
|
|
||||||
|
perfContext('cols=279', () => {
|
||||||
|
let dest: BufferLine;
|
||||||
|
let src: BufferLine;
|
||||||
|
before(() => {
|
||||||
|
({ dest, src } = setupCopyFromSparsePair(279));
|
||||||
|
});
|
||||||
|
new RuntimeCase('', () => {
|
||||||
|
for (let i = 0; i < ITERATIONS; i++) {
|
||||||
|
dest.copyFrom(src);
|
||||||
|
}
|
||||||
|
return { payloadSize: ITERATIONS };
|
||||||
|
}, { fork: false }).showAverageRuntime();
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user