Compare commits
14
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c2f5c424a8 | ||
|
|
3cdbc77386 | ||
|
|
f57b8e1534 | ||
|
|
c8f11d63b5 | ||
|
|
3d929a1db0 | ||
|
|
2fe3fd13a1 | ||
|
|
f33f502267 | ||
|
|
7c24c58007 | ||
|
|
596634ac41 | ||
|
|
0c8271db49 | ||
|
|
f3f4dcaf9f | ||
|
|
64718444e2 | ||
|
|
e9c648f519 | ||
|
|
60bae9528d |
+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`]);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -142,12 +142,14 @@ export class SearchAddon extends Disposable implements ITerminalAddon, ISearchAp
|
|||||||
}
|
}
|
||||||
prevResult = result;
|
prevResult = result;
|
||||||
results.push(prevResult);
|
results.push(prevResult);
|
||||||
result = this._engine.find(
|
const cols = this._terminal.cols;
|
||||||
term,
|
let nextCol = prevResult.col + prevResult.size;
|
||||||
prevResult.col + prevResult.term.length >= this._terminal.cols ? prevResult.row + 1 : prevResult.row,
|
let nextRow = prevResult.row;
|
||||||
prevResult.col + prevResult.term.length >= this._terminal.cols ? 0 : prevResult.col + 1,
|
if (nextCol >= cols) {
|
||||||
searchOptions
|
nextRow += Math.floor(nextCol / cols);
|
||||||
);
|
nextCol = nextCol % cols;
|
||||||
|
}
|
||||||
|
result = this._engine.find(term, nextRow, nextCol, searchOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
this._resultTracker.updateResults(results, this._highlightLimit);
|
this._resultTracker.updateResults(results, this._highlightLimit);
|
||||||
|
|||||||
@@ -414,6 +414,20 @@ test.describe('Search Tests', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test.describe('Regression tests', () => {
|
test.describe('Regression tests', () => {
|
||||||
|
test('should advance highlight-all scan by buffer match size for wide characters', async () => {
|
||||||
|
await ctx.page.evaluate(`
|
||||||
|
window.calls = [];
|
||||||
|
window.search.onDidChangeResults(e => window.calls.push(e));
|
||||||
|
`);
|
||||||
|
await ctx.proxy.resize(3, 5);
|
||||||
|
await ctx.proxy.write('𝄞𝄞𝄞');
|
||||||
|
strictEqual(await ctx.page.evaluate(`window.search.findNext('𝄞', { decorations: { activeMatchColorOverviewRuler: '#ff0000', matchOverviewRuler: '#ffff00' } })`), true);
|
||||||
|
deepStrictEqual(await ctx.page.evaluate('window.calls'), [
|
||||||
|
{ resultCount: 3, resultIndex: 0 }
|
||||||
|
]);
|
||||||
|
await ctx.proxy.resize(80, 24);
|
||||||
|
});
|
||||||
|
|
||||||
test.describe('#2444 wrapped line content not being found', () => {
|
test.describe('#2444 wrapped line content not being found', () => {
|
||||||
let fixture: string;
|
let fixture: string;
|
||||||
test.beforeAll(async () => {
|
test.beforeAll(async () => {
|
||||||
|
|||||||
@@ -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 = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -495,6 +495,21 @@ describe('BufferLine', function(): void {
|
|||||||
assert.deepEqual(columns, [0]);
|
assert.deepEqual(columns, [0]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
describe('copyCellsFrom', () => {
|
||||||
|
it('should only move combined data within the copied range', () => {
|
||||||
|
const src = new TestBufferLine(10, NULL_CELL_DATA, false);
|
||||||
|
src.setCell(1, createCellData(1, 'a', 1));
|
||||||
|
src.addCodepointToCell(1, '\u0301'.charCodeAt(0), 0);
|
||||||
|
src.setCell(5, createCellData(1, 'z', 1));
|
||||||
|
src.addCodepointToCell(5, '\u0301'.charCodeAt(0), 0);
|
||||||
|
|
||||||
|
const dest = new TestBufferLine(10, NULL_CELL_DATA, false);
|
||||||
|
dest.copyCellsFrom(src, 0, 0, 2, false);
|
||||||
|
|
||||||
|
assert.property(dest.combined, '1');
|
||||||
|
assert.notProperty(dest.combined, '5');
|
||||||
|
});
|
||||||
|
});
|
||||||
describe('addCharToCell', () => {
|
describe('addCharToCell', () => {
|
||||||
it('should set width to 1 for empty cell', () => {
|
it('should set width to 1 for empty cell', () => {
|
||||||
const line = new TestBufferLine(3, NULL_CELL_DATA, false);
|
const line = new TestBufferLine(3, NULL_CELL_DATA, false);
|
||||||
|
|||||||
@@ -609,9 +609,13 @@ export class BufferLine implements IBufferLine {
|
|||||||
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._combined[destCol] = src._combined[srcCol];
|
this._combined[destCol] = src._combined[srcCol];
|
||||||
|
} else {
|
||||||
|
delete this._combined[destCol];
|
||||||
}
|
}
|
||||||
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._extendedAttrs[destCol] = src._extendedAttrs[srcCol];
|
||||||
|
} else {
|
||||||
|
delete this._extendedAttrs[destCol];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import { CellData } from './CellData';
|
|||||||
import { NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE } from './Constants';
|
import { NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE } from './Constants';
|
||||||
import { reflowLargerGetLinesToRemove, reflowSmallerGetNewLineLengths } from './BufferReflow';
|
import { reflowLargerGetLinesToRemove, reflowSmallerGetNewLineLengths } from './BufferReflow';
|
||||||
import { IBufferLine } from './Types';
|
import { IBufferLine } from './Types';
|
||||||
|
import { createCellData, NULL_CELL_DATA } from '../TestUtils.test';
|
||||||
|
|
||||||
const TEST_STRING_CACHE = new BufferLineStringCache();
|
const TEST_STRING_CACHE = new BufferLineStringCache();
|
||||||
|
|
||||||
@@ -122,5 +123,27 @@ describe('BufferReflow', () => {
|
|||||||
const lines = createWrappedLines('abcde');
|
const lines = createWrappedLines('abcde');
|
||||||
assert.notDeepEqual(reflowLargerGetLinesToRemove(lines, 1, 5, 10, nullCell, false), []);
|
assert.notDeepEqual(reflowLargerGetLinesToRemove(lines, 1, 5, 10, nullCell, false), []);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should clear isWrapped on the last retained row after unwrap', () => {
|
||||||
|
const lines = new CircularList<BufferLine>(10);
|
||||||
|
const set2 = (line: BufferLine, s: string) => {
|
||||||
|
for (let i = 0; i < s.length; i++) {
|
||||||
|
line.setCell(i, createCellData(0, s[i], 1));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const l0 = new BufferLine(TEST_STRING_CACHE, 2);
|
||||||
|
set2(l0, 'ab');
|
||||||
|
const l1 = new BufferLine(TEST_STRING_CACHE, 2, undefined, true);
|
||||||
|
set2(l1, 'cd');
|
||||||
|
const l2 = new BufferLine(TEST_STRING_CACHE, 2, undefined, true);
|
||||||
|
set2(l2, 'ef');
|
||||||
|
lines.push(l0);
|
||||||
|
lines.push(l1);
|
||||||
|
lines.push(l2);
|
||||||
|
|
||||||
|
reflowLargerGetLinesToRemove(lines, 2, 6, 99, NULL_CELL_DATA, false);
|
||||||
|
|
||||||
|
assert.equal(l1.isWrapped, false);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -99,6 +99,11 @@ export function reflowLargerGetLinesToRemove(lines: CircularList<IBufferLine>, o
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (countToRemove > 0) {
|
if (countToRemove > 0) {
|
||||||
|
wrappedLines[destLineIndex].isWrapped = false;
|
||||||
|
const lineAfterGroup = lines.get(y + wrappedLines.length);
|
||||||
|
if (lineAfterGroup) {
|
||||||
|
lineAfterGroup.isWrapped = false;
|
||||||
|
}
|
||||||
toRemove.push(y + wrappedLines.length - countToRemove); // index
|
toRemove.push(y + wrappedLines.length - countToRemove); // index
|
||||||
toRemove.push(countToRemove);
|
toRemove.push(countToRemove);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user