From 253d1e9beb365db483f48151010bee5a283d822a Mon Sep 17 00:00:00 2001 From: Jeffrey Chen Date: Sun, 12 Apr 2026 19:47:13 -0700 Subject: [PATCH 01/47] Underline entire wrapped OSC 8 link on hover --- src/browser/Linkifier.test.ts | 40 +++++++++++ src/browser/OscLinkProvider.test.ts | 105 ++++++++++++++++++++++++++++ src/browser/OscLinkProvider.ts | 92 ++++++++++++++++++++---- 3 files changed, 224 insertions(+), 13 deletions(-) create mode 100644 src/browser/OscLinkProvider.test.ts diff --git a/src/browser/Linkifier.test.ts b/src/browser/Linkifier.test.ts index 4294ef105..685c404f4 100644 --- a/src/browser/Linkifier.test.ts +++ b/src/browser/Linkifier.test.ts @@ -43,6 +43,20 @@ describe('Linkifier2', () => { }, activate: () => { } }; + const multiLineLink: ILink = { + text: 'foo', + range: { + start: { + x: 2, + y: 1 + }, + end: { + x: 4, + y: 2 + } + }, + activate: () => { } + }; beforeEach(() => { const dom = new jsdom.JSDOM(); @@ -86,4 +100,30 @@ describe('Linkifier2', () => { linkifier.linkLeave({ classList: { add: () => { } } } as any, link, {} as any); }); + it('onShowLinkUnderline event range is correct for wrapped links', done => { + linkifier.onShowLinkUnderline(e => { + assert.equal(multiLineLink.range.start.x - 1, e.x1); + assert.equal(multiLineLink.range.start.y - 1, e.y1); + assert.equal(multiLineLink.range.end.x, e.x2); + assert.equal(multiLineLink.range.end.y - 1, e.y2); + + done(); + }); + + linkifier.linkHover({ classList: { add: () => { } } } as any, multiLineLink, {} as any); + }); + + it('onHideLinkUnderline event range is correct for wrapped links', done => { + linkifier.onHideLinkUnderline(e => { + assert.equal(multiLineLink.range.start.x - 1, e.x1); + assert.equal(multiLineLink.range.start.y - 1, e.y1); + assert.equal(multiLineLink.range.end.x, e.x2); + assert.equal(multiLineLink.range.end.y - 1, e.y2); + + done(); + }); + + linkifier.linkLeave({ classList: { add: () => { } } } as any, multiLineLink, {} as any); + }); + }); diff --git a/src/browser/OscLinkProvider.test.ts b/src/browser/OscLinkProvider.test.ts new file mode 100644 index 000000000..9e844cd16 --- /dev/null +++ b/src/browser/OscLinkProvider.test.ts @@ -0,0 +1,105 @@ +/** + * Copyright (c) 2026 The xterm.js authors. All rights reserved. + * @license MIT + */ + +import { assert } from 'chai'; +import { OscLinkProvider } from 'browser/OscLinkProvider'; +import { ILink } from 'browser/Types'; +import { createCellData, MockBufferService, MockOptionsService } from 'common/TestUtils.test'; +import { IBufferService, IOscLinkService } from 'common/services/Services'; +import { IBufferLine, IOscLinkData } from 'common/Types'; + +class TestOscLinkService implements IOscLinkService { + public serviceBrand: any; + public registerLink(_linkData: IOscLinkData): number { return 0; } + public addLineToLink(_linkId: number, _y: number): void { } + public getLinkData(linkId: number): IOscLinkData | undefined { + return { uri: `https://example.com/${linkId}` }; + } +} + +function setText(line: IBufferLine | undefined, x: number, text: string): void { + if (!line) { + throw new Error('Missing buffer line'); + } + for (let i = 0; i < text.length; i++) { + line.setCell(x + i, createCellData(0, text[i], 1)); + } +} + +function setUrl(line: IBufferLine | undefined, x: number, text: string, linkId: number): void { + if (!line) { + throw new Error('Missing buffer line'); + } + for (let i = 0; i < text.length; i++) { + const cell = createCellData(0, text[i], 1); + cell.extended.urlId = linkId; + cell.updateExtended(); + line.setCell(x + i, cell); + } +} + +function getLinks(provider: OscLinkProvider, y: number): Promise { + return new Promise(resolve => provider.provideLinks(y, links => resolve(links ?? []))); +} + +describe('OscLinkProvider', () => { + let bufferService: IBufferService; + let provider: OscLinkProvider; + + beforeEach(() => { + const optionsService = new MockOptionsService(); + bufferService = new MockBufferService(5, 5, optionsService); + provider = new OscLinkProvider(bufferService, optionsService, new TestOscLinkService()); + }); + + it('expands a wrapped link range backward to the previous line', async () => { + const line1 = bufferService.buffer.lines.get(0); + const line2 = bufferService.buffer.lines.get(1); + setText(line1, 0, 'aa'); + setUrl(line1, 2, 'bbb', 1); + setUrl(line2, 0, 'cccc', 1); + setText(line2, 4, 'x'); + line2!.isWrapped = true; + + const links = await getLinks(provider, 2); + assert.lengthOf(links, 1); + assert.deepEqual(links[0].range, { + start: { x: 3, y: 1 }, + end: { x: 4, y: 2 } + }); + }); + + it('expands a wrapped link range forward when a link ends at line boundary', async () => { + const line1 = bufferService.buffer.lines.get(0); + const line2 = bufferService.buffer.lines.get(1); + setUrl(line1, 0, 'aaaaa', 1); + setUrl(line2, 0, 'bb', 1); + setText(line2, 2, 'ccc'); + line2!.isWrapped = true; + + const links = await getLinks(provider, 1); + assert.lengthOf(links, 1); + assert.deepEqual(links[0].range, { + start: { x: 1, y: 1 }, + end: { x: 2, y: 2 } + }); + }); + + it('does not merge wrapped links with different url ids', async () => { + const line1 = bufferService.buffer.lines.get(0); + const line2 = bufferService.buffer.lines.get(1); + setUrl(line1, 0, 'aaaaa', 1); + setUrl(line2, 0, 'bbb', 2); + setText(line2, 3, 'cc'); + line2!.isWrapped = true; + + const links = await getLinks(provider, 1); + assert.lengthOf(links, 1); + assert.deepEqual(links[0].range, { + start: { x: 1, y: 1 }, + end: { x: 5, y: 1 } + }); + }); +}); diff --git a/src/browser/OscLinkProvider.ts b/src/browser/OscLinkProvider.ts index b6a3cce0a..d01a20b35 100644 --- a/src/browser/OscLinkProvider.ts +++ b/src/browser/OscLinkProvider.ts @@ -6,6 +6,7 @@ import { IBufferRange, ILink } from 'browser/Types'; import { ILinkProvider } from 'browser/services/Services'; import { CellData } from 'common/buffer/CellData'; +import { IBufferLine } from 'common/Types'; import { IBufferService, IOptionsService, IOscLinkService } from 'common/services/Services'; export class OscLinkProvider implements ILinkProvider { @@ -57,19 +58,8 @@ export class OscLinkProvider implements ILinkProvider { if (finishLink || (currentStart !== -1 && x === lineLength - 1)) { const text = this._oscLinkService.getLinkData(currentLinkId)?.uri; if (text) { - // These ranges are 1-based - const range: IBufferRange = { - start: { - x: currentStart + 1, - y - }, - end: { - // Offset end x if it's a link that ends on the last cell in the line - x: x + (!finishLink && x === lineLength - 1 ? 1 : 0), - y - } - }; - + const endX = x + (!finishLink && x === lineLength - 1 ? 1 : 0); + const range = this._getRangeWithLineWrap(y, currentStart, endX, currentLinkId); let ignoreLink = false; if (!linkHandler?.allowNonHttpProtocols) { try { @@ -111,6 +101,82 @@ export class OscLinkProvider implements ILinkProvider { // id callback(result); } + + /** + * Expand a single-line OSC 8 range to a contiguous wrapped range for the same link id. + */ + private _getRangeWithLineWrap(y: number, startX: number, endX: number, linkId: number): IBufferRange { + let startY = y; + let finalStartX = startX; + let endY = y; + let finalEndX = endX; + + // Expand upward only when this segment starts at column 0 and the current line is wrapped. + while (finalStartX === 0) { + const currentLine = this._bufferService.buffer.lines.get(startY - 1); + if (!currentLine?.isWrapped) { + break; + } + const previousLine = this._bufferService.buffer.lines.get(startY - 2); + if (!previousLine) { + break; + } + const previousLineLength = previousLine.getTrimmedLength(); + if (previousLineLength === 0 || !this._hasUrlId(previousLine, previousLineLength - 1, linkId)) { + break; + } + let previousStartX = previousLineLength - 1; + while (previousStartX > 0 && this._hasUrlId(previousLine, previousStartX - 1, linkId)) { + previousStartX--; + } + startY--; + finalStartX = previousStartX; + } + + // Expand downward only when this segment reaches trimmed EOL and the next line is wrapped. + while (true) { + const currentLine = this._bufferService.buffer.lines.get(endY - 1); + if (!currentLine) { + break; + } + const currentLineLength = currentLine.getTrimmedLength(); + if (finalEndX !== currentLineLength) { + break; + } + const nextLine = this._bufferService.buffer.lines.get(endY); + if (!nextLine?.isWrapped) { + break; + } + const nextLineLength = nextLine.getTrimmedLength(); + if (nextLineLength === 0 || !this._hasUrlId(nextLine, 0, linkId)) { + break; + } + let nextEndX = 1; + while (nextEndX < nextLineLength && this._hasUrlId(nextLine, nextEndX, linkId)) { + nextEndX++; + } + endY++; + finalEndX = nextEndX; + } + + // IBufferRange uses 1-based coordinates. + return { + start: { + x: finalStartX + 1, + y: startY + }, + end: { + x: finalEndX, + y: endY + } + }; + } + + private _hasUrlId(line: IBufferLine, x: number, linkId: number): boolean { + const cell = this._workCell; + line.loadCell(x, cell); + return !!cell.hasExtendedAttrs() && cell.extended.urlId === linkId; + } } function defaultActivate(e: MouseEvent, uri: string): void { From 69ee4ad23b10bdf8e3fe71a1644518f469ed0820 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Mangeonjean?= Date: Mon, 13 Apr 2026 12:51:17 +0200 Subject: [PATCH 02/47] fix: use element window instead of global object --- src/browser/services/MouseCoordsService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/browser/services/MouseCoordsService.ts b/src/browser/services/MouseCoordsService.ts index e2455fd2b..60f0a6ab9 100644 --- a/src/browser/services/MouseCoordsService.ts +++ b/src/browser/services/MouseCoordsService.ts @@ -18,7 +18,7 @@ export class MouseCoordsService implements IMouseCoordsService { public getCoords(event: {clientX: number, clientY: number}, element: HTMLElement, colCount: number, rowCount: number, isSelection?: boolean): [number, number] | undefined { return getCoords( - window, + getWindow(element), event, element, colCount, From ff72c00219718c14e6aabb086b49746c7077256b Mon Sep 17 00:00:00 2001 From: lawrence3699 Date: Wed, 22 Apr 2026 12:57:27 +1000 Subject: [PATCH 03/47] fix(addon-ligatures): run package build on publish --- addons/addon-ligatures/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/addon-ligatures/package.json b/addons/addon-ligatures/package.json index 5fb0c4f66..cd70ada2b 100644 --- a/addons/addon-ligatures/package.json +++ b/addons/addon-ligatures/package.json @@ -20,7 +20,7 @@ "package": "webpack", "pretest": "npm run build", "test": "nyc mocha out/**/*.test.js", - "prepublish": "npm run package" + "prepublishOnly": "npm run package" }, "keywords": [ "font", From 685434ca847cfca6152604b8be878f3e3a749ef5 Mon Sep 17 00:00:00 2001 From: Sridhar Ratnakumar Date: Thu, 23 Apr 2026 11:35:56 -0400 Subject: [PATCH 04/47] Fix IntersectionObserver retention on dispose --- src/browser/services/RenderService.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/browser/services/RenderService.ts b/src/browser/services/RenderService.ts index 83e3d3379..e999b7eac 100644 --- a/src/browser/services/RenderService.ts +++ b/src/browser/services/RenderService.ts @@ -29,6 +29,7 @@ export class RenderService extends Disposable implements IRenderService { private _renderDebouncer: IRenderDebouncerWithCallback; private _pausedResizeTask: DebouncedIdleTask; private _observerDisposable = this._register(new MutableDisposable()); + private _intersectionObserver: IntersectionObserver | undefined; private _isPaused: boolean = false; private _needsFullRefresh: boolean = false; @@ -127,8 +128,12 @@ export class RenderService extends Disposable implements IRenderService { // and resume based on terminal visibility if so if ('IntersectionObserver' in w) { const observer = new w.IntersectionObserver(e => this._handleIntersectionChange(e[e.length - 1]), { threshold: 0 }); + this._observerDisposable.value = toDisposable(() => { + this._intersectionObserver?.disconnect(); + this._intersectionObserver = undefined; + }); + this._intersectionObserver = observer; observer.observe(screenElement); - this._observerDisposable.value = toDisposable(() => observer.disconnect()); } } From 41d9c4080f759e95139aa3094f2dcae7f7f797c2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 21 May 2026 10:06:23 +0000 Subject: [PATCH 05/47] Bump fast-uri from 3.1.0 to 3.1.2 Bumps [fast-uri](https://github.com/fastify/fast-uri) from 3.1.0 to 3.1.2. - [Release notes](https://github.com/fastify/fast-uri/releases) - [Commits](https://github.com/fastify/fast-uri/compare/v3.1.0...v3.1.2) --- updated-dependencies: - dependency-name: fast-uri dependency-version: 3.1.2 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- package-lock.json | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index e0ac4e728..9f35e360c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3945,9 +3945,9 @@ "dev": true }, "node_modules/fast-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", - "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.2.tgz", + "integrity": "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==", "dev": true, "funding": [ { @@ -3958,7 +3958,8 @@ "type": "opencollective", "url": "https://opencollective.com/fastify" } - ] + ], + "license": "BSD-3-Clause" }, "node_modules/fastest-levenshtein": { "version": "1.0.16", From 17e7dafa17af28b1e0aaf9ce789db287546d19a8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 21 May 2026 10:06:53 +0000 Subject: [PATCH 06/47] Bump axios from 1.15.0 to 1.15.2 Bumps [axios](https://github.com/axios/axios) from 1.15.0 to 1.15.2. - [Release notes](https://github.com/axios/axios/releases) - [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md) - [Commits](https://github.com/axios/axios/compare/v1.15.0...v1.15.2) --- updated-dependencies: - dependency-name: axios dependency-version: 1.15.2 dependency-type: direct:development ... Signed-off-by: dependabot[bot] --- addons/addon-ligatures/package.json | 2 +- package-lock.json | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/addons/addon-ligatures/package.json b/addons/addon-ligatures/package.json index fc7a433de..0e06add18 100644 --- a/addons/addon-ligatures/package.json +++ b/addons/addon-ligatures/package.json @@ -37,7 +37,7 @@ "devDependencies": { "@types/lru-cache": "^5.1.0", "@types/opentype.js": "^0.7.0", - "axios": "^1.15.0", + "axios": "^1.15.2", "font-finder": "^1.1.0", "mkdirp": "0.5.5", "yauzl": "^3.2.1" diff --git a/package-lock.json b/package-lock.json index e0ac4e728..a8072f3f6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -89,7 +89,7 @@ "devDependencies": { "@types/lru-cache": "^5.1.0", "@types/opentype.js": "^0.7.0", - "axios": "^1.15.0", + "axios": "^1.15.2", "font-finder": "^1.1.0", "mkdirp": "0.5.5", "yauzl": "^3.2.1" @@ -2578,9 +2578,9 @@ "dev": true }, "node_modules/axios": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.15.0.tgz", - "integrity": "sha512-wWyJDlAatxk30ZJer+GeCWS209sA42X+N5jU2jy6oHTp7ufw8uzUTVFBX9+wTfAlhiJXGS0Bq7X6efruWjuK9Q==", + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.15.2.tgz", + "integrity": "sha512-wLrXxPtcrPTsNlJmKjkPnNPK2Ihe0hn0wGSaTEiHRPxwjvJwT3hKmXF4dpqxmPO9SoNb2FsYXj/xEo0gHN+D5A==", "dev": true, "license": "MIT", "dependencies": { From f484584b706d2a91363f17e98c61f07d713f1c34 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 26 May 2026 09:30:25 -0700 Subject: [PATCH 07/47] Speed up decoration cell lookup with a per-line index Refs #5176. forEachDecorationAtCell and getDecorationsAtCell previously scanned every registered decoration on each call. With thousands of search highlights this becomes O(total decorations) per cell and blocks rendering during viewport refreshes. Maintain a Map of buffer line -> decorations that cover that line. Multi-line decorations are indexed on every line in their span, so mixed single-line and multi-line workloads stay fast without falling back to a full scan. Keep the index aligned with buffer mutations: - Register and unregister decorations in the line buckets on add/dispose. - On trim, shift line keys synchronously (before marker handlers run). - On insert/delete, re-index affected decorations in a microtask after marker line updates are applied. Inject IBufferService so line listeners follow the active buffer. Add unit tests for trim, dispose-on-trim, insert, and dense single-line plus multi-line lookup. Add DecorationService.benchmark.ts to measure sparse hits and viewport-sized grid scans with 20k decorations. Co-authored-by: Cursor --- src/common/services/DecorationService.test.ts | 105 +++++++++- src/common/services/DecorationService.ts | 185 ++++++++++++++++-- src/common/services/Services.ts | 2 + test/benchmark/DecorationService.benchmark.ts | 109 +++++++++++ 4 files changed, 376 insertions(+), 25 deletions(-) create mode 100644 test/benchmark/DecorationService.benchmark.ts diff --git a/src/common/services/DecorationService.test.ts b/src/common/services/DecorationService.test.ts index 95c7c0e24..b70232f36 100644 --- a/src/common/services/DecorationService.test.ts +++ b/src/common/services/DecorationService.test.ts @@ -8,7 +8,9 @@ import { DecorationService } from './DecorationService'; import { IMarker } from 'common/Types'; import { Disposable } from 'common/Lifecycle'; import { Emitter } from 'common/Event'; -import { MockLogService } from 'common/TestUtils.test'; +import { MockLogService, MockBufferService, MockOptionsService } from 'common/TestUtils.test'; +import { Buffer } from 'common/buffer/Buffer'; +import { DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine'; function createFakeMarker(line: number): IMarker { return Object.freeze(new class extends Disposable { @@ -19,11 +21,16 @@ function createFakeMarker(line: number): IMarker { }()); } +function createDecorationService(): DecorationService { + const bufferService = new MockBufferService(80, 24, new MockOptionsService()); + return new DecorationService(new MockLogService(), bufferService); +} + const fakeMarker: IMarker = createFakeMarker(1); describe('DecorationService', () => { it('should set isDisposed to true after dispose', () => { - const service = new DecorationService(new MockLogService()); + const service = createDecorationService(); const decoration = service.registerDecoration({ marker: fakeMarker }); @@ -35,7 +42,7 @@ describe('DecorationService', () => { describe('forEachDecorationAtCell', () => { it('should find decoration at its marker line', () => { - const service = new DecorationService(new MockLogService()); + const service = createDecorationService(); const decoration = service.registerDecoration({ marker: createFakeMarker(5), width: 10 @@ -48,7 +55,7 @@ describe('DecorationService', () => { }); it('should find decoration with height > 1 on subsequent lines', () => { - const service = new DecorationService(new MockLogService()); + const service = createDecorationService(); const decoration = service.registerDecoration({ marker: createFakeMarker(5), width: 10, @@ -74,7 +81,7 @@ describe('DecorationService', () => { }); it('should not find decoration outside its x range', () => { - const service = new DecorationService(new MockLogService()); + const service = createDecorationService(); const decoration = service.registerDecoration({ marker: createFakeMarker(5), x: 5, @@ -99,11 +106,35 @@ describe('DecorationService', () => { service.forEachDecorationAtCell(8, 5, undefined, d => foundAtX8.push(d)); assert.strictEqual(foundAtX8.length, 0); }); + + it('should find multi-line decoration when single-line decorations exist on other lines', () => { + const bufferService = new MockBufferService(80, 24, new MockOptionsService()); + const serviceWithBuffer = new DecorationService(new MockLogService(), bufferService); + const buffer = bufferService.buffer; + (buffer as Buffer).fillViewportRows(); + + for (let i = 0; i < 100; i++) { + serviceWithBuffer.registerDecoration({ + marker: buffer.addMarker(i), + width: 5 + }); + } + const multiLine = serviceWithBuffer.registerDecoration({ + marker: buffer.addMarker(10), + width: 10, + height: 3 + }); + assert.ok(multiLine); + + const found: typeof multiLine[] = []; + serviceWithBuffer.forEachDecorationAtCell(0, 11, undefined, d => found.push(d)); + assert.include(found, multiLine); + }); }); describe('getDecorationsAtCell', () => { it('should find decoration with height > 1 on subsequent lines', () => { - const service = new DecorationService(new MockLogService()); + const service = createDecorationService(); const decoration = service.registerDecoration({ marker: createFakeMarker(5), width: 10, @@ -117,4 +148,66 @@ describe('DecorationService', () => { assert.strictEqual([...service.getDecorationsAtCell(0, 8)].length, 0); }); }); + + describe('line index maintenance', () => { + it('should keep lookups correct after buffer trim', () => { + const bufferService = new MockBufferService(80, 5, new MockOptionsService({ scrollback: 0 })); + const service = new DecorationService(new MockLogService(), bufferService); + const buffer = bufferService.buffer; + (buffer as Buffer).fillViewportRows(); + + const marker = buffer.addMarker(buffer.lines.length - 1); + const decoration = service.registerDecoration({ marker, width: 10 }); + assert.ok(decoration); + + buffer.lines.onTrimEmitter.fire(1); + + const found: typeof decoration[] = []; + service.forEachDecorationAtCell(0, marker.line, undefined, d => found.push(d)); + assert.strictEqual(found.length, 1); + }); + + it('should remove decoration from line index when marker is trimmed off buffer', () => { + const bufferService = new MockBufferService(80, 5, new MockOptionsService({ scrollback: 0 })); + const service = new DecorationService(new MockLogService(), bufferService); + const buffer = bufferService.buffer; + (buffer as Buffer).fillViewportRows(); + + const marker = buffer.addMarker(0); + const decoration = service.registerDecoration({ marker, width: 10 }); + assert.ok(decoration); + + buffer.lines.onTrimEmitter.fire(1); + assert.isTrue(marker.isDisposed); + assert.isTrue(decoration!.isDisposed); + + const found: typeof decoration[] = []; + service.forEachDecorationAtCell(0, 0, undefined, d => found.push(d)); + assert.strictEqual(found.length, 0); + }); + + it('should keep multi-line decoration indexed after line insert', async () => { + const bufferService = new MockBufferService(80, 10, new MockOptionsService({ scrollback: 100 })); + const service = new DecorationService(new MockLogService(), bufferService); + const buffer = bufferService.buffer; + (buffer as Buffer).fillViewportRows(); + + const marker = buffer.addMarker(3); + const decoration = service.registerDecoration({ marker, width: 10, height: 3 }); + assert.ok(decoration); + + buffer.lines.splice(5, 0, buffer.getBlankLine(DEFAULT_ATTR_DATA)); + await new Promise(resolve => queueMicrotask(resolve)); + + const foundOnSpan: typeof decoration[] = []; + for (let line = marker.line; line < marker.line + 3; line++) { + service.forEachDecorationAtCell(0, line, undefined, d => foundOnSpan.push(d)); + } + assert.include(foundOnSpan, decoration); + + const foundOutsideSpan: typeof decoration[] = []; + service.forEachDecorationAtCell(0, marker.line + 3, undefined, d => foundOutsideSpan.push(d)); + assert.strictEqual(foundOutsideSpan.length, 0); + }); + }); }); diff --git a/src/common/services/DecorationService.ts b/src/common/services/DecorationService.ts index 133e1a985..2bac9717c 100644 --- a/src/common/services/DecorationService.ts +++ b/src/common/services/DecorationService.ts @@ -4,18 +4,17 @@ */ import { css } from 'common/Color'; -import { Disposable, DisposableStore, toDisposable } from 'common/Lifecycle'; -import { IDecorationService, IInternalDecoration, ILogService } from 'common/services/Services'; +import { Disposable, DisposableStore, MutableDisposable, toDisposable } from 'common/Lifecycle'; +import { IBufferService, IDecorationService, IInternalDecoration, ILogService } from 'common/services/Services'; import { SortedList } from 'common/SortedList'; import { IColor } from 'common/Types'; import { IDecoration, IDecorationOptions, IMarker } from '@xterm/xterm'; import { Emitter } from 'common/Event'; +import type { IDeleteEvent, IInsertEvent } from 'common/CircularList'; // Work variables to avoid garbage collection let $xmin = 0; let $xmax = 0; -let $ymin = 0; -let $ymax = 0; export class DecorationService extends Disposable implements IDecorationService { public serviceBrand: any; @@ -27,6 +26,14 @@ export class DecorationService extends Disposable implements IDecorationService */ private readonly _decorations: SortedList; + /** + * Decorations indexed by buffer line. Multi-line decorations are present in every line bucket + * they span so cell lookup only iterates decorations relevant to that line. + */ + private readonly _decorationsByLine: Map = new Map(); + + private readonly _bufferLineListeners = this._register(new MutableDisposable()); + private readonly _onDecorationRegistered = this._register(new Emitter()); public readonly onDecorationRegistered = this._onDecorationRegistered.event; private readonly _onDecorationRemoved = this._register(new Emitter()); @@ -34,12 +41,17 @@ export class DecorationService extends Disposable implements IDecorationService public get decorations(): IterableIterator { return this._decorations.values(); } - constructor(@ILogService private readonly _logService: ILogService) { + constructor( + @ILogService private readonly _logService: ILogService, + @IBufferService private readonly _bufferService: IBufferService + ) { super(); this._decorations = new SortedList(e => e?.marker.line, this._logService); this._register(toDisposable(() => this.reset())); + this._register(this._bufferService.buffers.onBufferActivate(() => this._attachBufferLineListeners())); + this._attachBufferLineListeners(); } public registerDecoration(options: IDecorationOptions): IDecoration | undefined { @@ -53,12 +65,14 @@ export class DecorationService extends Disposable implements IDecorationService listener.dispose(); if (decoration) { if (this._decorations.delete(decoration)) { + this._removeFromLineIndex(decoration); this._onDecorationRemoved.fire(decoration); } markerDispose.dispose(); } }); this._decorations.insert(decoration); + this._addToLineIndex(decoration); this._onDecorationRegistered.fire(decoration); } return decoration; @@ -69,19 +83,17 @@ export class DecorationService extends Disposable implements IDecorationService d.dispose(); } this._decorations.clear(); + this._decorationsByLine.clear(); } public *getDecorationsAtCell(x: number, line: number, layer?: 'bottom' | 'top'): IterableIterator { + const bucket = this._decorationsByLine.get(line); + if (!bucket) { + return; + } let xmin = 0; let xmax = 0; - let ymin = 0; - let ymax = 0; - for (const d of this._decorations.values()) { - ymin = d.marker.line; - ymax = ymin + (d.options.height ?? 1); - if (line < ymin || line >= ymax) { - continue; - } + for (const d of bucket) { xmin = d.options.x ?? 0; xmax = xmin + (d.options.width ?? 1); if (x >= xmin && x < xmax && (!layer || (d.options.layer ?? 'bottom') === layer)) { @@ -91,12 +103,11 @@ export class DecorationService extends Disposable implements IDecorationService } public forEachDecorationAtCell(x: number, line: number, layer: 'bottom' | 'top' | undefined, callback: (decoration: IInternalDecoration) => void): void { - for (const d of this._decorations.values()) { - $ymin = d.marker.line; - $ymax = $ymin + (d.options.height ?? 1); - if (line < $ymin || line >= $ymax) { - continue; - } + const bucket = this._decorationsByLine.get(line); + if (!bucket) { + return; + } + for (const d of bucket) { $xmin = d.options.x ?? 0; $xmax = $xmin + (d.options.width ?? 1); if (x >= $xmin && x < $xmax && (!layer || (d.options.layer ?? 'bottom') === layer)) { @@ -104,12 +115,147 @@ export class DecorationService extends Disposable implements IDecorationService } } } + + private _attachBufferLineListeners(): void { + const store = new DisposableStore(); + this._bufferLineListeners.value = store; + const lines = this._bufferService.buffer.lines; + store.add(lines.onTrim(amount => this._handleBufferLinesTrim(amount))); + store.add(lines.onInsert(event => this._handleBufferLinesInsert(event))); + store.add(lines.onDelete(event => this._handleBufferLinesDelete(event))); + } + + private _getDecorationHeight(decoration: IInternalDecoration): number { + return decoration.options.height ?? 1; + } + + private _addToLineIndex(decoration: IInternalDecoration): void { + const start = decoration.marker.line; + if (start < 0) { + return; + } + decoration._indexedStartLine = start; + const height = this._getDecorationHeight(decoration); + for (let line = start; line < start + height; line++) { + let bucket = this._decorationsByLine.get(line); + if (!bucket) { + bucket = []; + this._decorationsByLine.set(line, bucket); + } + bucket.push(decoration); + } + } + + private _removeFromLineIndex(decoration: IInternalDecoration): void { + const start = decoration._indexedStartLine; + const height = this._getDecorationHeight(decoration); + for (let line = start; line < start + height; line++) { + const bucket = this._decorationsByLine.get(line); + if (!bucket) { + continue; + } + const index = bucket.indexOf(decoration); + if (index !== -1) { + bucket.splice(index, 1); + } + if (bucket.length === 0) { + this._decorationsByLine.delete(line); + } + } + } + + private _reindexDecoration(decoration: IInternalDecoration): void { + this._removeFromLineIndex(decoration); + if (!decoration.marker.isDisposed && decoration.marker.line >= 0) { + this._addToLineIndex(decoration); + } + } + + private _lineIndexSyncCallbacks: (() => void)[] = []; + + /** Re-index after marker line updates (buffer listeners may run before markers). */ + private _scheduleLineIndexSync(callback: () => void): void { + this._lineIndexSyncCallbacks.push(callback); + if (this._lineIndexSyncCallbacks.length > 1) { + return; + } + queueMicrotask(() => { + const callbacks = this._lineIndexSyncCallbacks; + this._lineIndexSyncCallbacks = []; + for (const cb of callbacks) { + cb(); + } + }); + } + + private _handleBufferLinesTrim(amount: number): void { + if (amount <= 0) { + return; + } + const newMap = new Map(); + for (const [line, bucket] of this._decorationsByLine) { + const newLine = line - amount; + if (newLine < 0) { + continue; + } + const existing = newMap.get(newLine); + if (existing) { + existing.push(...bucket); + } else { + newMap.set(newLine, bucket.slice()); + } + } + this._decorationsByLine.clear(); + for (const [line, bucket] of newMap) { + this._decorationsByLine.set(line, bucket); + } + for (const d of this._decorations.values()) { + if (!d.marker.isDisposed) { + d._indexedStartLine -= amount; + } + } + } + + private _handleBufferLinesInsert(event: IInsertEvent): void { + this._scheduleLineIndexSync(() => { + for (const d of this._decorations.values()) { + if (d.marker.isDisposed) { + continue; + } + const height = this._getDecorationHeight(d); + if (d.marker.line + height > event.index) { + this._reindexDecoration(d); + } + } + }); + } + + private _handleBufferLinesDelete(event: IDeleteEvent): void { + const deleteEnd = event.index + event.amount; + this._scheduleLineIndexSync(() => { + for (const d of this._decorations.values()) { + if (d.marker.isDisposed) { + continue; + } + const start = d.marker.line; + const end = start + this._getDecorationHeight(d); + if (start >= deleteEnd) { + this._reindexDecoration(d); + } else if (start < event.index && end > event.index) { + this._reindexDecoration(d); + } + } + }); + } } class Decoration extends DisposableStore implements IInternalDecoration { public readonly marker: IMarker; public element: HTMLElement | undefined; + /** Start line used for line-index removal when marker.line is cleared on dispose. */ + public _indexedStartLine: number; + public readonly onRenderEmitter = this.add(new Emitter()); public readonly onRender = this.onRenderEmitter.event; private readonly _onDispose = this.add(new Emitter()); @@ -144,6 +290,7 @@ class Decoration extends DisposableStore implements IInternalDecoration { ) { super(); this.marker = options.marker; + this._indexedStartLine = options.marker.line; if (this.options.overviewRulerOptions && !this.options.overviewRulerOptions.position) { this.options.overviewRulerOptions.position = 'full'; } diff --git a/src/common/services/Services.ts b/src/common/services/Services.ts index a15dc23dc..a858193c3 100644 --- a/src/common/services/Services.ts +++ b/src/common/services/Services.ts @@ -396,4 +396,6 @@ export interface IInternalDecoration extends IDecoration { readonly backgroundColorRGB: IColor | undefined; readonly foregroundColorRGB: IColor | undefined; readonly onRenderEmitter: Emitter; + /** @internal Start line for line-index removal; kept in sync on buffer line shifts. */ + _indexedStartLine: number; } diff --git a/test/benchmark/DecorationService.benchmark.ts b/test/benchmark/DecorationService.benchmark.ts new file mode 100644 index 000000000..1a2033de1 --- /dev/null +++ b/test/benchmark/DecorationService.benchmark.ts @@ -0,0 +1,109 @@ +/** + * Copyright (c) 2026 The xterm.js authors. All rights reserved. + * @license MIT + */ + +import { perfContext, before, RuntimeCase } from 'xterm-benchmark'; +import { DecorationService } from 'common/services/DecorationService'; +import { MockLogService, MockBufferService, MockOptionsService } from 'common/TestUtils.test'; +const enum Constants { + COLS = 80, + ROWS = 30, + SINGLE_LINE_DECORATION_COUNT = 20_000, + MULTI_LINE_DECORATION_COUNT = 19_999, + MULTI_LINE_HEIGHT = 2, + VIEWPORT_SCAN_ITERATIONS = 20 +} + +function registerSingleLineDecorations(service: DecorationService, bufferService: MockBufferService, count: number): void { + const buffer = bufferService.buffer; + for (let i = 0; i < count; i++) { + const line = i % 5000; + const marker = buffer.addMarker(line); + service.registerDecoration({ marker, width: Constants.COLS }); + } +} + +function registerMixedDecorations(service: DecorationService, bufferService: MockBufferService): void { + const buffer = bufferService.buffer; + for (let i = 0; i < Constants.MULTI_LINE_DECORATION_COUNT; i++) { + const line = i % 5000; + const marker = buffer.addMarker(line); + service.registerDecoration({ marker, width: Constants.COLS }); + } + const multiLineMarker = buffer.addMarker(10); + service.registerDecoration({ + marker: multiLineMarker, + width: Constants.COLS, + height: Constants.MULTI_LINE_HEIGHT + }); +} + +function scanVisibleGrid(service: DecorationService, rows: number, cols: number): number { + let hitCount = 0; + for (let row = 0; row < rows; row++) { + for (let col = 0; col < cols; col++) { + service.forEachDecorationAtCell(col, row, undefined, () => { + hitCount++; + }); + } + } + return hitCount; +} + +perfContext('DecorationService.forEachDecorationAtCell', () => { + perfContext('single-line dense / sparse line hit', () => { + let service: DecorationService; + let bufferService: MockBufferService; + before(() => { + bufferService = new MockBufferService(Constants.COLS, Constants.ROWS, new MockOptionsService()); + service = new DecorationService(new MockLogService(), bufferService); + registerSingleLineDecorations(service, bufferService, Constants.SINGLE_LINE_DECORATION_COUNT); + }); + new RuntimeCase('', () => { + let hitCount = 0; + for (let i = 0; i < Constants.VIEWPORT_SCAN_ITERATIONS; i++) { + service.forEachDecorationAtCell(0, 0, undefined, () => { + hitCount++; + }); + } + return { payloadSize: Constants.VIEWPORT_SCAN_ITERATIONS, hitCount }; + }, { fork: false }).showAverageRuntime(); + }); + + perfContext('mixed single-line + multi-line / sparse line hit', () => { + let service: DecorationService; + let bufferService: MockBufferService; + before(() => { + bufferService = new MockBufferService(Constants.COLS, Constants.ROWS, new MockOptionsService()); + service = new DecorationService(new MockLogService(), bufferService); + registerMixedDecorations(service, bufferService); + }); + new RuntimeCase('', () => { + let hitCount = 0; + for (let i = 0; i < Constants.VIEWPORT_SCAN_ITERATIONS; i++) { + service.forEachDecorationAtCell(0, 10, undefined, () => { + hitCount++; + }); + } + return { payloadSize: Constants.VIEWPORT_SCAN_ITERATIONS, hitCount }; + }, { fork: false }).showAverageRuntime(); + }); + + perfContext('viewport grid scan', () => { + let service: DecorationService; + let bufferService: MockBufferService; + before(() => { + bufferService = new MockBufferService(Constants.COLS, Constants.ROWS, new MockOptionsService()); + service = new DecorationService(new MockLogService(), bufferService); + registerSingleLineDecorations(service, bufferService, Constants.SINGLE_LINE_DECORATION_COUNT); + }); + new RuntimeCase('', () => { + let totalHits = 0; + for (let i = 0; i < Constants.VIEWPORT_SCAN_ITERATIONS; i++) { + totalHits += scanVisibleGrid(service, Constants.ROWS, Constants.COLS); + } + return { payloadSize: Constants.VIEWPORT_SCAN_ITERATIONS * Constants.ROWS * Constants.COLS, totalHits }; + }, { fork: false }).showAverageRuntime(); + }); +}); From 9e72a4b6d3376609216d6ba34624f9fbc0e6bce6 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 26 May 2026 09:47:11 -0700 Subject: [PATCH 08/47] Refactor decoration line index into DecorationLineCache Extract the per-line lookup index from DecorationService into an exported DecorationLineCache class in the same module, with DecorationService listed first for readability. Optimize buffer insert/delete maintenance to shift map keys in O(unique indexed lines) instead of full re-indexing every affected decoration. Only multi-line decorations that span the mutation point get a full re-index. Add a direct DecorationLineCache unit test in DecorationService.test.ts. Co-authored-by: Cursor --- src/common/services/DecorationService.test.ts | 9 +- src/common/services/DecorationService.ts | 187 +++++++++++++----- 2 files changed, 145 insertions(+), 51 deletions(-) diff --git a/src/common/services/DecorationService.test.ts b/src/common/services/DecorationService.test.ts index b70232f36..e3a6c2f04 100644 --- a/src/common/services/DecorationService.test.ts +++ b/src/common/services/DecorationService.test.ts @@ -4,7 +4,7 @@ */ import { assert } from 'chai'; -import { DecorationService } from './DecorationService'; +import { DecorationLineCache, DecorationService } from './DecorationService'; import { IMarker } from 'common/Types'; import { Disposable } from 'common/Lifecycle'; import { Emitter } from 'common/Event'; @@ -149,6 +149,13 @@ describe('DecorationService', () => { }); }); + describe('DecorationLineCache', () => { + it('should return undefined for lines with no indexed decorations', () => { + const cache = new DecorationLineCache(); + assert.isUndefined(cache.getDecorationsOnLine(0)); + }); + }); + describe('line index maintenance', () => { it('should keep lookups correct after buffer trim', () => { const bufferService = new MockBufferService(80, 5, new MockOptionsService({ scrollback: 0 })); diff --git a/src/common/services/DecorationService.ts b/src/common/services/DecorationService.ts index 2bac9717c..645a63818 100644 --- a/src/common/services/DecorationService.ts +++ b/src/common/services/DecorationService.ts @@ -3,14 +3,14 @@ * @license MIT */ +import type { IDeleteEvent, IInsertEvent } from 'common/CircularList'; import { css } from 'common/Color'; import { Disposable, DisposableStore, MutableDisposable, toDisposable } from 'common/Lifecycle'; import { IBufferService, IDecorationService, IInternalDecoration, ILogService } from 'common/services/Services'; import { SortedList } from 'common/SortedList'; -import { IColor } from 'common/Types'; +import { IColor, ICircularList } from 'common/Types'; import { IDecoration, IDecorationOptions, IMarker } from '@xterm/xterm'; import { Emitter } from 'common/Event'; -import type { IDeleteEvent, IInsertEvent } from 'common/CircularList'; // Work variables to avoid garbage collection let $xmin = 0; @@ -26,13 +26,7 @@ export class DecorationService extends Disposable implements IDecorationService */ private readonly _decorations: SortedList; - /** - * Decorations indexed by buffer line. Multi-line decorations are present in every line bucket - * they span so cell lookup only iterates decorations relevant to that line. - */ - private readonly _decorationsByLine: Map = new Map(); - - private readonly _bufferLineListeners = this._register(new MutableDisposable()); + private readonly _lineCache = this._register(new DecorationLineCache()); private readonly _onDecorationRegistered = this._register(new Emitter()); public readonly onDecorationRegistered = this._onDecorationRegistered.event; @@ -50,8 +44,10 @@ export class DecorationService extends Disposable implements IDecorationService this._decorations = new SortedList(e => e?.marker.line, this._logService); this._register(toDisposable(() => this.reset())); - this._register(this._bufferService.buffers.onBufferActivate(() => this._attachBufferLineListeners())); - this._attachBufferLineListeners(); + this._register(this._bufferService.buffers.onBufferActivate(() => { + this._lineCache.attachToBufferLines(this._bufferService.buffer.lines); + })); + this._lineCache.attachToBufferLines(this._bufferService.buffer.lines); } public registerDecoration(options: IDecorationOptions): IDecoration | undefined { @@ -65,14 +61,14 @@ export class DecorationService extends Disposable implements IDecorationService listener.dispose(); if (decoration) { if (this._decorations.delete(decoration)) { - this._removeFromLineIndex(decoration); + this._lineCache.remove(decoration); this._onDecorationRemoved.fire(decoration); } markerDispose.dispose(); } }); this._decorations.insert(decoration); - this._addToLineIndex(decoration); + this._lineCache.add(decoration); this._onDecorationRegistered.fire(decoration); } return decoration; @@ -83,11 +79,11 @@ export class DecorationService extends Disposable implements IDecorationService d.dispose(); } this._decorations.clear(); - this._decorationsByLine.clear(); + this._lineCache.clear(); } public *getDecorationsAtCell(x: number, line: number, layer?: 'bottom' | 'top'): IterableIterator { - const bucket = this._decorationsByLine.get(line); + const bucket = this._lineCache.getDecorationsOnLine(line); if (!bucket) { return; } @@ -103,7 +99,7 @@ export class DecorationService extends Disposable implements IDecorationService } public forEachDecorationAtCell(x: number, line: number, layer: 'bottom' | 'top' | undefined, callback: (decoration: IInternalDecoration) => void): void { - const bucket = this._decorationsByLine.get(line); + const bucket = this._lineCache.getDecorationsOnLine(line); if (!bucket) { return; } @@ -115,11 +111,44 @@ export class DecorationService extends Disposable implements IDecorationService } } } +} - private _attachBufferLineListeners(): void { +/** + * Per-logical-line index of decorations for fast cell lookup. + * + * Keys are marker.line coordinates (logical buffer lines), not CircularList ring slots. + * Multi-line decorations appear in every line bucket they span. The index is kept aligned + * with marker.line updates via buffer line trim/insert/delete events. + */ +export class DecorationLineCache extends Disposable { + private readonly _decorationsByLine: Map = new Map(); + private readonly _decorations = new Set(); + private readonly _bufferLineListeners = this._register(new MutableDisposable()); + + private _lineIndexSyncCallbacks: (() => void)[] = []; + + public clear(): void { + this._decorationsByLine.clear(); + this._decorations.clear(); + } + + public add(decoration: IInternalDecoration): void { + this._decorations.add(decoration); + this._addToLineBuckets(decoration); + } + + public remove(decoration: IInternalDecoration): void { + this._decorations.delete(decoration); + this._removeFromLineBuckets(decoration); + } + + public getDecorationsOnLine(line: number): ReadonlyArray | undefined { + return this._decorationsByLine.get(line); + } + + public attachToBufferLines(lines: ICircularList): void { const store = new DisposableStore(); this._bufferLineListeners.value = store; - const lines = this._bufferService.buffer.lines; store.add(lines.onTrim(amount => this._handleBufferLinesTrim(amount))); store.add(lines.onInsert(event => this._handleBufferLinesInsert(event))); store.add(lines.onDelete(event => this._handleBufferLinesDelete(event))); @@ -129,7 +158,7 @@ export class DecorationService extends Disposable implements IDecorationService return decoration.options.height ?? 1; } - private _addToLineIndex(decoration: IInternalDecoration): void { + private _addToLineBuckets(decoration: IInternalDecoration): void { const start = decoration.marker.line; if (start < 0) { return; @@ -146,7 +175,7 @@ export class DecorationService extends Disposable implements IDecorationService } } - private _removeFromLineIndex(decoration: IInternalDecoration): void { + private _removeFromLineBuckets(decoration: IInternalDecoration): void { const start = decoration._indexedStartLine; const height = this._getDecorationHeight(decoration); for (let line = start; line < start + height; line++) { @@ -165,14 +194,12 @@ export class DecorationService extends Disposable implements IDecorationService } private _reindexDecoration(decoration: IInternalDecoration): void { - this._removeFromLineIndex(decoration); + this._removeFromLineBuckets(decoration); if (!decoration.marker.isDisposed && decoration.marker.line >= 0) { - this._addToLineIndex(decoration); + this._addToLineBuckets(decoration); } } - private _lineIndexSyncCallbacks: (() => void)[] = []; - /** Re-index after marker line updates (buffer listeners may run before markers). */ private _scheduleLineIndexSync(callback: () => void): void { this._lineIndexSyncCallbacks.push(callback); @@ -209,7 +236,7 @@ export class DecorationService extends Disposable implements IDecorationService for (const [line, bucket] of newMap) { this._decorationsByLine.set(line, bucket); } - for (const d of this._decorations.values()) { + for (const d of this._decorations) { if (!d.marker.isDisposed) { d._indexedStartLine -= amount; } @@ -217,35 +244,95 @@ export class DecorationService extends Disposable implements IDecorationService } private _handleBufferLinesInsert(event: IInsertEvent): void { - this._scheduleLineIndexSync(() => { - for (const d of this._decorations.values()) { - if (d.marker.isDisposed) { - continue; - } - const height = this._getDecorationHeight(d); - if (d.marker.line + height > event.index) { - this._reindexDecoration(d); - } - } - }); + this._scheduleLineIndexSync(() => this._applyBufferLinesInsert(event)); } private _handleBufferLinesDelete(event: IDeleteEvent): void { - const deleteEnd = event.index + event.amount; - this._scheduleLineIndexSync(() => { - for (const d of this._decorations.values()) { - if (d.marker.isDisposed) { - continue; - } - const start = d.marker.line; - const end = start + this._getDecorationHeight(d); - if (start >= deleteEnd) { - this._reindexDecoration(d); - } else if (start < event.index && end > event.index) { - this._reindexDecoration(d); - } + this._scheduleLineIndexSync(() => this._applyBufferLinesDelete(event)); + } + + private _mergeLineBucket(newMap: Map, line: number, bucket: IInternalDecoration[]): void { + const existing = newMap.get(line); + if (existing) { + existing.push(...bucket); + } else { + newMap.set(line, bucket.slice()); + } + } + + /** + * Shift indexed line keys and sync start lines. O(unique indexed lines), not O(decoration count). + * Decorations that span the insert point are re-indexed individually (rare vs single-line hits). + */ + private _applyBufferLinesInsert(event: IInsertEvent): void { + const { index, amount } = event; + const spanCrossers: IInternalDecoration[] = []; + for (const d of this._decorations) { + if (d.marker.isDisposed) { + continue; } - }); + const start = d._indexedStartLine; + if (start < index && start + this._getDecorationHeight(d) > index) { + spanCrossers.push(d); + this._removeFromLineBuckets(d); + } + } + const newMap = new Map(); + for (const [line, bucket] of this._decorationsByLine) { + const newLine = line >= index ? line + amount : line; + this._mergeLineBucket(newMap, newLine, bucket); + } + this._decorationsByLine.clear(); + for (const [line, bucket] of newMap) { + this._decorationsByLine.set(line, bucket); + } + for (const d of this._decorations) { + if (d.marker.isDisposed) { + continue; + } + if (d._indexedStartLine >= index) { + d._indexedStartLine = d.marker.line; + } + } + for (const d of spanCrossers) { + this._addToLineBuckets(d); + } + } + + /** + * Drop deleted line keys, shift keys below, sync start lines. Full re-index only when a + * multi-line decoration spans across the deleted range but survives. + */ + private _applyBufferLinesDelete(event: IDeleteEvent): void { + const deleteEnd = event.index + event.amount; + const newMap = new Map(); + for (const [line, bucket] of this._decorationsByLine) { + if (line >= event.index && line < deleteEnd) { + continue; + } + const newLine = line >= deleteEnd ? line - event.amount : line; + this._mergeLineBucket(newMap, newLine, bucket); + } + this._decorationsByLine.clear(); + for (const [line, bucket] of newMap) { + this._decorationsByLine.set(line, bucket); + } + const toReindex: IInternalDecoration[] = []; + for (const d of this._decorations) { + if (d.marker.isDisposed) { + continue; + } + const start = d._indexedStartLine; + const height = this._getDecorationHeight(d); + if (start >= deleteEnd) { + d._indexedStartLine = d.marker.line; + } else if (start < event.index && start + height > deleteEnd) { + toReindex.push(d); + } + } + for (const d of toReindex) { + this._reindexDecoration(d); + } } } From 4a0cb5ae6bd46bc7870d1c8351b754a57111e2d2 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 26 May 2026 09:52:53 -0700 Subject: [PATCH 09/47] Add MicrotaskTimer and use it for decoration line index sync Introduce MicrotaskTimer as a TimeoutTimer-style helper that schedules a single runner via queueMicrotask, with set and cancel but no cancelAndSet. Use it in DecorationLineCache to batch insert/delete line-index updates after marker line adjustments, replacing ad-hoc queueMicrotask scheduling. Co-authored-by: Cursor --- src/common/Async.ts | 36 ++++++++++++++++++++++++ src/common/services/DecorationService.ts | 10 +++---- 2 files changed, 41 insertions(+), 5 deletions(-) diff --git a/src/common/Async.ts b/src/common/Async.ts index 7345f19da..276532fb8 100644 --- a/src/common/Async.ts +++ b/src/common/Async.ts @@ -73,6 +73,42 @@ export class TimeoutTimer implements IDisposable { } } +/** + * Schedules a single runner on the microtask queue. Unlike {@link TimeoutTimer}, a scheduled + * microtask cannot be unqueued; {@link cancel} prevents the runner from executing if it has not + * run yet. + */ +export class MicrotaskTimer implements IDisposable { + private _isScheduled = false; + private _isDisposed = false; + + public dispose(): void { + this.cancel(); + this._isDisposed = true; + } + + public cancel(): void { + this._isScheduled = false; + } + + public set(runner: () => void): void { + if (this._isDisposed) { + throw new Error('Calling set on a disposed MicrotaskTimer'); + } + if (this._isScheduled) { + return; + } + this._isScheduled = true; + queueMicrotask(() => { + if (!this._isScheduled) { + return; + } + this._isScheduled = false; + runner(); + }); + } +} + export class IntervalTimer implements IDisposable { private _disposable: IDisposable | undefined; private _isDisposed = false; diff --git a/src/common/services/DecorationService.ts b/src/common/services/DecorationService.ts index 645a63818..a582c18af 100644 --- a/src/common/services/DecorationService.ts +++ b/src/common/services/DecorationService.ts @@ -4,6 +4,7 @@ */ import type { IDeleteEvent, IInsertEvent } from 'common/CircularList'; +import { MicrotaskTimer } from 'common/Async'; import { css } from 'common/Color'; import { Disposable, DisposableStore, MutableDisposable, toDisposable } from 'common/Lifecycle'; import { IBufferService, IDecorationService, IInternalDecoration, ILogService } from 'common/services/Services'; @@ -124,10 +125,12 @@ export class DecorationLineCache extends Disposable { private readonly _decorationsByLine: Map = new Map(); private readonly _decorations = new Set(); private readonly _bufferLineListeners = this._register(new MutableDisposable()); - + private readonly _lineIndexSyncTimer = this._register(new MicrotaskTimer()); private _lineIndexSyncCallbacks: (() => void)[] = []; public clear(): void { + this._lineIndexSyncCallbacks.length = 0; + this._lineIndexSyncTimer.cancel(); this._decorationsByLine.clear(); this._decorations.clear(); } @@ -203,10 +206,7 @@ export class DecorationLineCache extends Disposable { /** Re-index after marker line updates (buffer listeners may run before markers). */ private _scheduleLineIndexSync(callback: () => void): void { this._lineIndexSyncCallbacks.push(callback); - if (this._lineIndexSyncCallbacks.length > 1) { - return; - } - queueMicrotask(() => { + this._lineIndexSyncTimer.set(() => { const callbacks = this._lineIndexSyncCallbacks; this._lineIndexSyncCallbacks = []; for (const cb of callbacks) { From d195dc9a3b2222b49970121f75879c8bccd986ec Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Tue, 26 May 2026 18:03:15 +0000 Subject: [PATCH 10/47] Add space-unary-ops lint rule and fix SixelHandler violation Enable @stylistic/space-unary-ops to warn on spaces after unary operators like ! in conditions. Fix existing violation in SixelHandler. Fixes #5898 Co-authored-by: Daniel Imms --- addons/addon-image/src/SixelHandler.ts | 2 +- eslint.config.mjs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/addons/addon-image/src/SixelHandler.ts b/addons/addon-image/src/SixelHandler.ts index 2f12fbe19..1af2d85bc 100644 --- a/addons/addon-image/src/SixelHandler.ts +++ b/addons/addon-image/src/SixelHandler.ts @@ -91,7 +91,7 @@ export class SixelHandler implements IDcsHandler, IResetHandler { const height = this._dec.height; // partial fix for https://github.com/jerch/xterm-addon-image/issues/37 - if (!width || ! height) { + if (!width || !height) { if (height) { this._storage.advanceCursor(height); } diff --git a/eslint.config.mjs b/eslint.config.mjs index b73eece7a..6c2f80e30 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -43,6 +43,7 @@ export default tseslint.config( singleline: { delimiter: 'comma', requireLast: false } }], '@stylistic/type-annotation-spacing': 'warn', + '@stylistic/space-unary-ops': 'warn', '@typescript-eslint/array-type': ['warn', { default: 'array', readonly: 'generic' }], '@typescript-eslint/consistent-type-assertions': 'warn', From 4ec85e67baeaa767bed73f68b839e71a2cc5ebb4 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Tue, 26 May 2026 20:15:55 +0000 Subject: [PATCH 11/47] Add multiline-comment-style lint rule and fix violations Enable multiline-comment-style with starred-block to require JSDoc-style block comments with content on separate lines from the opening delimiter. Auto-fix existing violations across the codebase. Fixes #5899 Co-authored-by: Daniel Imms --- addons/addon-fit/test/FitAddon.test.ts | 32 +- addons/addon-image/src/IIPHeaderParser.ts | 6 +- addons/addon-image/src/IIPMetrics.ts | 6 +- addons/addon-image/src/ImageAddon.ts | 30 +- addons/addon-image/src/ImageRenderer.ts | 36 +- addons/addon-image/src/ImageStorage.ts | 48 +- addons/addon-image/src/SixelHandler.ts | 12 +- .../src/kitty/KittyGraphicsHandler.ts | 146 ++-- .../src/kitty/KittyGraphicsTypes.ts | 30 +- .../src/kitty/KittyImageStorage.ts | 46 +- addons/addon-image/test/ImageAddon.test.ts | 6 +- addons/addon-image/test/KittyGraphics.test.ts | 90 +- addons/addon-ligatures/src/font.ts | 16 +- .../src/fontLigatures/index.ts | 28 +- .../src/fontLigatures/merge.ts | 40 +- .../src/fontLigatures/mergeRange.ts | 30 +- .../src/fontLigatures/processors/6-2.ts | 6 +- .../src/fontLigatures/processors/6-3.ts | 6 +- .../src/fontLigatures/processors/helper.ts | 12 +- .../fontLigatures/processors/substitution.ts | 6 +- addons/addon-ligatures/src/index.ts | 24 +- addons/addon-ligatures/src/parse.ts | 18 +- addons/addon-search/src/SearchEngine.ts | 18 +- addons/addon-search/src/SearchLineCache.ts | 6 +- addons/addon-serialize/src/SerializeAddon.ts | 104 ++- .../test/SerializeAddon.test.ts | 28 +- .../src/UnicodeGraphemeProvider.ts | 8 +- .../test/WebFontsAddon.test.ts | 18 +- addons/addon-web-links/src/WebLinkProvider.ts | 18 +- addons/addon-web-links/src/WebLinksAddon.ts | 24 +- addons/addon-webgl/src/CellColorResolver.ts | 18 +- addons/addon-webgl/src/CharAtlasCache.ts | 6 +- addons/addon-webgl/src/CharAtlasUtils.ts | 6 +- addons/addon-webgl/src/Constants.ts | 8 +- .../src/CursorBlinkStateManager.ts | 22 +- addons/addon-webgl/src/DevicePixelObserver.ts | 10 +- addons/addon-webgl/src/GlyphRenderer.ts | 60 +- addons/addon-webgl/src/RectangleRenderer.ts | 8 +- addons/addon-webgl/src/TextureAtlas.ts | 206 +++-- addons/addon-webgl/src/WebglRenderer.ts | 164 ++-- .../customGlyphs/CustomGlyphDefinitions.ts | 68 +- .../src/customGlyphs/CustomGlyphRasterizer.ts | 18 +- addons/addon-webgl/test/WebglRenderer.test.ts | 8 +- demo/client/client.ts | 20 +- .../components/window/addonImageWindow.ts | 22 +- .../components/window/addonProgressWindow.ts | 12 +- demo/client/components/window/testWindow.ts | 76 +- demo/client/unicodeTable.ts | 6 +- demo/server/server.ts | 14 +- eslint.config.mjs | 1 + src/browser/AccessibilityManager.ts | 20 +- src/browser/Clipboard.ts | 6 +- src/browser/CoreBrowserTerminal.ts | 140 +-- src/browser/Linkifier.ts | 40 +- src/browser/OscLinkProvider.ts | 12 +- src/browser/Terminal.test.ts | 24 +- src/browser/TimeBasedDebouncer.ts | 6 +- src/browser/Types.ts | 6 +- src/browser/Viewport.ts | 32 +- .../decorations/BufferDecorationRenderer.ts | 6 +- src/browser/decorations/ColorZoneStore.ts | 14 +- .../decorations/OverviewRulerRenderer.ts | 12 +- src/browser/input/CompositionHelper.test.ts | 8 +- src/browser/input/CompositionHelper.ts | 86 +- src/browser/input/Mouse.ts | 8 +- src/browser/public/Terminal.ts | 8 +- src/browser/renderer/dom/DomRenderer.ts | 22 +- .../dom/DomRendererRowFactory.test.ts | 6 +- .../renderer/dom/DomRendererRowFactory.ts | 76 +- src/browser/renderer/dom/WidthCache.ts | 20 +- .../renderer/shared/RendererUtils.test.ts | 18 +- src/browser/renderer/shared/RendererUtils.ts | 14 +- src/browser/scrollable/abstractScrollbar.ts | 6 +- src/browser/scrollable/fastDomNode.ts | 6 +- .../scrollable/globalPointerMoveMonitor.ts | 6 +- src/browser/scrollable/horizontalScrollbar.ts | 6 +- src/browser/scrollable/mouseEvent.ts | 6 +- src/browser/scrollable/scrollable.ts | 6 +- src/browser/scrollable/scrollableElement.ts | 6 +- .../scrollable/scrollableElementOptions.ts | 6 +- src/browser/scrollable/scrollbarArrow.ts | 6 +- src/browser/scrollable/scrollbarState.ts | 6 +- .../scrollbarVisibilityController.ts | 6 +- src/browser/scrollable/touch.ts | 6 +- src/browser/scrollable/verticalScrollbar.ts | 6 +- src/browser/scrollable/widget.ts | 6 +- src/browser/services/CharSizeService.ts | 6 +- .../services/CharacterJoinerService.ts | 94 +- src/browser/services/MouseService.test.ts | 6 +- src/browser/services/MouseService.ts | 38 +- src/browser/services/RenderService.ts | 34 +- src/browser/services/SelectionService.test.ts | 12 +- src/browser/services/SelectionService.ts | 158 ++-- src/common/CircularList.ts | 6 +- src/common/Color.ts | 34 +- src/common/CoreTerminal.ts | 6 +- src/common/InputHandler.test.ts | 12 +- src/common/InputHandler.ts | 324 ++++--- src/common/Platform.ts | 22 +- src/common/SortedList.ts | 6 +- src/common/TaskQueue.ts | 20 +- src/common/Types.ts | 46 +- src/common/WindowsMode.ts | 22 +- src/common/buffer/Buffer.test.ts | 112 ++- src/common/buffer/Buffer.ts | 102 ++- src/common/buffer/BufferLine.test.ts | 34 +- src/common/buffer/BufferLine.ts | 34 +- src/common/buffer/BufferReflow.ts | 26 +- src/common/buffer/BufferSet.ts | 20 +- src/common/buffer/CellData.ts | 6 +- src/common/input/Keyboard.test.ts | 6 +- src/common/input/Keyboard.ts | 36 +- src/common/input/KittyKeyboard.test.ts | 12 +- src/common/input/KittyKeyboard.ts | 32 +- src/common/input/TextDecoder.ts | 12 +- src/common/input/UnicodeV6.test.ts | 32 +- src/common/input/UnicodeV6.ts | 18 +- src/common/input/Win32InputMode.ts | 14 +- src/common/input/WriteBuffer.ts | 64 +- src/common/input/XParseColor.ts | 8 +- src/common/parser/ApcParser.ts | 6 +- src/common/parser/DcsParser.ts | 16 +- .../parser/EscapeSequenceParser.test.ts | 18 +- src/common/parser/EscapeSequenceParser.ts | 82 +- src/common/parser/OscParser.ts | 22 +- src/common/services/BufferService.ts | 18 +- src/common/services/InstantiationService.ts | 6 +- src/common/services/LogService.ts | 6 +- src/common/services/MouseStateService.ts | 16 +- src/common/services/OptionsService.ts | 6 +- src/common/services/ServiceRegistry.ts | 6 +- src/common/services/UnicodeService.ts | 18 +- src/headless/public/Terminal.ts | 8 +- test/playwright/InputHandler.test.ts | 28 +- test/playwright/MouseTracking.test.ts | 810 ++++++++++-------- test/playwright/SharedRendererTests.ts | 30 +- test/playwright/Terminal.test.ts | 36 +- test/playwright/TestUtils.ts | 12 +- 138 files changed, 3040 insertions(+), 1837 deletions(-) diff --git a/addons/addon-fit/test/FitAddon.test.ts b/addons/addon-fit/test/FitAddon.test.ts index 6a7c41fa8..d82d3640a 100644 --- a/addons/addon-fit/test/FitAddon.test.ts +++ b/addons/addon-fit/test/FitAddon.test.ts @@ -26,18 +26,24 @@ test.describe('FitAddon', () => { `); }); - // test.beforeEach(async function(): Promise { - // await ctx.page.evaluate(`document.querySelector('#terminal-container').style.display=''`); - // await openTerminal(page); - // }); + /* + * test.beforeEach(async function(): Promise { + * await ctx.page.evaluate(`document.querySelector('#terminal-container').style.display=''`); + * await openTerminal(page); + * }); + */ - // after(async () => { - // await browser.close(); - // }); + /* + * after(async () => { + * await browser.close(); + * }); + */ - // afterEach(async function(): Promise { - // await ctx.proxy.dispose(); - // }); + /* + * afterEach(async function(): Promise { + * await ctx.proxy.dispose(); + * }); + */ test('no terminal', async function(): Promise { await ctx.page.evaluate(`window.fit2 = new FitAddon();`); @@ -79,8 +85,10 @@ test.describe('FitAddon', () => { await ctx.page.evaluate(`window.term.open(document.querySelector('#terminal-container'))`); await setDimensions(); const dimensions: { cols: number, rows: number } | undefined = await ctx.page.evaluate(`window.fit.proposeDimensions()`); - // The value of dims will be undefined if the char measure strategy falls back to the DOM - // method, so only assert if it's not undefined. + /* + * The value of dims will be undefined if the char measure strategy falls back to the DOM + * method, so only assert if it's not undefined. + */ if (dimensions) { ok(dimensions.cols > 85); ok(dimensions.cols < 88); diff --git a/addons/addon-image/src/IIPHeaderParser.ts b/addons/addon-image/src/IIPHeaderParser.ts index af4e9a68d..74e753289 100644 --- a/addons/addon-image/src/IIPHeaderParser.ts +++ b/addons/addon-image/src/IIPHeaderParser.ts @@ -24,8 +24,10 @@ export interface IHeaderFields { height?: string; // Optional, defaults to 1 respecting aspect ratio (width takes precedence). preserveAspectRatio?: number; - // Optional, defaults to 0. If set to 1, the file will be displayed inline, else downloaded - // (download not supported). + /* + * Optional, defaults to 0. If set to 1, the file will be displayed inline, else downloaded + * (download not supported). + */ inline?: number; } diff --git a/addons/addon-image/src/IIPMetrics.ts b/addons/addon-image/src/IIPMetrics.ts index b377cf061..1937a2740 100644 --- a/addons/addon-image/src/IIPMetrics.ts +++ b/addons/addon-image/src/IIPMetrics.ts @@ -23,8 +23,10 @@ export function imageType(d: Uint8Array): IMetrics { return UNSUPPORTED_TYPE; } const d32 = new Uint32Array(d.buffer, d.byteOffset, 6); - // PNG: 89 50 4E 47 0D 0A 1A 0A (8 first bytes == magic number for PNG) - // + first chunk must be IHDR + /* + * PNG: 89 50 4E 47 0D 0A 1A 0A (8 first bytes == magic number for PNG) + * + first chunk must be IHDR + */ if (d32[0] === 0x474E5089 && d32[1] === 0x0A1A0A0D && d32[3] === 0x52444849) { return { mime: 'image/png', diff --git a/addons/addon-image/src/ImageAddon.ts b/addons/addon-image/src/ImageAddon.ts index 7e0f29d3f..930a74bd8 100644 --- a/addons/addon-image/src/ImageAddon.ts +++ b/addons/addon-image/src/ImageAddon.ts @@ -137,11 +137,13 @@ export class ImageAddon implements ITerminalAddon, IImageApi { // enable size reports if (this._opts.enableSizeReports) { - // const windowOptions = terminal.getOption('windowOptions'); - // windowOptions.getWinSizePixels = true; - // windowOptions.getCellSizePixels = true; - // windowOptions.getWinSizeChars = true; - // terminal.setOption('windowOptions', windowOptions); + /* + * const windowOptions = terminal.getOption('windowOptions'); + * windowOptions.getWinSizePixels = true; + * windowOptions.getCellSizePixels = true; + * windowOptions.getWinSizeChars = true; + * terminal.setOption('windowOptions', windowOptions); + */ const windowOps = terminal.options.windowOptions ?? {}; windowOps.getWinSizePixels = true; windowOps.getCellSizePixels = true; @@ -290,11 +292,13 @@ export class ImageAddon implements ITerminalAddon, IImageApi { if (params[0]) { return true; } - // reported features: - // 62 - VT220 - // 4 - SIXEL support - // 9 - charsets - // 22 - ANSI colors + /* + * reported features: + * 62 - VT220 + * 4 - SIXEL support + * 9 - charsets + * 22 - ANSI colors + */ if (this._opts.sixelSupport) { this._report(`\x1b[?62;4;9;22c`); return true; @@ -352,8 +356,10 @@ export class ImageAddon implements ITerminalAddon, IImageApi { let width = this._renderer?.dimensions?.css.canvas.width; let height = this._renderer?.dimensions?.css.canvas.height; if (!width || !height) { - // for some reason we have no working image renderer - // --> fallback to default cell size + /* + * for some reason we have no working image renderer + * --> fallback to default cell size + */ const cellSize = CELL_SIZE_DEFAULT; width = (this._terminal?.cols || 80) * cellSize.width; height = (this._terminal?.rows || 24) * cellSize.height; diff --git a/addons/addon-image/src/ImageRenderer.ts b/addons/addon-image/src/ImageRenderer.ts index b36cdd004..b00002ca2 100644 --- a/addons/addon-image/src/ImageRenderer.ts +++ b/addons/addon-image/src/ImageRenderer.ts @@ -197,10 +197,12 @@ export class ImageRenderer extends Disposable implements IDisposable { const finalWidth = count * width + sx > img.width ? img.width - sx : count * width; const finalHeight = sy + height > img.height ? img.height - sy : height; - // Floor all pixel offsets to get stable tile mapping without any overflows. - // Note: For not pixel perfect aligned cells like in the DOM renderer - // this will move a tile slightly to the top/left (subpixel range, thus ignore it). - // FIX #34: avoid striping on displays with pixelDeviceRatio != 1 by ceiling height and width + /* + * Floor all pixel offsets to get stable tile mapping without any overflows. + * Note: For not pixel perfect aligned cells like in the DOM renderer + * this will move a tile slightly to the top/left (subpixel range, thus ignore it). + * FIX #34: avoid striping on displays with pixelDeviceRatio != 1 by ceiling height and width + */ ctx.drawImage( img, Math.floor(sx), Math.floor(sy), Math.ceil(finalWidth), Math.ceil(finalHeight), @@ -342,21 +344,27 @@ export class ImageRenderer extends Disposable implements IDisposable { ); canvas.classList.add(`xterm-image-layer-${layer}`); const screenElement = this._terminal._core.screenElement; - // Use isolation to create a stacking context without overriding z-index, - // which would conflict with integrators (e.g. VS Code) that set their - // own z-index on the screen element. + /* + * Use isolation to create a stacking context without overriding z-index, + * which would conflict with integrators (e.g. VS Code) that set their + * own z-index on the screen element. + */ screenElement.style.isolation = 'isolate'; if (layer === 'bottom') { - // Use z-index:-1 so it paints behind non-positioned text elements. - // The screen element needs to be a stacking context (via isolation) - // to contain the negative z-index, otherwise it would go behind the - // entire terminal. + /* + * Use z-index:-1 so it paints behind non-positioned text elements. + * The screen element needs to be a stacking context (via isolation) + * to contain the negative z-index, otherwise it would go behind the + * entire terminal. + */ canvas.style.zIndex = '-1'; screenElement.insertBefore(canvas, screenElement.firstChild); } else { - // Explicit z-index ensures the image canvas reliably stacks above - // the text layer (DOM renderer rows). z-index: 0 is below the - // selection overlay (z-index: 1). + /* + * Explicit z-index ensures the image canvas reliably stacks above + * the text layer (DOM renderer rows). z-index: 0 is below the + * selection overlay (z-index: 1). + */ canvas.style.zIndex = '0'; screenElement.appendChild(canvas); } diff --git a/addons/addon-image/src/ImageStorage.ts b/addons/addon-image/src/ImageStorage.ts index 8c89a16b0..3e3a7c298 100644 --- a/addons/addon-image/src/ImageStorage.ts +++ b/addons/addon-image/src/ImageStorage.ts @@ -154,8 +154,10 @@ export class ImageStorage implements IDisposable { for (const spec of this._images.values()) { spec.marker?.dispose(); } - // NOTE: marker.dispose above already calls ImageBitmap.close - // therefore we can just wipe the map here + /* + * NOTE: marker.dispose above already calls ImageBitmap.close + * therefore we can just wipe the map here + */ this._images.clear(); this._renderer.clearAll(); } @@ -306,8 +308,10 @@ export class ImageStorage implements IDisposable { this._delImg(id); } - // eviction marker: - // delete the image when the marker gets disposed + /* + * eviction marker: + * delete the image when the marker gets disposed + */ const endMarker = this._terminal.registerMarker(0); endMarker?.onDispose(() => { const spec = this._images.get(imageId); @@ -316,8 +320,10 @@ export class ImageStorage implements IDisposable { } }); - // since markers do not work on alternate for some reason, - // we evict images here manually + /* + * since markers do not work on alternate for some reason, + * we evict images here manually + */ if (this._terminal.buffer.active.type === 'alternate') { this._evictOnAlternate(); } @@ -416,10 +422,12 @@ export class ImageStorage implements IDisposable { const drawCalls: { imgSpec: IImageSpec, tileId: number, col: number, row: number, count: number }[] = []; const placeholderCalls: { col: number, row: number, count: number }[] = []; - // walk all cells in viewport and collect tiles found - // Note: We check _extendedAttrs directly (not just HAS_EXTENDED flag) - // because text writes clear the BG flag but leave image tile data intact. - // This lets top-layer images survive text overwrites (kitty C=1 behavior). + /* + * walk all cells in viewport and collect tiles found + * Note: We check _extendedAttrs directly (not just HAS_EXTENDED flag) + * because text writes clear the BG flag but leave image tile data intact. + * This lets top-layer images survive text overwrites (kitty C=1 behavior). + */ for (let row = start; row <= end; ++row) { const line = buffer.lines.get(row + buffer.ydisp) as IBufferLineExt; if (!line) return; @@ -492,8 +500,10 @@ export class ImageStorage implements IDisposable { return; } - // handle only viewport width enlargements, exit all other cases - // TODO: needs patch for tile counter + /* + * handle only viewport width enlargements, exit all other cases + * TODO: needs patch for tile counter + */ if (this._viewportMetrics.cols >= metrics.cols) { this._viewportMetrics = metrics; return; @@ -581,8 +591,10 @@ export class ImageStorage implements IDisposable { } } - // TODO: Do we need some blob offloading tricks here to avoid early eviction? - // also see https://stackoverflow.com/questions/28307789/is-there-any-limitation-on-javascript-max-blob-size + /* + * TODO: Do we need some blob offloading tricks here to avoid early eviction? + * also see https://stackoverflow.com/questions/28307789/is-there-any-limitation-on-javascript-max-blob-size + */ private _evictOldest(room: number): number { const used = this._getStoredPixels(); let current = used; @@ -605,9 +617,11 @@ export class ImageStorage implements IDisposable { const old = line._extendedAttrs[x]; if (old) { if (old.imageId !== undefined) { - // found an old ExtendedAttrsImage, since we know that - // they are always isolated instances (single cell usage), - // we can re-use it and just update their id entries + /* + * found an old ExtendedAttrsImage, since we know that + * they are always isolated instances (single cell usage), + * we can re-use it and just update their id entries + */ const oldSpec = this._images.get(old.imageId); if (oldSpec) { // early eviction for in-viewport overwrites diff --git a/addons/addon-image/src/SixelHandler.ts b/addons/addon-image/src/SixelHandler.ts index 1af2d85bc..da4798bf6 100644 --- a/addons/addon-image/src/SixelHandler.ts +++ b/addons/addon-image/src/SixelHandler.ts @@ -113,13 +113,17 @@ export class SixelHandler implements IDcsHandler, IResetHandler { * Some helpers to extract current terminal colors. */ -// get currently active background color from terminal -// also respect INVERSE setting +/* + * get currently active background color from terminal + * also respect INVERSE setting + */ function extractActiveBg(attr: AttributeData, colors: ReadonlyColorSet | undefined): RGBA8888 { let bg = 0; if (!colors) { - // FIXME: theme service is prolly not available yet, - // happens if .open() was not called yet (bug in core?) + /* + * FIXME: theme service is prolly not available yet, + * happens if .open() was not called yet (bug in core?) + */ return bg; } if (attr.isInverse()) { diff --git a/addons/addon-image/src/kitty/KittyGraphicsHandler.ts b/addons/addon-image/src/kitty/KittyGraphicsHandler.ts index de889dfff..8ad8ef794 100644 --- a/addons/addon-image/src/kitty/KittyGraphicsHandler.ts +++ b/addons/addon-image/src/kitty/KittyGraphicsHandler.ts @@ -62,9 +62,11 @@ export class KittyGraphicsHandler implements IApcHandler, IResetHandler, IDispos // Storage related states private _pendingTransmissions: Map = new Map(); - // Tracks the pending key of the most recently started chunked upload. - // Per spec, subsequent chunks only need m= (and optionally q=), without i=. - // When a chunk arrives with no i=, this key is used to find the pending upload. + /* + * Tracks the pending key of the most recently started chunked upload. + * Per spec, subsequent chunks only need m= (and optionally q=), without i=. + * When a chunk arrives with no i=, this key is used to find the pending upload. + */ private _lastPendingKey: number | undefined; constructor( @@ -173,9 +175,11 @@ export class KittyGraphicsHandler implements IApcHandler, IResetHandler, IDispos private _streamPayload(data: Uint32Array, start: number, end: number): void { if (this._aborted) return; - // Check size limit (compare encoded bytes against pre-calculated limit) - // Include cumulative size from pending transmission for multi-chunk images. - // Per spec, subsequent chunks may omit i=, so fall back to _lastPendingKey. + /* + * Check size limit (compare encoded bytes against pre-calculated limit) + * Include cumulative size from pending transmission for multi-chunk images. + * Per spec, subsequent chunks may omit i=, so fall back to _lastPendingKey. + */ const pendingKey = this._parsedCommand?.id ?? this._lastPendingKey ?? 0; const pending = this._pendingTransmissions.get(pendingKey); const previousEncodedSize = pending?.totalEncodedSize ?? 0; @@ -285,9 +289,11 @@ export class KittyGraphicsHandler implements IApcHandler, IResetHandler, IDispos } this._activeDecoder = null; - // Handle command first — handlers create Blob/ImageData from imageBytes, - // which copies the data. Only then is it safe to release the decoder's - // wasm memory that imageBytes points into. + /* + * Handle command first — handlers create Blob/ImageData from imageBytes, + * which copies the data. Only then is it safe to release the decoder's + * wasm memory that imageBytes points into. + */ const result = this._handleCommandWithBytesAndCmd(finalCmd, imageBytes, decodeError); if (decoder) { decoder.release(); @@ -325,10 +331,12 @@ export class KittyGraphicsHandler implements IApcHandler, IResetHandler, IDispos case KittyAction.PLACEMENT: return this._handlePlacement(cmd); default: - // TODO: Implement remaining actions when needed: - // - a=f (frame): animation frame operations - // - a=a (animation): animation control - // - a=c (compose): compose images + /* + * TODO: Implement remaining actions when needed: + * - a=f (frame): animation frame operations + * - a=a (animation): animation control + * - a=c (compose): compose images + */ if (cmd.id !== undefined) { this._sendResponse(cmd.id, 'EINVAL:unsupported action', cmd.quiet ?? 0); } @@ -342,8 +350,10 @@ export class KittyGraphicsHandler implements IApcHandler, IResetHandler, IDispos switch (action) { case KittyAction.TRANSMIT: { const result = this._handleTransmit(cmd, bytes, decodeError); - // Only send response when _handleTransmit didn't already respond - // (it handles unsupported transmission medium responses internally) + /* + * Only send response when _handleTransmit didn't already respond + * (it handles unsupported transmission medium responses internally) + */ if ((cmd.transmission ?? 'd') === 'd' && cmd.id !== undefined) { if (decodeError) { this._sendResponse(cmd.id, 'EINVAL:invalid base64 data', cmd.quiet ?? 0); @@ -361,10 +371,12 @@ export class KittyGraphicsHandler implements IApcHandler, IResetHandler, IDispos // a=p ignores any payload — image data was already transmitted return this._handlePlacement(cmd); default: - // TODO: Implement remaining actions when needed: - // - a=f (frame): animation frame operations - // - a=a (animation): animation control - // - a=c (compose): compose images + /* + * TODO: Implement remaining actions when needed: + * - a=f (frame): animation frame operations + * - a=a (animation): animation control + * - a=c (compose): compose images + */ if (cmd.id !== undefined) { this._sendResponse(cmd.id, 'EINVAL:unsupported action', cmd.quiet ?? 0); } @@ -390,16 +402,18 @@ export class KittyGraphicsHandler implements IApcHandler, IResetHandler, IDispos } private _handleTransmit(cmd: IKittyCommand, bytes: Uint8Array, decodeError: boolean): boolean { - // TODO: Support file-based transmission modes (t=f, t=t, t=s) - // Currently only supports direct transmission (t=d, the default). - // - t=f (file): Payload is base64-encoded file path. Terminal reads image from that path. - // - t=t (temp file): Payload is base64-encoded path in temp directory. Terminal reads, deletes. - // - t=s: Payload is base64-encoded POSIX shm name. Terminal reads from shared memory. - // These modes require filesystem/IPC access not available in browsers. For Node.js/Electron: - // 1. Check cmd.transmission (t key) before treating bytes as image data - // 2. For t=f/t/s: decode bytes as UTF-8 string (the path/name), then read file contents - // 3. For t=d: treat bytes as image data (current behavior) - // When implementing, also update _handleQuery to accept these transmission mediums. + /* + * TODO: Support file-based transmission modes (t=f, t=t, t=s) + * Currently only supports direct transmission (t=d, the default). + * - t=f (file): Payload is base64-encoded file path. Terminal reads image from that path. + * - t=t (temp file): Payload is base64-encoded path in temp directory. Terminal reads, deletes. + * - t=s: Payload is base64-encoded POSIX shm name. Terminal reads from shared memory. + * These modes require filesystem/IPC access not available in browsers. For Node.js/Electron: + * 1. Check cmd.transmission (t key) before treating bytes as image data + * 2. For t=f/t/s: decode bytes as UTF-8 string (the path/name), then read file contents + * 3. For t=d: treat bytes as image data (current behavior) + * When implementing, also update _handleQuery to accept these transmission mediums. + */ const transmission = cmd.transmission ?? 'd'; if (transmission !== 'd') { if (cmd.id !== undefined) { @@ -449,9 +463,11 @@ export class KittyGraphicsHandler implements IApcHandler, IResetHandler, IDispos const id = cmd.id ?? 0; const quiet = cmd.quiet ?? 0; - // Per spec: reject unsupported transmission mediums (only t=d is supported atm) - // TODO: When filesystem support is added (Node.js/Electron), update this to accept - // t=f (file), t=t (temp file), and t=s (shared memory) and respond OK for queries. + /* + * Per spec: reject unsupported transmission mediums (only t=d is supported atm) + * TODO: When filesystem support is added (Node.js/Electron), update this to accept + * t=f (file), t=t (temp file), and t=s (shared memory) and respond OK for queries. + */ const transmission = cmd.transmission ?? 'd'; if (transmission !== 'd') { this._sendResponse(id, 'EINVAL:unsupported transmission medium', quiet); @@ -500,9 +516,11 @@ export class KittyGraphicsHandler implements IApcHandler, IResetHandler, IDispos // Per spec: default delete selector is 'a' (delete all visible placements) const selector = cmd.deleteSelector ?? 'a'; - // TODO: Distinguish lowercase (delete placements only) from uppercase - // (delete placements + free stored image data). Currently both variants - // free everything since we don't separate stored data from placements. + /* + * TODO: Distinguish lowercase (delete placements only) from uppercase + * (delete placements + free stored image data). Currently both variants + * free everything since we don't separate stored data from placements. + */ switch (selector) { case 'a': case 'A': @@ -511,10 +529,12 @@ export class KittyGraphicsHandler implements IApcHandler, IResetHandler, IDispos break; case 'i': case 'I': - // TODO: When placement id tracking is implemented (see TODO in - // KittyImageStorage), d=i with p= should delete only that - // specific placement, while d=i without p should delete all - // placements for the image. + /* + * TODO: When placement id tracking is implemented (see TODO in + * KittyImageStorage), d=i with p= should delete only that + * specific placement, while d=i without p should delete all + * placements for the image. + */ if (cmd.id !== undefined) { const pending = this._pendingTransmissions.get(cmd.id); if (pending) { @@ -576,8 +596,10 @@ export class KittyGraphicsHandler implements IApcHandler, IResetHandler, IDispos const cw = this._renderer.dimensions?.css.cell.width || CELL_SIZE_DEFAULT.width; const ch = this._renderer.dimensions?.css.cell.height || CELL_SIZE_DEFAULT.height; - // Per spec: c/r default to image's natural cell dimensions. - // If only one of c/r is specified, compute the other from image aspect ratio. + /* + * Per spec: c/r default to image's natural cell dimensions. + * If only one of c/r is specified, compute the other from image aspect ratio. + */ let imgCols: number; let imgRows: number; if (cmd.columns !== undefined && cmd.rows !== undefined) { @@ -613,10 +635,12 @@ export class KittyGraphicsHandler implements IApcHandler, IResetHandler, IDispos const savedY = buffer.y; const savedYbase = buffer.ybase; - // Determine layer based on z-index: negative = behind text, 0+ = on top. - // When z<0 we always use the bottom layer even without allowTransparency — - // the image will simply be hidden behind the opaque text background, which - // is the correct behavior (client asked for "behind text"). + /* + * Determine layer based on z-index: negative = behind text, 0+ = on top. + * When z<0 we always use the bottom layer even without allowTransparency — + * the image will simply be hidden behind the opaque text background, which + * is the correct behavior (client asked for "behind text"). + */ const wantsBottom = cmd.zIndex !== undefined && cmd.zIndex < 0; const layer: ImageLayer = wantsBottom ? 'bottom' : 'top'; @@ -630,10 +654,12 @@ export class KittyGraphicsHandler implements IApcHandler, IResetHandler, IDispos const xOffset = Math.min(Math.max(0, cmd.xOffset ?? 0), cw - 1); const yOffset = Math.min(Math.max(0, cmd.yOffset ?? 0), ch - 1); if (xOffset !== 0 || yOffset !== 0) { - // Per spec: X/Y is not added to c/r area. When c/r are explicit, the - // total placement area remains c*cw × r*ch pixels and the offset image - // is clipped to fit. When c/r are unset, the padded canvas determines - // the natural cell dimensions. + /* + * Per spec: X/Y is not added to c/r area. When c/r are explicit, the + * total placement area remains c*cw × r*ch pixels and the offset image + * is clipped to fit. When c/r are unset, the padded canvas determines + * the natural cell dimensions. + */ const canvasW = (cmd.columns !== undefined) ? Math.round(imgCols * cw) : bitmap.width + xOffset; const canvasH = (cmd.rows !== undefined) ? Math.round(imgRows * ch) : bitmap.height + yOffset; const offsetCanvas = ImageRenderer.createCanvas(window.document, canvasW, canvasH); @@ -664,9 +690,11 @@ export class KittyGraphicsHandler implements IApcHandler, IResetHandler, IDispos this._kittyStorage.addImage(image.id, bitmap, true, layer, zIndex); bitmap = undefined; // ownership transferred to storage - // Kitty cursor movement - // Per spec: cursor placed at first column after last image column, - // on the last row of the image. C=1 means don't move cursor. + /* + * Kitty cursor movement + * Per spec: cursor placed at first column after last image column, + * on the last row of the image. C=1 means don't move cursor. + */ if (cmd.cursorMovement === 1) { // C=1: restore cursor to position before image was placed const scrolled = buffer.ybase - savedYbase; @@ -674,8 +702,10 @@ export class KittyGraphicsHandler implements IApcHandler, IResetHandler, IDispos // Can't restore cursor to scrollback? buffer.y = Math.max(savedY - scrolled, 0); } else { - // Default (C=0): advance cursor horizontally past the image - // addImage already positioned cursor on the last row via lineFeeds + /* + * Default (C=0): advance cursor horizontally past the image + * addImage already positioned cursor on the last row via lineFeeds + */ buffer.x = Math.min(savedX + imgCols, this._coreTerminal.cols); } } catch (e) { @@ -736,9 +766,11 @@ export class KittyGraphicsHandler implements IApcHandler, IResetHandler, IDispos return createImageBitmap(new ImageData(new Uint8ClampedArray(bytes.buffer as ArrayBuffer, bytes.byteOffset, pixelCount * KittyPixelConstants.BYTES_PER_PIXEL_RGBA), width, height)); } - // RGB→RGBA: interleave alpha using uint32 block processing (4 pixels per iteration). - // 3 uint32 reads + 4 uint32 writes per 4 pixels vs 28 byte reads/writes — ~6x faster. - // Assumes little-endian (all modern browsers/Node.js). + /* + * RGB→RGBA: interleave alpha using uint32 block processing (4 pixels per iteration). + * 3 uint32 reads + 4 uint32 writes per 4 pixels vs 28 byte reads/writes — ~6x faster. + * Assumes little-endian (all modern browsers/Node.js). + */ const data = new Uint8ClampedArray(pixelCount * KittyPixelConstants.BYTES_PER_PIXEL_RGBA); const src32 = new Uint32Array(bytes.buffer, bytes.byteOffset, Math.floor(bytes.byteLength / 4)); const dst32 = new Uint32Array(data.buffer); diff --git a/addons/addon-image/src/kitty/KittyGraphicsTypes.ts b/addons/addon-image/src/kitty/KittyGraphicsTypes.ts index 448ebcc05..da94675d8 100644 --- a/addons/addon-image/src/kitty/KittyGraphicsTypes.ts +++ b/addons/addon-image/src/kitty/KittyGraphicsTypes.ts @@ -7,8 +7,10 @@ import type Base64Decoder from 'xterm-wasm-parts/lib/base64/Base64Decoder.wasm'; -// Kitty graphics protocol action types. -// See: https://sw.kovidgoyal.net/kitty/graphics-protocol/#control-data-reference under key 'a'. +/* + * Kitty graphics protocol action types. + * See: https://sw.kovidgoyal.net/kitty/graphics-protocol/#control-data-reference under key 'a'. + */ export const enum KittyAction { TRANSMIT = 't', TRANSMIT_DISPLAY = 'T', @@ -17,23 +19,29 @@ export const enum KittyAction { DELETE = 'd' } -// Kitty graphics protocol format types. -// See: https://sw.kovidgoyal.net/kitty/graphics-protocol/#control-data-reference +/* + * Kitty graphics protocol format types. + * See: https://sw.kovidgoyal.net/kitty/graphics-protocol/#control-data-reference + */ export const enum KittyFormat { RGB = 24, RGBA = 32, PNG = 100 } -// Kitty graphics protocol compression types. -// See: https://sw.kovidgoyal.net/kitty/graphics-protocol/#control-data-reference under key 'o'. +/* + * Kitty graphics protocol compression types. + * See: https://sw.kovidgoyal.net/kitty/graphics-protocol/#control-data-reference under key 'o'. + */ export const enum KittyCompression { NONE = '', ZLIB = 'z' } -// Kitty graphics protocol control data keys. -// See: https://sw.kovidgoyal.net/kitty/graphics-protocol/#control-data-reference +/* + * Kitty graphics protocol control data keys. + * See: https://sw.kovidgoyal.net/kitty/graphics-protocol/#control-data-reference + */ export const enum KittyKey { // Action to perform (t=transmit, T=transmit+display, q=query, p=placement, d=delete) ACTION = 'a', @@ -115,8 +123,10 @@ export interface IKittyCommand { payload?: string; } -// Pending chunked transmission state. -// Stores metadata from the first chunk while accumulating decoded payload data. +/* + * Pending chunked transmission state. + * Stores metadata from the first chunk while accumulating decoded payload data. + */ export interface IPendingTransmission { // The parsed command from the first chunk (contains action, format, dimensions, etc.) cmd: IKittyCommand; diff --git a/addons/addon-image/src/kitty/KittyImageStorage.ts b/addons/addon-image/src/kitty/KittyImageStorage.ts index 95ea594c7..766806532 100644 --- a/addons/addon-image/src/kitty/KittyImageStorage.ts +++ b/addons/addon-image/src/kitty/KittyImageStorage.ts @@ -8,27 +8,31 @@ import { ImageStorage } from '../ImageStorage'; import { ImageLayer } from '../Types'; import { IKittyImageData } from './KittyGraphicsTypes'; -// Kitty-specific image storage controller. -// -// Wraps shared ImageStorage with kitty protocol semantics: -// - tracks transmitted image payloads by kitty image id -// - tracks kitty image id -> shared ImageStorage id mapping for displayed images -// - mirrors shared-storage evictions into kitty maps -// - applies protocol-level undisplayed-image eviction policy +/* + * Kitty-specific image storage controller. + * + * Wraps shared ImageStorage with kitty protocol semantics: + * - tracks transmitted image payloads by kitty image id + * - tracks kitty image id -> shared ImageStorage id mapping for displayed images + * - mirrors shared-storage evictions into kitty maps + * - applies protocol-level undisplayed-image eviction policy + */ export class KittyImageStorage implements IDisposable { private static readonly _maxStoredImages = 256; private _nextImageId = 1; private readonly _images: Map = new Map(); - // TODO: Support multiple placements per image. The kitty spec identifies - // placements by an (image id, placement id) pair — same i + different p - // values should coexist, and same i + same p should replace the prior - // placement. Currently we track only one storage entry per kitty image id, - // so multiple placements of the same image overwrite each other. Fixing - // this requires changing these maps to Map> - // (kittyId → placementId → storageId) and updating addImage/deleteById - // accordingly. The underlying shared ImageStorage would also need to - // support multiple entries per logical image. + /* + * TODO: Support multiple placements per image. The kitty spec identifies + * placements by an (image id, placement id) pair — same i + different p + * values should coexist, and same i + same p should replace the prior + * placement. Currently we track only one storage entry per kitty image id, + * so multiple placements of the same image overwrite each other. Fixing + * this requires changing these maps to Map> + * (kittyId → placementId → storageId) and updating addImage/deleteById + * accordingly. The underlying shared ImageStorage would also need to + * support multiple entries per logical image. + */ private readonly _kittyIdToStorageId: Map = new Map(); private readonly _storageIdToKittyId: Map = new Map(); @@ -90,10 +94,12 @@ export class KittyImageStorage implements IDisposable { } public addImage(kittyId: number, image: HTMLCanvasElement | ImageBitmap, scrolling: boolean, layer: ImageLayer, zIndex: number): void { - // Clean up stale reverse-mapping from a previous placement of the same - // kitty image. The old shared-storage entry is kept (it may still be - // visible on screen) but its reverse mapping is removed so that eviction - // of the old entry won't incorrectly delete the kitty image data. + /* + * Clean up stale reverse-mapping from a previous placement of the same + * kitty image. The old shared-storage entry is kept (it may still be + * visible on screen) but its reverse mapping is removed so that eviction + * of the old entry won't incorrectly delete the kitty image data. + */ const oldStorageId = this._kittyIdToStorageId.get(kittyId); if (oldStorageId !== undefined) { this._storageIdToKittyId.delete(oldStorageId); diff --git a/addons/addon-image/test/ImageAddon.test.ts b/addons/addon-image/test/ImageAddon.test.ts index dc729ec63..9db26cd7f 100644 --- a/addons/addon-image/test/ImageAddon.test.ts +++ b/addons/addon-image/test/ImageAddon.test.ts @@ -64,8 +64,10 @@ const TESTDATA: ITestData = (() => { }; })(); const SIXEL_SEQ_0 = introducer(0) + TESTDATA.sixel + FINALIZER; -// const SIXEL_SEQ_1 = introducer(1) + TESTDATA.sixel + FINALIZER; -// const SIXEL_SEQ_2 = introducer(2) + TESTDATA.sixel + FINALIZER; +/* + * const SIXEL_SEQ_1 = introducer(1) + TESTDATA.sixel + FINALIZER; + * const SIXEL_SEQ_2 = introducer(2) + TESTDATA.sixel + FINALIZER; + */ // NOTE: the data is loaded as string for easier transport through playwright const TESTDATA_IIP: [string, [number, number]][] = [ diff --git a/addons/addon-image/test/KittyGraphics.test.ts b/addons/addon-image/test/KittyGraphics.test.ts index a9b5d6845..2822cce6b 100644 --- a/addons/addon-image/test/KittyGraphics.test.ts +++ b/addons/addon-image/test/KittyGraphics.test.ts @@ -101,12 +101,14 @@ test.beforeAll(async ({ browser }) => { test.afterAll(async () => await ctx.page.close()); test.describe('Kitty Graphics Protocol', () => { - // TODO: Add tests for larger images with various dimensions - // TODO: Add tests for virtual placement (U=1) - // TODO: Add tests for animation frames - // TODO: Add performance tests for streaming large images - // TODO: Implement cursor movement per Kitty spec - cursor should move by cols/rows after placement (unless C=1) - // TODO: Distinguish lowercase delete selectors (placement only) from uppercase (placement + free data) + /* + * TODO: Add tests for larger images with various dimensions + * TODO: Add tests for virtual placement (U=1) + * TODO: Add tests for animation frames + * TODO: Add performance tests for streaming large images + * TODO: Implement cursor movement per Kitty spec - cursor should move by cols/rows after placement (unless C=1) + * TODO: Distinguish lowercase delete selectors (placement only) from uppercase (placement + free data) + */ test.beforeEach(async ({}, testInfo) => { await ctx.page.evaluate(` @@ -209,8 +211,10 @@ test.describe('Kitty Graphics Protocol', () => { }); test('enforces size limit across chunked transmissions', async () => { - // Create a custom addon with very small size limit (100 bytes) - // The 1x1 PNG is ~164 bytes base64, so 2 chunks should exceed 100 + /* + * Create a custom addon with very small size limit (100 bytes) + * The 1x1 PNG is ~164 bytes base64, so 2 chunks should exceed 100 + */ await ctx.page.evaluate(() => { (window as any).smallLimitAddon = new ImageAddon({ kittySupport: true, @@ -1279,8 +1283,10 @@ test.describe('Kitty Graphics Protocol', () => { }); test('only c specified computes r from aspect ratio', async () => { - // 200x100 image (2:1 aspect) with c=10. - // Per spec: r = ceil((h/w) * c * cw / ch) = ceil(0.5 * 10 * cw / ch) + /* + * 200x100 image (2:1 aspect) with c=10. + * Per spec: r = ceil((h/w) * c * cw / ch) = ceil(0.5 * 10 * cw / ch) + */ await ctx.proxy.write(`\x1b_Ga=t,f=100,i=225;${KITTY_MULTICOLOR_200X100_BASE64}\x1b\\`); await timeout(100); @@ -1297,8 +1303,10 @@ test.describe('Kitty Graphics Protocol', () => { }); test('only r specified computes c from aspect ratio', async () => { - // 200x100 image (2:1 aspect) with r=5. - // Per spec: c = ceil((w/h) * r * ch / cw) = ceil(2 * 5 * ch / cw) + /* + * 200x100 image (2:1 aspect) with r=5. + * Per spec: c = ceil((w/h) * r * ch / cw) = ceil(2 * 5 * ch / cw) + */ await ctx.proxy.write(`\x1b_Ga=t,f=100,i=226;${KITTY_MULTICOLOR_200X100_BASE64}\x1b\\`); await timeout(100); @@ -1316,9 +1324,11 @@ test.describe('Kitty Graphics Protocol', () => { }); test.describe('Cursor positioning', () => { - // NOTE: Current tests document ACTUAL behavior (MVP - cursor doesn't move) - // Per Kitty spec: cursor placed at first column after last image column, - // on the last row of the image. C=1 means don't move cursor. + /* + * NOTE: Current tests document ACTUAL behavior (MVP - cursor doesn't move) + * Per Kitty spec: cursor placed at first column after last image column, + * on the last row of the image. C=1 means don't move cursor. + */ test('cursor advances past 1x1 image', async () => { const cursorBefore = await getCursor(); @@ -1376,8 +1386,10 @@ test.describe('Kitty Graphics Protocol', () => { }); test('cursor should move right by cols when c specified', async () => { - // c=5, r computed from 1x1 aspect ratio: r = ceil((1/1) * (5*cw) / ch) - // With 1:1 aspect, image height in pixels = 5*cw, so r = ceil(5*cw/ch) + /* + * c=5, r computed from 1x1 aspect ratio: r = ceil((1/1) * (5*cw) / ch) + * With 1:1 aspect, image height in pixels = 5*cw, so r = ceil(5*cw/ch) + */ await ctx.proxy.write(`\x1b_Ga=T,f=100,c=5;${KITTY_BLACK_1X1_BASE64}\x1b\\`); await timeout(100); @@ -1634,10 +1646,12 @@ test.describe('Kitty Graphics Protocol', () => { }); test.describe('Pixel verification', () => { - // The 200x100 image has 20 colored rectangles in a 10x2 grid. - // Each rectangle is 20px wide x 50px tall. - // Top row (y=0..49): Red, Orange, Yellow, Lime, Green, Cyan, SkyBlue, Blue, Purple, Magenta - // Bottom row (y=50..99): Pink, Brown, Maroon, Olive, Teal, Navy, Gray, DarkGray, LightGray, White + /* + * The 200x100 image has 20 colored rectangles in a 10x2 grid. + * Each rectangle is 20px wide x 50px tall. + * Top row (y=0..49): Red, Orange, Yellow, Lime, Green, Cyan, SkyBlue, Blue, Purple, Magenta + * Bottom row (y=50..99): Pink, Brown, Maroon, Olive, Teal, Navy, Gray, DarkGray, LightGray, White + */ test('renders red rectangle at top-left origin (0,0)', async () => { await ctx.proxy.write(`\x1b_Ga=T,f=100;${KITTY_MULTICOLOR_200X100_BASE64}\x1b\\`); @@ -1650,9 +1664,11 @@ test.describe('Kitty Graphics Protocol', () => { await ctx.proxy.write(`\x1b_Ga=T,f=100;${KITTY_MULTICOLOR_200X100_BASE64}\x1b\\`); await timeout(200); - // Sample center of each top-row rectangle (y=25, x=10,30,50,...,190) - // All within the first cell row, so we read from the canvas at cell (0,0) - // Red at x=10 + /* + * Sample center of each top-row rectangle (y=25, x=10,30,50,...,190) + * All within the first cell row, so we read from the canvas at cell (0,0) + * Red at x=10 + */ deepStrictEqual(await getPixel(0, 0, 10, 25), [255, 0, 0, 255]); // Orange at x=30 deepStrictEqual(await getPixel(0, 0, 30, 25), [255, 128, 0, 255]); @@ -1668,8 +1684,10 @@ test.describe('Kitty Graphics Protocol', () => { await ctx.proxy.write(`\x1b_Ga=T,f=100;${KITTY_MULTICOLOR_200X100_BASE64}\x1b\\`); await timeout(200); - // Bottom row starts at y=50. Center at y=75. - // Pink at x=10 + /* + * Bottom row starts at y=50. Center at y=75. + * Pink at x=10 + */ deepStrictEqual(await getPixel(0, 0, 10, 75), [255, 192, 203, 255]); // Brown at x=30 deepStrictEqual(await getPixel(0, 0, 30, 75), [165, 42, 42, 255]); @@ -1725,8 +1743,10 @@ test.describe('Kitty Graphics Protocol', () => { }); test('scales cropped source region to c/r placement rectangle', async () => { - // Firefox's createImageBitmap uses different resize sampling, producing - // slightly off pixel values compared to Chromium, so skip on Firefox. + /* + * Firefox's createImageBitmap uses different resize sampling, producing + * slightly off pixel values compared to Chromium, so skip on Firefox. + */ if (ctx.browser.browserType().name() === 'firefox') { test.skip(); } @@ -2137,8 +2157,10 @@ test.describe('Kitty Graphics Protocol', () => { }); test('memory limit eviction cleans Kitty handler maps', async () => { - // Resize terminal to fit 7 non-overlapping 200x100 images without scrolling. - // Each image ≈ 29 cols × 8 rows at default cell size. + /* + * Resize terminal to fit 7 non-overlapping 200x100 images without scrolling. + * Each image ≈ 29 cols × 8 rows at default cell size. + */ await ctx.page.evaluate(` window.term.reset(); window.imageAddon?.dispose(); @@ -2147,9 +2169,11 @@ test.describe('Kitty Graphics Protocol', () => { window.term.loadAddon(window.imageAddon); `); - // storageLimit 0.5 MB = 125,000 pixels. Each 200x100 image = 20,000 pixels. - // 6 images = 120K pixels (under limit). 7th triggers eviction (140K > 125K). - // Place non-overlapping so tile-count eviction doesn't interfere. + /* + * storageLimit 0.5 MB = 125,000 pixels. Each 200x100 image = 20,000 pixels. + * 6 images = 120K pixels (under limit). 7th triggers eviction (140K > 125K). + * Place non-overlapping so tile-count eviction doesn't interfere. + */ const positions = [[1, 1], [30, 1], [1, 9], [30, 9], [1, 17], [30, 17]]; for (let n = 0; n < 6; n++) { const [c, r] = positions[n]; diff --git a/addons/addon-ligatures/src/font.ts b/addons/addon-ligatures/src/font.ts index 60ab3d746..8e5f93dd5 100644 --- a/addons/addon-ligatures/src/font.ts +++ b/addons/addon-ligatures/src/font.ts @@ -43,9 +43,11 @@ export default async function load(fontFamily: string, cacheSize: number): Promi throw new Error('Permission to access local fonts not granted.'); } } catch (err: unknown) { - // A `TypeError` indicates the 'local-fonts' - // permission is not yet implemented, so - // only `throw` if this is _not_ the problem. + /* + * A `TypeError` indicates the 'local-fonts' + * permission is not yet implemented, so + * only `throw` if this is _not_ the problem. + */ if (err instanceof Error && err.name !== 'TypeError') { throw err; } @@ -89,9 +91,11 @@ export default async function load(fontFamily: string, cacheSize: number): Promi const fonts = await fontsPromise; for (const family of parse(fontFamily)) { - // If we reach one of the generic font families, the font resolution - // will end for the browser and we can't determine the specific font - // used. Throw. + /* + * If we reach one of the generic font families, the font resolution + * will end for the browser and we can't determine the specific font + * used. Throw. + */ if (genericFontFamilies.includes(family)) { return undefined; } diff --git a/addons/addon-ligatures/src/fontLigatures/index.ts b/addons/addon-ligatures/src/fontLigatures/index.ts index d65e73bc4..794b39ecc 100644 --- a/addons/addon-ligatures/src/fontLigatures/index.ts +++ b/addons/addon-ligatures/src/fontLigatures/index.ts @@ -88,9 +88,11 @@ class FontImpl implements IFont { glyphIds.push(this._font.charToGlyphIndex(char)); } - // If there are no lookup groups, there's no point looking for - // replacements. This gives us a minor performance boost for fonts with - // no ligatures + /* + * If there are no lookup groups, there's no point looking for + * replacements. This gives us a minor performance boost for fonts with + * no ligatures + */ if (this._lookupTrees.length === 0) { return { inputGlyphs: glyphIds, @@ -113,8 +115,10 @@ class FontImpl implements IFont { } public findLigatureRanges(text: string): [number, number][] { - // Short circuit the process if there are no possible ligatures in the - // font + /* + * Short circuit the process if there are no possible ligatures in the + * font + */ if (this._lookupTrees.length === 0) { return []; } @@ -170,9 +174,11 @@ class FontImpl implements IFont { } } } else { - // We don't need to do the lastGlyphIndex tracking here because - // reverse processing isn't allowed to replace more than one - // character at a time. + /* + * We don't need to do the lastGlyphIndex tracking here because + * reverse processing isn't allowed to replace more than one + * character at a time. + */ for (let i = nextLookup.last - 1; i >= nextLookup.first; i--) { const result = walkTree(lookup.tree, sequence, i, i); if (result) { @@ -224,8 +230,10 @@ class FontImpl implements IFont { for (let j = 0; j < lookups.length; j++) { const lookupIndex = lookups[j]; if (lookupIndex >= start) { - // Update the lookup information if it's the one we're - // storing or earlier than it. + /* + * Update the lookup information if it's the one we're + * storing or earlier than it. + */ if (result.index === null || lookupIndex <= result.index) { result.index = lookupIndex; diff --git a/addons/addon-ligatures/src/fontLigatures/merge.ts b/addons/addon-ligatures/src/fontLigatures/merge.ts index 262ea85ae..a16f748a9 100644 --- a/addons/addon-ligatures/src/fontLigatures/merge.ts +++ b/addons/addon-ligatures/src/fontLigatures/merge.ts @@ -32,9 +32,11 @@ export default function mergeTrees(trees: ILookupTree[]): ILookupTree { function mergeSubtree(mainTree: ILookupTree, mergeTree: ILookupTree, mergedEntries: WeakMap>): void { // Need to fix this recursively (and handle lookups) for (const [glyphId, value] of Object.entries(mergeTree.individual)) { - // The main tree is guaranteed to have no overlaps between the - // individual and range values, so if we match an invididual, there - // must not be a range + /* + * The main tree is guaranteed to have no overlaps between the + * individual and range values, so if we match an invididual, there + * must not be a range + */ if (mainTree.individual[glyphId]) { mergeTreeEntry(mainTree.individual[glyphId], value, mergedEntries); } else { @@ -49,13 +51,17 @@ function mergeSubtree(mainTree: ILookupTree, mergeTree: ILookupTree, mergedEntri matched = true; - // If they overlap, we have to split the range and then - // merge the overlap + /* + * If they overlap, we have to split the range and then + * merge the overlap + */ mainTree.individual[glyphId] = value; mergeTreeEntry(mainTree.individual[glyphId], cloneEntry(entry), mergedEntries); - // When there's an overlap, we also have to fix up the range - // that we had already processed + /* + * When there's an overlap, we also have to fix up the range + * that we had already processed + */ mainTree.range.splice(index, 1); for (const glyph of overlap.second) { if (Array.isArray(glyph)) { @@ -76,9 +82,11 @@ function mergeSubtree(mainTree: ILookupTree, mergeTree: ILookupTree, mergedEntri } for (const { range, entry } of mergeTree.range) { - // Ranges are more complicated, because they can overlap with - // multiple things, individual and range alike. We start by - // eliminating ranges that are already present in another range + /* + * Ranges are more complicated, because they can overlap with + * multiple things, individual and range alike. We start by + * eliminating ranges that are already present in another range + */ let remainingRanges: (number | [number, number])[] = [range]; for (let index = 0; index < mainTree.range.length; index++) { @@ -123,13 +131,17 @@ function mergeSubtree(mainTree: ILookupTree, mergeTree: ILookupTree, mergedEntri continue; } - // If they overlap, we have to split the range and then - // merge the overlap + /* + * If they overlap, we have to split the range and then + * merge the overlap + */ mainTree.individual[remainingRange] = cloneEntry(entry); mergeTreeEntry(mainTree.individual[remainingRange], cloneEntry(resultEntry), mergedEntries); - // When there's an overlap, we also have to fix up the range - // that we had already processed + /* + * When there's an overlap, we also have to fix up the range + * that we had already processed + */ mainTree.range.splice(index, 1); index--; diff --git a/addons/addon-ligatures/src/fontLigatures/mergeRange.ts b/addons/addon-ligatures/src/fontLigatures/mergeRange.ts index ec5305083..8058e077f 100644 --- a/addons/addon-ligatures/src/fontLigatures/mergeRange.ts +++ b/addons/addon-ligatures/src/fontLigatures/mergeRange.ts @@ -18,14 +18,18 @@ export default function mergeRange(ranges: [number, number][], newRangeStart: nu return ranges; } if (newRangeEnd <= range[1]) { - // Case 2: New range is either wholly contained within the - // search range or overlaps with the front of it + /* + * Case 2: New range is either wholly contained within the + * search range or overlaps with the front of it + */ range[0] = Math.min(newRangeStart, range[0]); return ranges; } if (newRangeStart < range[1]) { - // Case 3: New range either wholly contains the search range - // or overlaps with the end of it + /* + * Case 3: New range either wholly contains the search range + * or overlaps with the end of it + */ range[0] = Math.min(newRangeStart, range[0]); inRange = true; } else { @@ -34,21 +38,27 @@ export default function mergeRange(ranges: [number, number][], newRangeStart: nu } } else { if (newRangeEnd <= range[0]) { - // Case 5: New range extends from previous range but doesn't - // reach the current one + /* + * Case 5: New range extends from previous range but doesn't + * reach the current one + */ ranges[i - 1][1] = newRangeEnd; return ranges; } if (newRangeEnd <= range[1]) { - // Case 6: New range extends from prvious range into the - // current range + /* + * Case 6: New range extends from prvious range into the + * current range + */ ranges[i - 1][1] = Math.max(newRangeEnd, range[1]); ranges.splice(i, 1); inRange = false; return ranges; } - // Case 7: New range extends from previous range past the - // end of the current range + /* + * Case 7: New range extends from previous range past the + * end of the current range + */ ranges.splice(i, 1); i--; } diff --git a/addons/addon-ligatures/src/fontLigatures/processors/6-2.ts b/addons/addon-ligatures/src/fontLigatures/processors/6-2.ts index f39682423..890188508 100644 --- a/addons/addon-ligatures/src/fontLigatures/processors/6-2.ts +++ b/addons/addon-ligatures/src/fontLigatures/processors/6-2.ts @@ -72,8 +72,10 @@ export default function buildTree(table: ChainingContextualSubstitutionTable.IFo ); } - // When we get to the end, all of the entries we've accumulated - // should have a lookup defined + /* + * When we get to the end, all of the entries we've accumulated + * should have a lookup defined + */ for (const { entry, substitutions } of currentEntries) { entry.lookup = { substitutions, diff --git a/addons/addon-ligatures/src/fontLigatures/processors/6-3.ts b/addons/addon-ligatures/src/fontLigatures/processors/6-3.ts index 334e6a566..2f8290824 100644 --- a/addons/addon-ligatures/src/fontLigatures/processors/6-3.ts +++ b/addons/addon-ligatures/src/fontLigatures/processors/6-3.ts @@ -53,8 +53,10 @@ export default function buildTree(table: ChainingContextualSubstitutionTable.IFo ); } - // When we get to the end, all of the entries we've accumulated - // should have a lookup defined + /* + * When we get to the end, all of the entries we've accumulated + * should have a lookup defined + */ for (const { entry, substitutions } of currentEntries) { entry.lookup = { substitutions, diff --git a/addons/addon-ligatures/src/fontLigatures/processors/helper.ts b/addons/addon-ligatures/src/fontLigatures/processors/helper.ts index e6259b6d4..6bb6060d1 100644 --- a/addons/addon-ligatures/src/fontLigatures/processors/helper.ts +++ b/addons/addon-ligatures/src/fontLigatures/processors/helper.ts @@ -57,8 +57,10 @@ export function processLookaheadPosition( range: [] }; - // All glyphs at this position share ONE entry - lookahead just needs to match, - // all paths lead to the same result + /* + * All glyphs at this position share ONE entry - lookahead just needs to match, + * all paths lead to the same result + */ const sharedEntry: ILookupTreeEntry = {}; for (const glyph of glyphs) { @@ -100,8 +102,10 @@ export function processBacktrackPosition( range: [] }; - // All glyphs at this position share ONE entry - backtrack just needs to match, - // all paths lead to the same result + /* + * All glyphs at this position share ONE entry - backtrack just needs to match, + * all paths lead to the same result + */ const sharedEntry: ILookupTreeEntry = {}; for (const glyph of glyphs) { diff --git a/addons/addon-ligatures/src/fontLigatures/processors/substitution.ts b/addons/addon-ligatures/src/fontLigatures/processors/substitution.ts index 3a41e936c..7e461b87c 100644 --- a/addons/addon-ligatures/src/fontLigatures/processors/substitution.ts +++ b/addons/addon-ligatures/src/fontLigatures/processors/substitution.ts @@ -49,8 +49,10 @@ export function getIndividualSubstitutionGlyph(table: SubstitutionTable, glyphId switch (table.substFormat) { // https://docs.microsoft.com/en-us/typography/opentype/spec/gsub#11-single-substitution-format-1 case 1: - // TODO: determine if there's a rhyme or reason to the 16-bit - // wraparound and if it can ever be a different number + /* + * TODO: determine if there's a rhyme or reason to the 16-bit + * wraparound and if it can ever be a different number + */ return (glyphId + table.deltaGlyphId) % (2 ** 16); // https://docs.microsoft.com/en-us/typography/opentype/spec/gsub#12-single-substitution-format-2 case 2: diff --git a/addons/addon-ligatures/src/index.ts b/addons/addon-ligatures/src/index.ts index fb8e85e73..33990b773 100644 --- a/addons/addon-ligatures/src/index.ts +++ b/addons/addon-ligatures/src/index.ts @@ -15,8 +15,10 @@ const enum LoadingState { FAILED } -// Caches 100K characters worth of ligatures. In practice this works out to -// about 650 KB worth of cache, when a moderate number of ligatures are present. +/* + * Caches 100K characters worth of ligatures. In practice this works out to + * about 650 KB worth of cache, when a moderate number of ligatures are present. + */ const CACHE_SIZE = 100000; /** @@ -46,8 +48,10 @@ export function enableLigatures(term: Terminal, fallbackLigatures: string[] = [] load(currentCallFontName, CACHE_SIZE) .then(f => { - // Another request may have come in while we were waiting, so make - // sure our font is still vaild. + /* + * Another request may have come in while we were waiting, so make + * sure our font is still vaild. + */ if (currentCallFontName === term.options.fontFamily) { loadingState = LoadingState.LOADED; font = f; @@ -59,8 +63,10 @@ export function enableLigatures(term: Terminal, fallbackLigatures: string[] = [] } }) .catch(e => { - // Another request may have come in while we were waiting, so make - // sure our font is still vaild. + /* + * Another request may have come in while we were waiting, so make + * sure our font is still vaild. + */ if (currentCallFontName === term.options.fontFamily) { loadingState = LoadingState.FAILED; if (term.options.logLevel === 'debug') { @@ -73,8 +79,10 @@ export function enableLigatures(term: Terminal, fallbackLigatures: string[] = [] } if (font && loadingState === LoadingState.LOADED) { - // We clone the entries to avoid the internal cache of the ligature finder - // getting messed up. + /* + * We clone the entries to avoid the internal cache of the ligature finder + * getting messed up. + */ return font.findLigatureRanges(text).map<[number, number]>( range => [range[0], range[1]] ); diff --git a/addons/addon-ligatures/src/parse.ts b/addons/addon-ligatures/src/parse.ts index 6289e4681..7cf7ad2f8 100644 --- a/addons/addon-ligatures/src/parse.ts +++ b/addons/addon-ligatures/src/parse.ts @@ -27,8 +27,10 @@ export default function parse(family: string): string[] { const families = []; let currentFamily = ''; - // Work through the input character by character until there are none left. - // This lexing and parsing in one pass. + /* + * Work through the input character by character until there are none left. + * This lexing and parsing in one pass. + */ while (context.offset < context.input.length) { const char = context.input[context.offset++]; switch (char) { @@ -152,13 +154,17 @@ function parseUnicode(context: IParseContext): string { while (context.offset < context.input.length) { const char = context.input[context.offset++]; if (/\s/.test(char)) { - // The first whitespace character after a unicode escape indicates the end - // of the escape and is swallowed. + /* + * The first whitespace character after a unicode escape indicates the end + * of the escape and is swallowed. + */ return unicodeToString(str); } if (str.length >= 6 || !/[\dA-Fa-f]/.test(char)) { - // If the next character is not a valid hex digit or we have reached the - // maximum of 6 digits in the escape, terminate the escape. + /* + * If the next character is not a valid hex digit or we have reached the + * maximum of 6 digits in the escape, terminate the escape. + */ context.offset--; return unicodeToString(str); } diff --git a/addons/addon-search/src/SearchEngine.ts b/addons/addon-search/src/SearchEngine.ts index b9991974d..cbff3eb8e 100644 --- a/addons/addon-search/src/SearchEngine.ts +++ b/addons/addon-search/src/SearchEngine.ts @@ -261,8 +261,10 @@ export class SearchEngine { return; } - // This will iterate until we find the line start. - // When we find it, we will search using the calculated start column. + /* + * This will iterate until we find the line start. + * When we find it, we will search using the calculated start column. + */ searchPosition.startRow--; searchPosition.startCol += this._terminal.cols; return this._findInLine(term, searchPosition, searchOptions); @@ -315,8 +317,10 @@ export class SearchEngine { return; } - // Adjust the row number and search index if needed since a "line" of text can span multiple - // rows + /* + * Adjust the row number and search index if needed since a "line" of text can span multiple + * rows + */ let startRowOffset = 0; while (startRowOffset < offsets.length - 1 && resultIndex >= offsets[startRowOffset + 1]) { startRowOffset++; @@ -355,8 +359,10 @@ export class SearchEngine { if (char.length > 1) { offset -= char.length - 1; } - // Adjust the searchIndex for empty characters following wide unicode - // chars (eg. CJK) + /* + * Adjust the searchIndex for empty characters following wide unicode + * chars (eg. CJK) + */ const nextCell = line.getCell(i + 1); if (nextCell && nextCell.getWidth() === 0) { offset++; diff --git a/addons/addon-search/src/SearchLineCache.ts b/addons/addon-search/src/SearchLineCache.ts index 526f4bfcc..d62f47cd8 100644 --- a/addons/addon-search/src/SearchLineCache.ts +++ b/addons/addon-search/src/SearchLineCache.ts @@ -38,8 +38,10 @@ export class SearchLineCache extends Disposable { private _linesCache: LineCacheEntry[] | undefined; private _linesCacheTimeout = this._register(new MutableDisposable()); private _linesCacheDisposables = this._register(new MutableDisposable()); - // Track access to avoid recreating a timeout on every init call which occurs once per search - // result (findNext/findPrevious -> _highlightAllMatches -> find loop). + /* + * Track access to avoid recreating a timeout on every init call which occurs once per search + * result (findNext/findPrevious -> _highlightAllMatches -> find loop). + */ private _lastAccessTimestamp = 0; constructor(private readonly _terminal: Terminal) { diff --git a/addons/addon-serialize/src/SerializeAddon.ts b/addons/addon-serialize/src/SerializeAddon.ts index 4980c8eda..1d46fd918 100644 --- a/addons/addon-serialize/src/SerializeAddon.ts +++ b/addons/addon-serialize/src/SerializeAddon.ts @@ -84,8 +84,10 @@ function equalBg(cell1: IBufferCell | IAttributeData, cell2: IBufferCell): boole } function equalUnderline(cell1: IBufferCell | IAttributeData, cell2: IBufferCell): boolean { - // If neither cell has underline, consider them equal regardless of internal underline color - // values + /* + * If neither cell has underline, consider them equal regardless of internal underline color + * values + */ if (!cell1.isUnderline() && !cell2.isUnderline()) { return true; } @@ -134,14 +136,18 @@ class StringSerializeHandler extends BaseSerializeHandler { private _currentRow: string = ''; private _nullCellCount: number = 0; - // we can see a full colored cell and a null cell that only have background the same style - // but the information isn't preserved by null cell itself - // so wee need to record it when required. + /* + * we can see a full colored cell and a null cell that only have background the same style + * but the information isn't preserved by null cell itself + * so wee need to record it when required. + */ private _cursorStyle: IBufferCell = this._buffer.getNullCell(); - // where exact the cursor styles comes from - // because we can't copy the cell directly - // so we remember where the content comes from instead + /* + * where exact the cursor styles comes from + * because we can't copy the cell directly + * so we remember where the content comes from instead + */ private _cursorStyleRow: number = 0; private _cursorStyleCol: number = 0; @@ -172,8 +178,10 @@ class StringSerializeHandler extends BaseSerializeHandler { private _thisRowLastSecondChar: IBufferCell = this._buffer.getNullCell(); private _nextRowFirstChar: IBufferCell = this._buffer.getNullCell(); protected _rowEnd(row: number, isLastRow: boolean): void { - // if there is colorful empty cell at line end, whe must pad it back, or the the color block - // will missing + /* + * if there is colorful empty cell at line end, whe must pad it back, or the the color block + * will missing + */ if (this._nullCellCount > 0 && !equalBg(this._cursorStyle, this._backgroundCell)) { // use clear right to set background. this._currentRow += `\u001b[${this._nullCellCount}X`; @@ -206,8 +214,10 @@ class StringSerializeHandler extends BaseSerializeHandler { const nextRowFirstChar = nextLine.getCell(0, this._nextRowFirstChar)!; const isNextRowFirstCharDoubleWidth = nextRowFirstChar.getWidth() > 1; - // validate whether this line wrap is ever possible - // which mean whether cursor can placed at a overflow position (x === row) naturally + /* + * validate whether this line wrap is ever possible + * which mean whether cursor can placed at a overflow position (x === row) naturally + */ let isValid = false; if ( @@ -216,23 +226,31 @@ class StringSerializeHandler extends BaseSerializeHandler { isNextRowFirstCharDoubleWidth ? this._nullCellCount <= 1 : this._nullCellCount <= 0 ) { if ( - // the last character can't be null, - // you can't use control sequence to move cursor to (x === row) + /* + * the last character can't be null, + * you can't use control sequence to move cursor to (x === row) + */ (thisRowLastChar.getChars() || thisRowLastChar.getWidth() === 0) && - // change background of the first wrapped cell also affects BCE - // so we mark it as invalid to simply the process to determine line separator + /* + * change background of the first wrapped cell also affects BCE + * so we mark it as invalid to simply the process to determine line separator + */ equalBg(thisRowLastChar, nextRowFirstChar) ) { isValid = true; } if ( - // the second to last character can't be null if the next line starts with CJK, - // you can't use control sequence to move cursor to (x === row) + /* + * the second to last character can't be null if the next line starts with CJK, + * you can't use control sequence to move cursor to (x === row) + */ isNextRowFirstCharDoubleWidth && (thisRowLastSecondChar.getChars() || thisRowLastSecondChar.getWidth() === 0) && - // change background of the first wrapped cell also affects BCE - // so we mark it as invalid to simply the process to determine line separator + /* + * change background of the first wrapped cell also affects BCE + * so we mark it as invalid to simply the process to determine line separator + */ equalBg(thisRowLastChar, nextRowFirstChar) && equalBg(thisRowLastSecondChar, nextRowFirstChar) ) { @@ -241,8 +259,10 @@ class StringSerializeHandler extends BaseSerializeHandler { } if (!isValid) { - // force the wrap with magic - // insert enough character to force the wrap + /* + * force the wrap with magic + * insert enough character to force the wrap + */ rowSeparator = '-'.repeat(this._nullCellCount + 1); // move back and erase next line head rowSeparator += '\u001b[1D\u001b[1X'; @@ -256,8 +276,10 @@ class StringSerializeHandler extends BaseSerializeHandler { rowSeparator += '\u001b[B'; } - // This is content and need the be serialized even it is invisible. - // without this, wrap will be missing from outputs. + /* + * This is content and need the be serialized even it is invisible. + * without this, wrap will be missing from outputs. + */ this._lastContentCursorRow = row + 1; this._lastContentCursorCol = 0; @@ -358,8 +380,10 @@ class StringSerializeHandler extends BaseSerializeHandler { const sgrSeq = this._diffStyle(cell, this._cursorStyle); - // the empty cell style is only assumed to be changed when background changed, because - // foreground is always 0. + /* + * the empty cell style is only assumed to be changed when background changed, because + * foreground is always 0. + */ const styleChanged = isEmptyCell ? !equalBg(this._cursorStyle, cell) : sgrSeq.length > 0; /** @@ -398,9 +422,11 @@ class StringSerializeHandler extends BaseSerializeHandler { this._nullCellCount += cell.getWidth(); } else { if (this._nullCellCount > 0) { - // we can just assume we have same style with previous one here - // because style change is handled by previous stage - // use move right when background is empty, use clear right when there is background. + /* + * we can just assume we have same style with previous one here + * because style change is handled by previous stage + * use move right when background is empty, use clear right when there is background. + */ if (equalBg(this._cursorStyle, this._backgroundCell)) { this._currentRow += `\u001b[${this._nullCellCount}C`; } else { @@ -421,8 +447,10 @@ class StringSerializeHandler extends BaseSerializeHandler { protected _serializeString(excludeFinalCursorPosition: boolean): string { let rowEnd = this._allRows.length; - // the fixup is only required for data without scrollback - // because it will always be placed at last line otherwise + /* + * the fixup is only required for data without scrollback + * because it will always be placed at last line otherwise + */ if (this._buffer.length - this._firstRow <= this._terminal.rows) { rowEnd = this._lastContentCursorRow + 1 - this._firstRow; this._lastCursorCol = this._lastContentCursorCol; @@ -466,9 +494,11 @@ class StringSerializeHandler extends BaseSerializeHandler { } } - // Restore the cursor's current style, see https://github.com/xtermjs/xterm.js/issues/3677 - // HACK: Internal API access since it's awkward to expose this in the API and serialize will - // likely be the only consumer + /* + * Restore the cursor's current style, see https://github.com/xtermjs/xterm.js/issues/3677 + * HACK: Internal API access since it's awkward to expose this in the API and serialize will + * likely be the only consumer + */ const curAttrData: IAttributeData = (this._terminal as any)._core._inputHandler._curAttrData; const sgrSeq = this._diffStyle(curAttrData, this._cursorStyle); if (sgrSeq.length > 0) { @@ -581,8 +611,10 @@ export class SerializeAddon implements ITerminalAddon, ISerializeApi { } } - // Cursor visibility (DECTCEM) - // Default: visible + /* + * Cursor visibility (DECTCEM) + * Default: visible + */ if (!modes.showCursor) { content += '\x1b[?25l'; } diff --git a/addons/addon-serialize/test/SerializeAddon.test.ts b/addons/addon-serialize/test/SerializeAddon.test.ts index fb42d807d..02188146d 100644 --- a/addons/addon-serialize/test/SerializeAddon.test.ts +++ b/addons/addon-serialize/test/SerializeAddon.test.ts @@ -416,11 +416,13 @@ test.describe('SerializeAddon', () => { '中文中文', '12中文', '中文12', - // This line is going to be wrapped at last character - // because it has line length of 11 (1+2*5). - // We concat it back without the null cell currently. - // But this may be incorrect. - // see also #3097 + /* + * This line is going to be wrapped at last character + * because it has line length of 11 (1+2*5). + * We concat it back without the null cell currently. + * But this may be incorrect. + * see also #3097 + */ '1中文中文中' ]; await ctx.proxy.write(lines.join('\r\n')); @@ -503,9 +505,11 @@ test.describe('SerializeAddon', () => { const MOVE_DOWN = (l: number): string => `\u001b[${l}B`; const MOVE_LEFT = (l: number): string => `\u001b[${l}D`; - // A line wrap happened after current line. - // But there is no content. - // so wrap shouldn't even be able to happen. + /* + * A line wrap happened after current line. + * But there is no content. + * so wrap shouldn't even be able to happen. + */ const segments = [ `123456789012345`, MOVE_UP(1), @@ -531,9 +535,11 @@ test.describe('SerializeAddon', () => { 10 ); - // A line wrap happened after current line. - // But there is no content. - // so wrap shouldn't even be able to happen. + /* + * A line wrap happened after current line. + * But there is no content. + * so wrap shouldn't even be able to happen. + */ const lines = [ padLines.join('\r\n'), '\r\n', diff --git a/addons/addon-unicode-graphemes/src/UnicodeGraphemeProvider.ts b/addons/addon-unicode-graphemes/src/UnicodeGraphemeProvider.ts index 3d5717a0f..e68185f76 100644 --- a/addons/addon-unicode-graphemes/src/UnicodeGraphemeProvider.ts +++ b/addons/addon-unicode-graphemes/src/UnicodeGraphemeProvider.ts @@ -22,9 +22,11 @@ export class UnicodeGraphemeProvider implements IUnicodeVersionProvider { = UnicodeService.createPropertyValue(UC.GRAPHEME_BREAK_Other, 1, false); public charProperties(codepoint: number, preceding: UnicodeCharProperties): UnicodeCharProperties { - // Optimize the simple ASCII case, under the condition that - // UnicodeService.extractCharKind(preceding) === GRAPHEME_BREAK_Other - // (which also covers the case that preceding === 0). + /* + * Optimize the simple ASCII case, under the condition that + * UnicodeService.extractCharKind(preceding) === GRAPHEME_BREAK_Other + * (which also covers the case that preceding === 0). + */ if ((codepoint >= 32 && codepoint < 127) && (preceding >> 3) === 0) { return UnicodeGraphemeProvider._plainNarrowProperties; } diff --git a/addons/addon-web-fonts/test/WebFontsAddon.test.ts b/addons/addon-web-fonts/test/WebFontsAddon.test.ts index c94bff84d..100e34f55 100644 --- a/addons/addon-web-fonts/test/WebFontsAddon.test.ts +++ b/addons/addon-web-fonts/test/WebFontsAddon.test.ts @@ -21,8 +21,10 @@ test.describe('WebFontsAddon', () => { deepStrictEqual(empty, []); }); test.afterEach(async () => { - // for font loading tests to work, we have to remove added rules and fonts - // to work around the quite aggressive font caching done by the browsers + /* + * for font loading tests to work, we have to remove added rules and fonts + * to work around the quite aggressive font caching done by the browsers + */ await ctx.page.evaluate(` document.styleSheets[0].deleteRule(1); document.styleSheets[0].deleteRule(0); @@ -73,8 +75,10 @@ test.describe('WebFontsAddon', () => { }); test('autoload & relayout from ctor', async ({ browser }) => { - // to make this test work, we exclude the default measurement char W (x57) by restricting unicode-range - // now the browser will postpone font loading until codepoint is hit --> wrong glyph metrics on first usage + /* + * to make this test work, we exclude the default measurement char W (x57) by restricting unicode-range + * now the browser will postpone font loading until codepoint is hit --> wrong glyph metrics on first usage + */ const data = await ctx.page.evaluate(` document.styleSheets[0].insertRule("@font-face {font-family: Kongtext; src: url(/fonts/kongtext.regular.ttf) format('truetype'); unicode-range: U+00A0-00FF}", 0); `); @@ -97,8 +101,10 @@ test.describe('WebFontsAddon', () => { deepStrictEqual(await getDocumentFonts(), [{ family: 'Kongtext', status: 'unloaded' }]); } - // good case: addon fixes layout for webfont in ctor - // the relayout happens async, so wait a bit with a promise + /* + * good case: addon fixes layout for webfont in ctor + * the relayout happens async, so wait a bit with a promise + */ await ctx.page.evaluate(` window.helperTerm.dispose(); window.helperTerm = new Terminal({fontFamily: '"Kongtext", ' + term.options.fontFamily}); diff --git a/addons/addon-web-links/src/WebLinkProvider.ts b/addons/addon-web-links/src/WebLinkProvider.ts index efaead5c4..3f4a894dd 100644 --- a/addons/addon-web-links/src/WebLinkProvider.ts +++ b/addons/addon-web-links/src/WebLinkProvider.ts @@ -73,8 +73,10 @@ export class LinkComputer { continue; } - // map string positions back to buffer positions - // values are 0-based right side excluding + /* + * map string positions back to buffer positions + * values are 0-based right side excluding + */ const [startY, startX] = LinkComputer._mapStrIdx(terminal, startLineIndex, 0, match.index); const [endY, endX] = LinkComputer._mapStrIdx(terminal, startY, startX, text.length); @@ -172,11 +174,13 @@ export class LinkComputer { if (width) { stringIndex -= chars.length || 1; - // correct stringIndex for early wrapped wide chars: - // - currently only happens at last cell - // - cells to the right are reset with chars='' and width=1 in InputHandler.print - // - follow-up line must be wrapped and contain wide char at first cell - // --> if all these conditions are met, correct stringIndex by +1 + /* + * correct stringIndex for early wrapped wide chars: + * - currently only happens at last cell + * - cells to the right are reset with chars='' and width=1 in InputHandler.print + * - follow-up line must be wrapped and contain wide char at first cell + * --> if all these conditions are met, correct stringIndex by +1 + */ if (i === line.length - 1 && chars === '') { const line = buf.getLine(lineIndex + 1); if (line && line.isWrapped) { diff --git a/addons/addon-web-links/src/WebLinksAddon.ts b/addons/addon-web-links/src/WebLinksAddon.ts index 3328a3c3f..0a7171987 100644 --- a/addons/addon-web-links/src/WebLinksAddon.ts +++ b/addons/addon-web-links/src/WebLinksAddon.ts @@ -7,17 +7,19 @@ import type { Terminal, ITerminalAddon, IDisposable } from '@xterm/xterm'; import type { WebLinksAddon as IWebLinksApi } from '@xterm/addon-web-links'; import { ILinkProviderOptions, WebLinkProvider } from './WebLinkProvider'; -// consider everthing starting with http:// or https:// -// up to first whitespace, `"` or `'` as url -// NOTE: The repeated end clause is needed to not match a dangling `:` -// resembling the old (...)*([^:"\'\\s]) final path clause -// additionally exclude early + final: -// - unsafe from rfc3986: !*'() -// - unsafe chars from rfc1738: {}|\^~[]` (minus [] as we need them for ipv6 adresses, also allow ~) -// also exclude as finals: -// - final interpunction like ,.!? -// - any sort of brackets <>()[]{} (not spec conform, but often used to enclose urls) -// - unsafe chars from rfc1738: {}|\^~[]` +/* + * consider everthing starting with http:// or https:// + * up to first whitespace, `"` or `'` as url + * NOTE: The repeated end clause is needed to not match a dangling `:` + * resembling the old (...)*([^:"\'\\s]) final path clause + * additionally exclude early + final: + * - unsafe from rfc3986: !*'() + * - unsafe chars from rfc1738: {}|\^~[]` (minus [] as we need them for ipv6 adresses, also allow ~) + * also exclude as finals: + * - final interpunction like ,.!? + * - any sort of brackets <>()[]{} (not spec conform, but often used to enclose urls) + * - unsafe chars from rfc1738: {}|\^~[]` + */ const strictUrlRegex = /(https?|HTTPS?):[/]{2}[^\s"'!*(){}|\\\^<>`]*[^\s"':,.!?{}|\\\^~\[\]`()<>]/; diff --git a/addons/addon-webgl/src/CellColorResolver.ts b/addons/addon-webgl/src/CellColorResolver.ts index 3a4661cc3..fe8baa35a 100644 --- a/addons/addon-webgl/src/CellColorResolver.ts +++ b/addons/addon-webgl/src/CellColorResolver.ts @@ -47,8 +47,10 @@ export class CellColorResolver { this.result.bg = cell.bg; this.result.fg = cell.fg; this.result.ext = cell.bg & BgFlags.HAS_EXTENDED ? cell.extended.ext : 0; - // Get any foreground/background overrides, this happens on the model to avoid spreading - // override logic throughout the different sub-renderers + /* + * Get any foreground/background overrides, this happens on the model to avoid spreading + * override logic throughout the different sub-renderers + */ // Reset overrides work variables $bg = 0; @@ -186,8 +188,10 @@ export class CellColorResolver { } }); - // Convert any overrides from rgba to the fg/bg packed format. This resolves the inverse flag - // ahead of time in order to use the correct cache key + /* + * Convert any overrides from rgba to the fg/bg packed format. This resolves the inverse flag + * ahead of time in order to use the correct cache key + */ if ($hasBg) { if ($isSelected) { // Non-RGB attributes from model + force non-dim + override + force RGB color mode @@ -202,8 +206,10 @@ export class CellColorResolver { $fg = (cell.fg & ~Attributes.RGB_MASK & ~FgFlags.INVERSE) | $fg | Attributes.CM_RGB; } - // Handle case where inverse was specified by only one of bg override or fg override was set, - // resolving the other inverse color and setting the inverse flag if needed. + /* + * Handle case where inverse was specified by only one of bg override or fg override was set, + * resolving the other inverse color and setting the inverse flag if needed. + */ if (this.result.fg & FgFlags.INVERSE) { if ($hasBg && !$hasFg) { // Resolve bg color type (default color has a different meaning in fg vs bg) diff --git a/addons/addon-webgl/src/CharAtlasCache.ts b/addons/addon-webgl/src/CharAtlasCache.ts index 823a74cc6..5581ea22d 100644 --- a/addons/addon-webgl/src/CharAtlasCache.ts +++ b/addons/addon-webgl/src/CharAtlasCache.ts @@ -13,8 +13,10 @@ import type { ILogService } from 'common/services/Services'; interface ITextureAtlasCacheEntry { atlas: ITextureAtlas; config: ICharAtlasConfig; - // N.B. This implementation potentially holds onto copies of the terminal forever, so - // this may cause memory leaks. + /* + * N.B. This implementation potentially holds onto copies of the terminal forever, so + * this may cause memory leaks. + */ ownedBy: Terminal[]; } diff --git a/addons/addon-webgl/src/CharAtlasUtils.ts b/addons/addon-webgl/src/CharAtlasUtils.ts index 4ce103e98..15e796256 100644 --- a/addons/addon-webgl/src/CharAtlasUtils.ts +++ b/addons/addon-webgl/src/CharAtlasUtils.ts @@ -25,8 +25,10 @@ export function generateConfig(deviceCellWidth: number, deviceCellHeight: number scrollbarSliderBackground: NULL_COLOR, scrollbarSliderHoverBackground: NULL_COLOR, scrollbarSliderActiveBackground: NULL_COLOR, - // For the static char atlas, we only use the first 16 colors, but we need all 256 for the - // dynamic character atlas. + /* + * For the static char atlas, we only use the first 16 colors, but we need all 256 for the + * dynamic character atlas. + */ ansi: colors.ansi.slice(), contrastCache: colors.contrastCache, halfContrastCache: colors.halfContrastCache diff --git a/addons/addon-webgl/src/Constants.ts b/addons/addon-webgl/src/Constants.ts index a00434d61..af08fe8bb 100644 --- a/addons/addon-webgl/src/Constants.ts +++ b/addons/addon-webgl/src/Constants.ts @@ -6,7 +6,9 @@ import { isFirefox, isLegacyEdge } from 'common/Platform'; export const DIM_OPACITY = 0.5; -// The text baseline is set conditionally by browser. Using 'ideographic' for Firefox or Legacy Edge -// would result in truncated text (Issue 3353). Using 'bottom' for Chrome would result in slightly -// unaligned Powerline fonts (PR 3356#issuecomment-850928179). +/* + * The text baseline is set conditionally by browser. Using 'ideographic' for Firefox or Legacy Edge + * would result in truncated text (Issue 3353). Using 'bottom' for Chrome would result in slightly + * unaligned Powerline fonts (PR 3356#issuecomment-850928179). + */ export const TEXT_BASELINE: CanvasTextBaseline = isFirefox || isLegacyEdge ? 'bottom' : 'ideographic'; diff --git a/addons/addon-webgl/src/CursorBlinkStateManager.ts b/addons/addon-webgl/src/CursorBlinkStateManager.ts index 688aee7b7..1e9695c94 100644 --- a/addons/addon-webgl/src/CursorBlinkStateManager.ts +++ b/addons/addon-webgl/src/CursorBlinkStateManager.ts @@ -87,13 +87,17 @@ export class CursorBlinkStateManager { this._blinkInterval = undefined; } - // Setup the initial timeout which will hide the cursor, this is done before - // the regular interval is setup in order to support restarting the blink - // animation in a lightweight way (without thrashing clearInterval and - // setInterval). + /* + * Setup the initial timeout which will hide the cursor, this is done before + * the regular interval is setup in order to support restarting the blink + * animation in a lightweight way (without thrashing clearInterval and + * setInterval). + */ this._blinkStartTimeout = this._coreBrowserService.window.setTimeout(() => { - // Check if another animation restart was requested while this was being - // started + /* + * Check if another animation restart was requested while this was being + * started + */ if (this._animationTimeRestarted) { const time = Constants.BLINK_INTERVAL - (Date.now() - this._animationTimeRestarted); this._animationTimeRestarted = undefined; @@ -114,8 +118,10 @@ export class CursorBlinkStateManager { this._blinkInterval = this._coreBrowserService.window.setInterval(() => { // Adjust the animation time if it was restarted if (this._animationTimeRestarted) { - // calc time diff - // Make restart interval do a setTimeout initially? + /* + * calc time diff + * Make restart interval do a setTimeout initially? + */ const time = Constants.BLINK_INTERVAL - (Date.now() - this._animationTimeRestarted); this._animationTimeRestarted = undefined; this._restartInterval(time); diff --git a/addons/addon-webgl/src/DevicePixelObserver.ts b/addons/addon-webgl/src/DevicePixelObserver.ts index dcd8de0a6..c7af6a1bb 100644 --- a/addons/addon-webgl/src/DevicePixelObserver.ts +++ b/addons/addon-webgl/src/DevicePixelObserver.ts @@ -6,10 +6,12 @@ import { toDisposable, IDisposable } from 'common/Lifecycle'; export function observeDevicePixelDimensions(element: HTMLElement, parentWindow: Window & typeof globalThis, callback: (deviceWidth: number, deviceHeight: number) => void): IDisposable { - // Observe any resizes to the element and extract the actual pixel size of the element if the - // devicePixelContentBoxSize API is supported. This allows correcting rounding errors when - // converting between CSS pixels and device pixels which causes blurry rendering when device - // pixel ratio is not a round number. + /* + * Observe any resizes to the element and extract the actual pixel size of the element if the + * devicePixelContentBoxSize API is supported. This allows correcting rounding errors when + * converting between CSS pixels and device pixels which causes blurry rendering when device + * pixel ratio is not a round number. + */ let observer: ResizeObserver | undefined = new parentWindow.ResizeObserver((entries) => { const entry = entries.find((entry) => entry.target === element); if (!entry) { diff --git a/addons/addon-webgl/src/GlyphRenderer.ts b/addons/addon-webgl/src/GlyphRenderer.ts index 46b8efeb9..82ca01b63 100644 --- a/addons/addon-webgl/src/GlyphRenderer.ts +++ b/addons/addon-webgl/src/GlyphRenderer.ts @@ -148,9 +148,11 @@ export class GlyphRenderer extends Disposable { gl.enableVertexAttribArray(VertexAttribLocations.UNIT_QUAD); gl.vertexAttribPointer(VertexAttribLocations.UNIT_QUAD, 2, this._gl.FLOAT, false, 0, 0); - // Setup the unit quad element array buffer, this points to indices in - // unitQuadVertices to allow is to draw 2 triangles from the vertices via a - // triangle strip + /* + * Setup the unit quad element array buffer, this points to indices in + * unitQuadVertices to allow is to draw 2 triangles from the vertices via a + * triangle strip + */ const unitQuadElementIndices = new Uint8Array([0, 1, 2, 3]); const elementIndicesBuffer = gl.createBuffer(); this._register(toDisposable(() => gl.deleteBuffer(elementIndicesBuffer))); @@ -189,8 +191,10 @@ export class GlyphRenderer extends Disposable { gl.uniform1iv(this._textureLocation, textureUnits); gl.uniformMatrix4fv(this._projectionLocation, false, PROJECTION_MATRIX); - // Setup 1x1 red pixel textures for all potential atlas pages, if one of these invalid textures - // is ever drawn it will show characters as red rectangles. + /* + * Setup 1x1 red pixel textures for all potential atlas pages, if one of these invalid textures + * is ever drawn it will show characters as red rectangles. + */ this._atlasTextures = []; for (let i = 0; i < TextureAtlas.maxAtlasPages; i++) { const glTexture = new GLTexture(throwIfFalsy(gl.createTexture())); @@ -216,18 +220,22 @@ export class GlyphRenderer extends Disposable { } public updateCell(x: number, y: number, code: number, bg: number, fg: number, ext: number, chars: string, width: number, lastBg: number): void { - // Since this function is called for every cell (`rows*cols`), it must be very optimized. It - // should not instantiate any variables unless a new glyph is drawn to the cache where the - // slight slowdown is acceptable for the developer ergonomics provided as it's a once of for - // each glyph. + /* + * Since this function is called for every cell (`rows*cols`), it must be very optimized. It + * should not instantiate any variables unless a new glyph is drawn to the cache where the + * slight slowdown is acceptable for the developer ergonomics provided as it's a once of for + * each glyph. + */ this._updateCell(this._vertices.attributes, x, y, code, bg, fg, ext, chars, width, lastBg); } private _updateCell(array: Float32Array, x: number, y: number, code: number | undefined, bg: number, fg: number, ext: number, chars: string, width: number, lastBg: number): void { $i = (y * this._terminal.cols + x) * Constants.INDICES_PER_CELL; - // Exit early if this is a null character, allow space character to continue as it may have - // underline/strikethrough styles + /* + * Exit early if this is a null character, allow space character to continue as it may have + * underline/strikethrough styles + */ if (code === NULL_CELL_CODE || code === undefined/* This is used for the right side of wide chars */) { array.fill(0, $i, $i + Constants.INDICES_PER_CELL - 1 - Constants.CELL_POSITION_INDICES); return; @@ -279,8 +287,10 @@ export class GlyphRenderer extends Disposable { } // a_cellpos only changes on resize - // Reduce scale horizontally for wide glyphs printed in cells that would overlap with the - // following cell (ie. the width is not 2). + /* + * Reduce scale horizontally for wide glyphs printed in cells that would overlap with the + * following cell (ie. the width is not 2). + */ if (this._optionsService.rawOptions.rescaleOverlappingGlyphs) { if (allowRescaling(code, width, $glyph.size.x, this._dimensions.device.cell.width)) { array[$i + 2] = (this._dimensions.device.cell.width - 1) / this._dimensions.device.canvas.width; // - 1 to improve readability @@ -339,13 +349,15 @@ export class GlyphRenderer extends Disposable { this._activeBuffer = (this._activeBuffer + 1) % 2; const activeBuffer = this._vertices.attributesBuffers[this._activeBuffer]; - // Copy data for each cell of each line up to its line length (the last non-whitespace cell) - // from the attributes buffer into activeBuffer, which is the one that gets bound to the GPU. - // The reasons for this are as follows: - // - So the active buffer can be alternated so we don't get blocked on rendering finishing - // - To copy either the normal attributes buffer or the selection attributes buffer when there - // is a selection - // - So we don't send vertices for all the line-ending whitespace to the GPU + /* + * Copy data for each cell of each line up to its line length (the last non-whitespace cell) + * from the attributes buffer into activeBuffer, which is the one that gets bound to the GPU. + * The reasons for this are as follows: + * - So the active buffer can be alternated so we don't get blocked on rendering finishing + * - To copy either the normal attributes buffer or the selection attributes buffer when there + * is a selection + * - So we don't send vertices for all the line-ending whitespace to the GPU + */ let bufferLength = 0; for (let y = 0; y < renderModel.lineLengths.length; y++) { const si = y * this._terminal.cols * Constants.INDICES_PER_CELL; @@ -358,9 +370,11 @@ export class GlyphRenderer extends Disposable { gl.bindBuffer(gl.ARRAY_BUFFER, this._attributesBuffer); gl.bufferData(gl.ARRAY_BUFFER, activeBuffer.subarray(0, bufferLength), gl.STREAM_DRAW); - // Bind the atlas page texture if they have changed. AtlasPage.version is globally - // monotonic, so a page object swap at the same index (which happens after a page merge) - // is detected by the same comparison. + /* + * Bind the atlas page texture if they have changed. AtlasPage.version is globally + * monotonic, so a page object swap at the same index (which happens after a page merge) + * is detected by the same comparison. + */ for (let i = 0; i < this._atlas.pages.length; i++) { if (this._atlas.pages[i].version !== this._atlasTextures[i].version) { this._bindAtlasPageTexture(gl, this._atlas, i); diff --git a/addons/addon-webgl/src/RectangleRenderer.ts b/addons/addon-webgl/src/RectangleRenderer.ts index 81e129e5c..e87bc690c 100644 --- a/addons/addon-webgl/src/RectangleRenderer.ts +++ b/addons/addon-webgl/src/RectangleRenderer.ts @@ -114,9 +114,11 @@ export class RectangleRenderer extends Disposable { gl.enableVertexAttribArray(VertexAttribLocations.UNIT_QUAD); gl.vertexAttribPointer(VertexAttribLocations.UNIT_QUAD, 2, this._gl.FLOAT, false, 0, 0); - // Setup the unit quad element array buffer, this points to indices in - // unitQuadVertices to allow is to draw 2 triangles from the vertices via a - // triangle strip + /* + * Setup the unit quad element array buffer, this points to indices in + * unitQuadVertices to allow is to draw 2 triangles from the vertices via a + * triangle strip + */ const unitQuadElementIndices = new Uint8Array([0, 1, 2, 3]); const elementIndicesBuffer = gl.createBuffer(); this._register(toDisposable(() => gl.deleteBuffer(elementIndicesBuffer))); diff --git a/addons/addon-webgl/src/TextureAtlas.ts b/addons/addon-webgl/src/TextureAtlas.ts index 34188235c..09293d4fa 100644 --- a/addons/addon-webgl/src/TextureAtlas.ts +++ b/addons/addon-webgl/src/TextureAtlas.ts @@ -151,13 +151,17 @@ export class TextureAtlas implements ITextureAtlas { } private _createNewPage(): AtlasPage { - // Try merge the set of the 4 most used pages of the largest size. This is is deferred to a - // microtask to ensure it does not interrupt textures that will be rendered in the current - // animation frame which would result in blank rendered areas. This is actually not that - // expensive relative to drawing the glyphs, so there is no need to wait for an idle callback. + /* + * Try merge the set of the 4 most used pages of the largest size. This is is deferred to a + * microtask to ensure it does not interrupt textures that will be rendered in the current + * animation frame which would result in blank rendered areas. This is actually not that + * expensive relative to drawing the glyphs, so there is no need to wait for an idle callback. + */ if (TextureAtlas.maxAtlasPages && this._pages.length >= Math.max(4, TextureAtlas.maxAtlasPages)) { - // Find the set of the largest 4 images, below the maximum size, with the highest - // percentages used + /* + * Find the set of the largest 4 images, below the maximum size, with the highest + * percentages used + */ const pagesBySize = this._pages.filter(e => { return e.canvas.width * 2 <= (TextureAtlas.maxTextureSize || Constants.FORCED_MAX_TEXTURE_SIZE); }).sort((a, b) => { @@ -180,8 +184,10 @@ export class TextureAtlas implements ITextureAtlas { // Gather details of the merge const mergingPages = pagesBySize.slice(sameSizeI, sameSizeI + 4); - // Only proceed with merge if we have exactly 4 same-sized pages. If not, we cannot - // effectively reduce page count and merging would cause issues. + /* + * Only proceed with merge if we have exactly 4 same-sized pages. If not, we cannot + * effectively reduce page count and merging would cause issues. + */ if (mergingPages.length < 4 || mergingPages.some(p => p.canvas.width !== mergingPages[0].canvas.width)) { const newPage = new AtlasPage(this._document, this._textureSize); this._pages.push(newPage); @@ -294,9 +300,11 @@ export class TextureAtlas implements ITextureAtlas { private _getBackgroundColor(bgColorMode: number, bgColor: number, inverse: boolean, dim: boolean): IColor { if (this._config.allowTransparency) { - // The background color might have some transparency, so we need to render it as fully - // transparent in the atlas. Otherwise we'd end up drawing the transparent background twice - // around the anti-aliased edges of the glyph, and it would look too dark. + /* + * The background color might have some transparency, so we need to render it as fully + * transparent in the atlas. Otherwise we'd end up drawing the transparent background twice + * around the anti-aliased edges of the glyph, and it would look too dark. + */ return NULL_COLOR; } @@ -418,8 +426,10 @@ export class TextureAtlas implements ITextureAtlas { const bgRgba = this._resolveBackgroundRgba(bgColorMode, bgColor, inverse); const fgRgba = this._resolveForegroundRgba(fgColorMode, fgColor, inverse, bold); - // Dim cells only require half the contrast, otherwise they wouldn't be distinguishable from - // non-dim cells + /* + * Dim cells only require half the contrast, otherwise they wouldn't be distinguishable from + * non-dim cells + */ const result = rgba.ensureContrastRatio(bgRgba, fgRgba, this._config.minimumContrastRatio / (dim ? 2 : 1)); if (!result) { @@ -447,20 +457,26 @@ export class TextureAtlas implements ITextureAtlas { private _drawToCache(codeOrChars: number | string, bg: number, fg: number, ext: number, restrictToCellHeight: boolean, domContainer: HTMLElement | undefined): IRasterizedGlyph { const chars = typeof codeOrChars === 'number' ? String.fromCharCode(codeOrChars) : codeOrChars; - // Uncomment for debugging - // console.log(`draw to cache "${chars}"`, bg, fg, ext); + /* + * Uncomment for debugging + * console.log(`draw to cache "${chars}"`, bg, fg, ext); + */ - // Attach the canvas to the DOM in order to inherit font-feature-settings - // from the parent elements. This is necessary for ligatures and variants to - // work. + /* + * Attach the canvas to the DOM in order to inherit font-feature-settings + * from the parent elements. This is necessary for ligatures and variants to + * work. + */ if (domContainer && this._tmpCanvas.parentElement !== domContainer) { this._tmpCanvas.style.display = 'none'; domContainer.append(this._tmpCanvas); } - // Allow 1 cell width per character, with a minimum of 2 (CJK), plus some padding. This is used - // to draw the glyph to the canvas as well as to restrict the bounding box search to ensure - // giant ligatures (eg. =====>) don't impact overall performance. + /* + * Allow 1 cell width per character, with a minimum of 2 (CJK), plus some padding. This is used + * to draw the glyph to the canvas as well as to restrict the bounding box search to ensure + * giant ligatures (eg. =====>) don't impact overall performance. + */ const allowedWidth = Math.min(this._config.deviceCellWidth * Math.max(chars.length, 2) + TMP_CANVAS_GLYPH_PADDING * 2, this._config.deviceMaxTextureSize); if (this._tmpCanvas.width < allowedWidth) { this._tmpCanvas.width = allowedWidth; @@ -503,8 +519,10 @@ export class TextureAtlas implements ITextureAtlas { // draw the background const backgroundColor = this._getBackgroundColor(bgColorMode, bgColor, inverse, dim); - // Use a 'copy' composite operation to clear any existing glyph out of _tmpCtxWithAlpha, - // regardless of transparency in backgroundColor + /* + * Use a 'copy' composite operation to clear any existing glyph out of _tmpCtxWithAlpha, + * regardless of transparency in backgroundColor + */ this._tmpCtx.globalCompositeOperation = 'copy'; this._tmpCtx.fillStyle = backgroundColor.css; this._tmpCtx.fillRect(0, 0, this._tmpCanvas.width, this._tmpCanvas.height); @@ -532,9 +550,11 @@ export class TextureAtlas implements ITextureAtlas { customGlyph = tryDrawCustomGlyph(this._tmpCtx, chars, padding, padding, this._config.deviceCellWidth, this._config.deviceCellHeight, this._config.deviceCharWidth, this._config.deviceCharHeight, this._config.fontSize, this._config.devicePixelRatio, backgroundColor.css, variantOffset); } - // Whether to clear pixels based on a threshold difference between the glyph color and the - // background color. This should be disabled when the glyph contains multiple colors such as - // underline colors to prevent important colors could get cleared. + /* + * Whether to clear pixels based on a threshold difference between the glyph color and the + * background color. This should be disabled when the glyph contains multiple colors such as + * underline colors to prevent important colors could get cleared. + */ let enableClearThresholdCheck = !powerlineGlyph; let chWidth: number; @@ -599,8 +619,10 @@ export class TextureAtlas implements ITextureAtlas { clipRegion.rect(xChLeft, yTop, this._config.deviceCellWidth, yBot - yTop); this._tmpCtx.clip(clipRegion); - // Draw a zigzag pattern, this is derived from the SVG used in monaco for the same - // style. The viewbox is 6x3 so scale it using that. + /* + * Draw a zigzag pattern, this is derived from the SVG used in monaco for the same + * style. The viewbox is 6x3 so scale it using that. + */ const cellW = this._config.deviceCellWidth; const curlyH = (yBot - yTop); const scaleX = cellW / 6; @@ -671,15 +693,21 @@ export class TextureAtlas implements ITextureAtlas { } this._tmpCtx.restore(); - // Draw stroke in the background color for non custom characters in order to give an outline - // between the text and the underline. Only do this when font size is >= 12 as the underline - // looks odd when the font size is too small + /* + * Draw stroke in the background color for non custom characters in order to give an outline + * between the text and the underline. Only do this when font size is >= 12 as the underline + * looks odd when the font size is too small + */ if (!customGlyph && this._config.fontSize >= 12) { - // This only works when transparency is disabled because it's not clear how to clear stroked - // text + /* + * This only works when transparency is disabled because it's not clear how to clear stroked + * text + */ if (!this._config.allowTransparency && chars !== ' ') { - // Measure the text, only draw the stroke if there is a descent beyond an alphabetic text - // baseline + /* + * Measure the text, only draw the stroke if there is a descent beyond an alphabetic text + * baseline + */ this._tmpCtx.save(); this._tmpCtx.textBaseline = 'alphabetic'; const metrics = this._tmpCtx.measureText(chars); @@ -687,9 +715,11 @@ export class TextureAtlas implements ITextureAtlas { if ('actualBoundingBoxDescent' in metrics && metrics.actualBoundingBoxDescent > 0) { // This translates to 1/2 the line width in either direction this._tmpCtx.save(); - // Clip the region to only draw in valid pixels near the underline to avoid a slight - // outline around the whole glyph, as well as additional pixels in the glyph at the top - // which would increase GPU memory demands + /* + * Clip the region to only draw in valid pixels near the underline to avoid a slight + * outline around the whole glyph, as well as additional pixels in the glyph at the top + * which would increase GPU memory demands + */ const clipRegion = new Path2D(); clipRegion.rect(xLeft, yTop - Math.ceil(lineWidth / 2), this._config.deviceCellWidth * chWidth, yBot - yTop + Math.ceil(lineWidth / 2)); this._tmpCtx.clip(clipRegion); @@ -719,8 +749,10 @@ export class TextureAtlas implements ITextureAtlas { this._tmpCtx.fillText(chars, padding, padding + this._config.deviceCharHeight); } - // If this character is underscore and beyond the cell bounds, shift it up until it is visible - // even on the bottom row, try for a maximum of 5 pixels. + /* + * If this character is underscore and beyond the cell bounds, shift it up until it is visible + * even on the bottom row, try for a maximum of 5 pixels. + */ if (chars === '_' && !this._config.allowTransparency) { let isBeyondCellBounds = clearColor(this._tmpCtx.getImageData(padding, padding, this._config.deviceCellWidth, this._config.deviceCellHeight), backgroundColor, foregroundColor, enableClearThresholdCheck); if (isBeyondCellBounds) { @@ -752,8 +784,10 @@ export class TextureAtlas implements ITextureAtlas { this._tmpCtx.restore(); - // clear the background from the character to avoid issues with drawing over the previous - // character if it extends past it's bounds + /* + * clear the background from the character to avoid issues with drawing over the previous + * character if it extends past it's bounds + */ const imageData = this._tmpCtx.getImageData( 0, 0, this._tmpCanvas.width, this._tmpCanvas.height ); @@ -796,12 +830,16 @@ export class TextureAtlas implements ITextureAtlas { } } - // TODO: This algorithm could be simplified: - // - Search for the page with ROW_PIXEL_THRESHOLD in mind - // - Keep track of current/fixed rows in a Map + /* + * TODO: This algorithm could be simplified: + * - Search for the page with ROW_PIXEL_THRESHOLD in mind + * - Keep track of current/fixed rows in a Map + */ - // Replace the best current row with a fixed row if there is one at least as good as the - // current row. Search in reverse to prioritize filling in older pages. + /* + * Replace the best current row with a fixed row if there is one at least as good as the + * current row. Search in reverse to prioritize filling in older pages. + */ for (let i = this._activePages.length - 1; i >= 0; i--) { for (const row of this._activePages[i].fixedRows) { if (row.height <= activeRow.height && rasterizedGlyph.size.y <= row.height) { @@ -832,12 +870,16 @@ export class TextureAtlas implements ITextureAtlas { break; } - // Create a new page if too much vertical space would be wasted or there is not enough room - // left in the page. The previous active row will become fixed in the process as it now has a - // fixed height + /* + * Create a new page if too much vertical space would be wasted or there is not enough room + * left in the page. The previous active row will become fixed in the process as it now has a + * fixed height + */ if (activeRow.y + rasterizedGlyph.size.y >= activePage.canvas.height || activeRow.height > rasterizedGlyph.size.y + Constants.ROW_PIXEL_THRESHOLD) { - // Create the new fixed height row, creating a new page if there isn't enough room on the - // current page + /* + * Create the new fixed height row, creating a new page if there isn't enough room on the + * current page + */ let wasPageAndRowFound = false; if (activePage.currentRow.y + activePage.currentRow.height + rasterizedGlyph.size.y >= activePage.canvas.height) { // Find the first page with room to create the new row on @@ -852,10 +894,12 @@ export class TextureAtlas implements ITextureAtlas { if (candidatePage) { activePage = candidatePage; } else { - // Before creating a new atlas page that would trigger a page merge, check if the - // current active row is sufficient when ignoring the ROW_PIXEL_THRESHOLD. This will - // improve texture utilization by using the available space before the page is merged - // and becomes static. + /* + * Before creating a new atlas page that would trigger a page merge, check if the + * current active row is sufficient when ignoring the ROW_PIXEL_THRESHOLD. This will + * improve texture utilization by using the available space before the page is merged + * and becomes static. + */ if ( TextureAtlas.maxAtlasPages && this._pages.length >= TextureAtlas.maxAtlasPages && @@ -923,8 +967,10 @@ export class TextureAtlas implements ITextureAtlas { rasterizedGlyph.sizeClipSpace.x /= activePage.canvas.width; rasterizedGlyph.sizeClipSpace.y /= activePage.canvas.height; - // Update atlas current row, for fixed rows the glyph height will never be larger than the row - // height + /* + * Update atlas current row, for fixed rows the glyph height will never be larger than the row + * height + */ activeRow.height = Math.max(activeRow.height, rasterizedGlyph.size.y); activeRow.x += rasterizedGlyph.size.x; @@ -1057,16 +1103,18 @@ class AtlasPage { public static nextVersion: number = 0; public version = ++AtlasPage.nextVersion; - // Texture atlas current positioning data. The texture packing strategy used is to fill from - // left-to-right and top-to-bottom. When the glyph being written is less than half of the current - // row's height, the following happens: - // - // - The current row becomes the fixed height row A - // - A new fixed height row B the exact size of the glyph is created below the current row - // - A new dynamic height current row is created below B - // - // This strategy does a good job preventing space being wasted for very short glyphs such as - // underscores, hyphens etc. or those with underlines rendered. + /* + * Texture atlas current positioning data. The texture packing strategy used is to fill from + * left-to-right and top-to-bottom. When the glyph being written is less than half of the current + * row's height, the following happens: + * + * - The current row becomes the fixed height row A + * - A new fixed height row B the exact size of the glyph is created below the current row + * - A new dynamic height current row is created below B + * + * This strategy does a good job preventing space being wasted for very short glyphs such as + * underscores, hyphens etc. or those with underlines rendered. + */ public currentRow: ICharAtlasActiveRow = { x: 0, y: 0, @@ -1086,9 +1134,11 @@ class AtlasPage { } } this.canvas = createCanvas(document, size, size); - // 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 - // set. + /* + * 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 + * set. + */ this.ctx = throwIfFalsy(this.canvas.getContext('2d', { alpha: true })); } @@ -1116,12 +1166,14 @@ function clearColor(imageData: ImageData, bg: IColor, fg: IColor, enableThreshol const fgG = fg.rgba >>> 16 & 0xFF; const fgB = fg.rgba >>> 8 & 0xFF; - // Calculate a threshold that when below a color will be treated as transpart when the sum of - // channel value differs. This helps improve rendering when glyphs overlap with others. This - // threshold is calculated relative to the difference between the background and foreground to - // ensure important details of the glyph are always shown, even when the contrast ratio is low. - // The number 12 is largely arbitrary to ensure the pixels that escape the cell in the test case - // were covered (fg=#8ae234, bg=#c4a000). + /* + * Calculate a threshold that when below a color will be treated as transpart when the sum of + * channel value differs. This helps improve rendering when glyphs overlap with others. This + * threshold is calculated relative to the difference between the background and foreground to + * ensure important details of the glyph are always shown, even when the contrast ratio is low. + * The number 12 is largely arbitrary to ensure the pixels that escape the cell in the test case + * were covered (fg=#8ae234, bg=#c4a000). + */ const threshold = Math.floor((Math.abs(r - fgR) + Math.abs(g - fgG) + Math.abs(b - fgB)) / 12); // Set alpha channel of relevent pixels to 0 diff --git a/addons/addon-webgl/src/WebglRenderer.ts b/addons/addon-webgl/src/WebglRenderer.ts index 500308bd1..2997129f2 100644 --- a/addons/addon-webgl/src/WebglRenderer.ts +++ b/addons/addon-webgl/src/WebglRenderer.ts @@ -84,9 +84,11 @@ export class WebglRenderer extends Disposable implements IRenderer { ) { super(); - // IMPORTANT: Canvas initialization and fetching of the context must be first in order to - // prevent possible listeners leaking and continuing to operate after the WebglRenderer has been - // discarded. + /* + * IMPORTANT: Canvas initialization and fetching of the context must be first in order to + * prevent possible listeners leaking and continuing to operate after the WebglRenderer has been + * discarded. + */ this._canvas = this._coreBrowserService.mainDocument.createElement('canvas'); const contextAttributes = { antialias: false, @@ -125,8 +127,10 @@ export class WebglRenderer extends Disposable implements IRenderer { console.log('webglcontextlost event received'); // Prevent the default behavior in order to enable WebGL context restoration. e.preventDefault(); - // Wait a few seconds to see if the 'webglcontextrestored' event is fired. - // If not, dispatch the onContextLoss notification to observers. + /* + * Wait a few seconds to see if the 'webglcontextrestored' event is fired. + * If not, dispatch the onContextLoss notification to observers. + */ this._contextRestorationTimeout = setTimeout(() => { this._contextRestorationTimeout = undefined; console.warn('webgl context not restored; firing onContextLoss'); @@ -137,8 +141,10 @@ export class WebglRenderer extends Disposable implements IRenderer { console.warn('webglcontextrestored event received'); clearTimeout(this._contextRestorationTimeout); this._contextRestorationTimeout = undefined; - // The texture atlas and glyph renderer must be fully reinitialized - // because their contents have been lost. + /* + * The texture atlas and glyph renderer must be fully reinitialized + * because their contents have been lost. + */ removeTerminalFromCache(this._terminal); this._initializeWebGLState(); this._requestRedrawViewport(); @@ -178,8 +184,10 @@ export class WebglRenderer extends Disposable implements IRenderer { } public handleDevicePixelRatioChange(): void { - // If the device pixel ratio changed, the char atlas needs to be regenerated - // and the terminal needs to refreshed + /* + * If the device pixel ratio changed, the char atlas needs to be regenerated + * and the terminal needs to refreshed + */ if (this._devicePixelRatio !== this._coreBrowserService.dpr) { this._devicePixelRatio = this._coreBrowserService.dpr; this.handleResize(this._terminal.cols, this._terminal.rows); @@ -215,8 +223,10 @@ export class WebglRenderer extends Disposable implements IRenderer { this._refreshCharAtlas(); - // Force a full refresh. Resizing `_glyphRenderer` should clear it already, - // so there is no need to clear it again here. + /* + * Force a full refresh. Resizing `_glyphRenderer` should clear it already, + * so there is no need to clear it again here. + */ this._clearModel(false); // Render synchronously to avoid flicker when the canvas is cleared @@ -368,9 +378,11 @@ export class WebglRenderer extends Disposable implements IRenderer { return; } - // Tell renderer the frame is beginning - // upon a model clear also refresh the full viewport model - // (also triggered by an atlas page merge, part of #4480) + /* + * Tell renderer the frame is beginning + * upon a model clear also refresh the full viewport model + * (also triggered by an atlas page merge, part of #4480) + */ if (this._glyphRenderer.value.beginFrame()) { this._clearModel(true); this._updateModel(0, this._terminal.rows - 1); @@ -379,8 +391,10 @@ export class WebglRenderer extends Disposable implements IRenderer { this._updateModel(start, end); } - // A mid-update atlas page merge invalidates vertex data and may not bump the host - // page's version, so re-run the update and force a full texture rebind. + /* + * A mid-update atlas page merge invalidates vertex data and may not bump the host + * page's version, so re-run the update and force a full texture rebind. + */ let merged = false; let mergeRetries = 0; while (this._charAtlas && this._glyphRenderer.value.beginFrame() && mergeRetries++ < Constants.MERGE_RETRY_LIMIT) { @@ -408,8 +422,10 @@ export class WebglRenderer extends Disposable implements IRenderer { } else { this._cursorBlinkStateManager.clear(); } - // Request a refresh from the terminal as management of rendering is being - // moved back to the terminal + /* + * Request a refresh from the terminal as management of rendering is being + * moved back to the terminal + */ this._requestRedrawCursor(); } @@ -470,20 +486,26 @@ export class WebglRenderer extends Disposable implements IRenderer { // If true, indicates that the current character(s) to draw were joined. isJoined = false; - // Indicates whether this cell is part of a joined range that should be ignored as it cannot - // be rendered entirely, like the selection state differs across the range. + /* + * Indicates whether this cell is part of a joined range that should be ignored as it cannot + * be rendered entirely, like the selection state differs across the range. + */ isValidJoinRange = (x >= skipJoinedCheckUntilX); lastCharX = x; - // Process any joined character ranges as needed. Because of how the - // ranges are produced, we know that they are valid for the characters - // and attributes of our input. + /* + * Process any joined character ranges as needed. Because of how the + * ranges are produced, we know that they are valid for the characters + * and attributes of our input. + */ if (joinedRanges.length > 0 && x === joinedRanges[0][0] && isValidJoinRange) { range = joinedRanges.shift()!; - // If the ligature's selection state is not consistent, don't join it. This helps the - // selection render correctly regardless whether they should be joined. + /* + * If the ligature's selection state is not consistent, don't join it. This helps the + * selection render correctly regardless whether they should be joined. + */ const firstSelectionState = this._model.selection.isCellSelected(this._terminal, range[0], row); for (i = range[0] + 1; i < range[1]; i++) { isValidJoinRange &&= (firstSelectionState === this._model.selection.isCellSelected(this._terminal, i, row)); @@ -495,8 +517,10 @@ export class WebglRenderer extends Disposable implements IRenderer { } else { isJoined = true; - // We already know the exact start and end column of the joined range, - // so we get the string and width representing it directly. + /* + * We already know the exact start and end column of the joined range, + * so we get the string and width representing it directly. + */ cell = new JoinedCellData( cell, line!.translateToString(true, range[0], range[1]), @@ -586,8 +610,10 @@ export class WebglRenderer extends Disposable implements IRenderer { 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 - // supported + /* + * Don't re-resolve the cell color since multi-colored ligature backgrounds are not + * supported + */ this._model.cells[j + RenderModelConstants.BG_OFFSET] = this._cellColorResolver.result.bg; this._model.cells[j + RenderModelConstants.FG_OFFSET] = this._cellColorResolver.result.fg; this._model.cells[j + RenderModelConstants.EXT_OFFSET] = this._cellColorResolver.result.ext; @@ -631,48 +657,64 @@ export class WebglRenderer extends Disposable implements IRenderer { return; } - // Calculate the device character width. Width is floored as it must be drawn to an integer grid - // in order for the char atlas glyphs to not be blurry. + /* + * Calculate the device character width. Width is floored as it must be drawn to an integer grid + * in order for the char atlas glyphs to not be blurry. + */ this.dimensions.device.char.width = Math.floor(this._charSizeService.width * this._devicePixelRatio); - // Calculate the device character height. Height is ceiled in case devicePixelRatio is a - // floating point number in order to ensure there is enough space to draw the character to the - // cell. + /* + * Calculate the device character height. Height is ceiled in case devicePixelRatio is a + * floating point number in order to ensure there is enough space to draw the character to the + * cell. + */ this.dimensions.device.char.height = Math.ceil(this._charSizeService.height * this._devicePixelRatio); - // Calculate the device cell height, if lineHeight is _not_ 1, the resulting value will be - // floored since lineHeight can never be lower then 1, this guarentees the device cell height - // will always be larger than device char height. + /* + * Calculate the device cell height, if lineHeight is _not_ 1, the resulting value will be + * floored since lineHeight can never be lower then 1, this guarentees the device cell height + * will always be larger than device char height. + */ this.dimensions.device.cell.height = Math.floor(this.dimensions.device.char.height * this._optionsService.rawOptions.lineHeight); - // Calculate the y offset within a cell that glyph should draw at in order for it to be centered - // correctly within the cell. + /* + * Calculate the y offset within a cell that glyph should draw at in order for it to be centered + * correctly within the cell. + */ this.dimensions.device.char.top = this._optionsService.rawOptions.lineHeight === 1 ? 0 : Math.round((this.dimensions.device.cell.height - this.dimensions.device.char.height) / 2); // Calculate the device cell width, taking the letterSpacing into account. this.dimensions.device.cell.width = this.dimensions.device.char.width + Math.round(this._optionsService.rawOptions.letterSpacing); - // Calculate the x offset with a cell that text should draw from in order for it to be centered - // correctly within the cell. + /* + * Calculate the x offset with a cell that text should draw from in order for it to be centered + * correctly within the cell. + */ this.dimensions.device.char.left = Math.floor(this._optionsService.rawOptions.letterSpacing / 2); - // Recalculate the canvas dimensions, the device dimensions define the actual number of pixel in - // the canvas + /* + * Recalculate the canvas dimensions, the device dimensions define the actual number of pixel in + * the canvas + */ this.dimensions.device.canvas.height = this._terminal.rows * this.dimensions.device.cell.height; this.dimensions.device.canvas.width = this._terminal.cols * this.dimensions.device.cell.width; - // The the size of the canvas on the page. It's important that this rounds to nearest integer - // and not ceils as browsers often have floating point precision issues where - // `window.devicePixelRatio` ends up being something like `1.100000023841858` for example, when - // it's actually 1.1. Ceiling may causes blurriness as the backing canvas image is 1 pixel too - // large for the canvas element size. + /* + * The the size of the canvas on the page. It's important that this rounds to nearest integer + * and not ceils as browsers often have floating point precision issues where + * `window.devicePixelRatio` ends up being something like `1.100000023841858` for example, when + * it's actually 1.1. Ceiling may causes blurriness as the backing canvas image is 1 pixel too + * large for the canvas element size. + */ this.dimensions.css.canvas.height = Math.round(this.dimensions.device.canvas.height / this._devicePixelRatio); this.dimensions.css.canvas.width = Math.round(this.dimensions.device.canvas.width / this._devicePixelRatio); - // Get the CSS dimensions of an individual cell. This needs to be derived from the calculated - // device pixel canvas value above. CharMeasure.width/height by itself is insufficient when the - // page is not at 100% zoom level as CharMeasure is measured in CSS pixels, but the actual char - // size on the canvas can differ. + /* + * Get the CSS dimensions of an individual cell. This needs to be derived from the calculated + * device pixel canvas value above. CharMeasure.width/height by itself is insufficient when the + * page is not at 100% zoom level as CharMeasure is measured in CSS pixels, but the actual char + * size on the canvas can differ. + */ this.dimensions.css.cell.height = this.dimensions.device.cell.height / this._devicePixelRatio; this.dimensions.css.cell.width = this.dimensions.device.cell.width / this._devicePixelRatio; } @@ -681,8 +723,10 @@ export class WebglRenderer extends Disposable implements IRenderer { if (this._canvas.width === width && this._canvas.height === height) { return; } - // While the actual canvas size has changed, keep device canvas dimensions as the value before - // the change as it's an exact multiple of the cell sizes. + /* + * While the actual canvas size has changed, keep device canvas dimensions as the value before + * the change as it's an exact multiple of the cell sizes. + */ this._canvas.width = width; this._canvas.height = height; // Render synchronously to avoid flicker when the canvas is cleared @@ -702,8 +746,10 @@ export class WebglRenderer extends Disposable implements IRenderer { // TODO: Share impl with core export class JoinedCellData extends AttributeData implements ICellData { private _width: number; - // .content carries no meaning for joined CellData, simply nullify it - // thus we have to overload all other .content accessors + /* + * .content carries no meaning for joined CellData, simply nullify it + * thus we have to overload all other .content accessors + */ public content: number = 0; public fg: number; public bg: number; @@ -731,8 +777,10 @@ export class JoinedCellData extends AttributeData implements ICellData { } public getCode(): number { - // code always gets the highest possible fake codepoint (read as -1) - // this is needed as code is used by caches as identifier + /* + * code always gets the highest possible fake codepoint (read as -1) + * this is needed as code is used by caches as identifier + */ return 0x1FFFFF; } diff --git a/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts b/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts index c35299809..d2c95723a 100644 --- a/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts +++ b/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts @@ -274,15 +274,17 @@ export const customGlyphDefinitions: { [index: string]: CustomGlyphCharacterDefi // #region Powerline Symbols (E0A0-E0BF) - // This contains the definitions of the primarily used box drawing characters as vector shapes. - // The reason these characters are defined specially is to avoid common problems if a user's font - // has not been patched with powerline characters and also to get pixel perfect rendering as - // rendering issues can occur around AA/SPAA. - // - // The line variants draw beyond the cell and get clipped to ensure the end of the line is not - // visible. - // - // Original symbols defined in https://github.com/powerline/fontpatcher + /* + * This contains the definitions of the primarily used box drawing characters as vector shapes. + * The reason these characters are defined specially is to avoid common problems if a user's font + * has not been patched with powerline characters and also to get pixel perfect rendering as + * rendering issues can occur around AA/SPAA. + * + * The line variants draw beyond the cell and get clipped to ensure the end of the line is not + * visible. + * + * Original symbols defined in https://github.com/powerline/fontpatcher + */ // Git branch '\u{E0A0}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M.3,1 L.03,1 L.03,.88 C.03,.82,.06,.78,.11,.73 C.15,.7,.2,.68,.28,.65 L.43,.6 C.49,.58,.53,.56,.56,.53 C.59,.5,.6,.47,.6,.43 L.6,.27 L.4,.27 L.69,.1 L.98,.27 L.78,.27 L.78,.46 C.78,.52,.76,.56,.72,.61 C.68,.66,.63,.67,.56,.7 L.48,.72 C.42,.74,.38,.76,.35,.78 C.32,.8,.31,.84,.31,.88 L.31,1 M.3,.5 L.03,.59 L.03,.09 L.3,.09 L.3,.655', type: CustomGlyphVectorType.FILL } }, @@ -355,8 +357,10 @@ export const customGlyphDefinitions: { [index: string]: CustomGlyphCharacterDefi // #region Progress Indicators (EE00-EE0B) - // initially added in Fira Code and later Nerd Fonts - // https://github.com/ryanoasis/nerd-fonts/pull/1733 + /* + * initially added in Fira Code and later Nerd Fonts + * https://github.com/ryanoasis/nerd-fonts/pull/1733 + */ // Progress bars (EE00-EE05) '\u{EE00}': { type: CustomGlyphDefinitionType.PATH, data: 'M0,0 L1,0 L1,.05 L.1,.05 L.1,.95 L1,.95 L1,1 L0,1 Z' }, // PROGRESS BAR EMPTY START @@ -378,8 +382,10 @@ export const customGlyphDefinitions: { [index: string]: CustomGlyphCharacterDefi // #region Git Branch Symbols (F5D0-F60D) - // Initially added in Kitty (https://github.com/kovidgoyal/kitty/pull/7681) - // then in Wezterm (https://github.com/wezterm/wezterm/issues/6328). + /* + * Initially added in Kitty (https://github.com/kovidgoyal/kitty/pull/7681) + * then in Wezterm (https://github.com/wezterm/wezterm/issues/6328). + */ // Straight lines (F5D0-F5D9) '\u{F5D0}': GitBranchSymbolsParts.LINE_H, // Same as 2500 @@ -433,8 +439,10 @@ export const customGlyphDefinitions: { [index: string]: CustomGlyphCharacterDefi '\u{F5FA}': [GitBranchSymbolsParts.NODE_FILL, GitBranchSymbolsParts.NODE_STROKE, GitBranchSymbolsParts.NODE_LINE_DOWN, GitBranchSymbolsParts.NODE_LINE_UP], '\u{F5FB}': [GitBranchSymbolsParts.NODE_STROKE, GitBranchSymbolsParts.NODE_LINE_DOWN, GitBranchSymbolsParts.NODE_LINE_UP], - // Extended Nodes (F5FC-F60D) - // These were added a little later https://github.com/kovidgoyal/kitty/pull/7805 + /* + * Extended Nodes (F5FC-F60D) + * These were added a little later https://github.com/kovidgoyal/kitty/pull/7805 + */ '\u{F5FC}': [GitBranchSymbolsParts.NODE_FILL, GitBranchSymbolsParts.NODE_STROKE, GitBranchSymbolsParts.NODE_LINE_DOWN, GitBranchSymbolsParts.NODE_LINE_RIGHT], '\u{F5FD}': [GitBranchSymbolsParts.NODE_STROKE, GitBranchSymbolsParts.NODE_LINE_DOWN, GitBranchSymbolsParts.NODE_LINE_RIGHT], '\u{F5FE}': [GitBranchSymbolsParts.NODE_FILL, GitBranchSymbolsParts.NODE_STROKE, GitBranchSymbolsParts.NODE_LINE_DOWN, GitBranchSymbolsParts.NODE_LINE_LEFT], @@ -460,8 +468,10 @@ export const customGlyphDefinitions: { [index: string]: CustomGlyphCharacterDefi // https://www.unicode.org/charts/PDF/U1FB00.pdf - // Block mosaic terminal graphic characters (1FB00-1FB3B) - // The term "sextant" refers to block mosaics divided into six parts. + /* + * Block mosaic terminal graphic characters (1FB00-1FB3B) + * The term "sextant" refers to block mosaics divided into six parts. + */ '\u{1FB00}': sextant(0b000001), // BLOCK SEXTANT-1 '\u{1FB01}': sextant(0b000010), // BLOCK SEXTANT-2 '\u{1FB02}': sextant(0b000011), // BLOCK SEXTANT-12 (upper one third block) @@ -724,8 +734,10 @@ export const customGlyphDefinitions: { [index: string]: CustomGlyphCharacterDefi '\u{1FBB2}': { type: CustomGlyphDefinitionType.PATH, data: 'M.29,.27 L0.13,.56 L.22,.59 L.35,0.35 L.67,.35 L.57,.57 L.71,.76 L.22,.76 L.42,1 L.53,.98 L.43,.86 L.9,.86 L.71,.6 L1,.6 L1,.52 L.83,.52 L.92,.36 L1,.36 L1,.27Z M.99,.13 A.12,.12,0,1,1,.75,.13 A.12,.12,0,1,1,.99,.13' }, // LEFT HALF RUNNING MAN '\u{1FBB3}': { type: CustomGlyphDefinitionType.PATH, data: 'M0,.27 L.3,.27 L.55,.12 L.63,.18 L.33,.36 L0,.36 M0,.52 L.33,.52 L.59,.89 L.73,.89 L.73,.98 L.53,.98 L.28,.6 L0,.6' }, // RIGHT HALF RUNNING MAN - // Arrows (1FBB4-1FBB8) - // TODO: Improve all arrows, use hybrid approach + /* + * Arrows (1FBB4-1FBB8) + * TODO: Improve all arrows, use hybrid approach + */ '\u{1FBB4}': { type: CustomGlyphDefinitionType.PATH_NEGATIVE, data: { d: 'M.15,.55 L.5,.4 L.5,.45 L.65,.45 L.65,.35 L.85,.35 L.85,.625 L.5,.625 L.5,.7 Z', type: CustomGlyphVectorType.FILL }, scaleType: CustomGlyphScaleType.CHAR }, // INVERSE DOWNWARDS ARROW WITH TIP LEFTWARDS '\u{1FBB5}': [ // LEFTWARDS ARROW AND UPPER AND LOWER ONE EIGHTH BLOCK { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M0,0 L1,0 L1,.0625 L0,.0625 Z M0,.9375 L1,.9375 L1,1 L0,1 Z', type: CustomGlyphVectorType.FILL } }, @@ -880,9 +892,11 @@ function sextant(pattern: number): { type: CustomGlyphDefinitionType.PATH_FUNCTI return { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: () => { - // Sextant grid: 2 columns, 3 rows - // Row heights in 8ths: top=3, middle=2, bottom=3 - // Column widths: left=4, right=4 + /* + * Sextant grid: 2 columns, 3 rows + * Row heights in 8ths: top=3, middle=2, bottom=3 + * Column widths: left=4, right=4 + */ const rects: string[] = []; const colW = 0.5; // Each column is half width const rowH = [3 / 8, 2 / 8, 3 / 8]; // Row heights as fractions @@ -932,10 +946,12 @@ function sextant(pattern: number): { type: CustomGlyphDefinitionType.PATH_FUNCTI function segmentedDigit(pattern: number): string { const paths: string[] = []; - // Each segment should have approximately the same stroke width, this is somewhat difficult to be - // precise since coordinates are 0-1 of the whole cell (percentage-based). To handle this, the - // fact that terminal cells are typically sized at ~2:1 (height:width) is leveraged. - // for horizontal vs vertical to make segments appear the same thickness + /* + * Each segment should have approximately the same stroke width, this is somewhat difficult to be + * precise since coordinates are 0-1 of the whole cell (percentage-based). To handle this, the + * fact that terminal cells are typically sized at ~2:1 (height:width) is leveraged. + * for horizontal vs vertical to make segments appear the same thickness + */ const segW = 0.15; // Width of vertical segments (fraction of cell width) const segH = 0.075; // Height of horizontal segments (fraction of cell height, ~half of segW for 2:1 cells) const padX = 0.05; // Horizontal padding from edge diff --git a/addons/addon-webgl/src/customGlyphs/CustomGlyphRasterizer.ts b/addons/addon-webgl/src/customGlyphs/CustomGlyphRasterizer.ts index ba8925727..ad36378cd 100644 --- a/addons/addon-webgl/src/customGlyphs/CustomGlyphRasterizer.ts +++ b/addons/addon-webgl/src/customGlyphs/CustomGlyphRasterizer.ts @@ -489,8 +489,10 @@ function drawPatternChar( pattern = throwIfFalsy(ctx.createPattern(tmpCanvas, null)); patternSet.set(fillStyle, pattern); } - // Apply pattern offset to ensure seamless tiling across cells when cell dimensions are odd. - // variantOffset encodes: bit 1 = x pixel shift, bit 0 = y pixel shift. + /* + * Apply pattern offset to ensure seamless tiling across cells when cell dimensions are odd. + * variantOffset encodes: bit 1 = x pixel shift, bit 0 = y pixel shift. + */ const dx = (variantOffset >> 1) & 1; const dy = variantOffset & 1; if (dx !== 0 || dy !== 0) { @@ -713,8 +715,10 @@ function translateArgs(args: string[], cellWidth: number, cellHeight: number, xO for (let x = 0; x < result.length; x += 2) { // Translate from 0-1 to 0-cellWidth result[x] *= cellWidth - (leftPadding * devicePixelRatio) - (rightPadding * devicePixelRatio); - // Round to the nearest 0.5 to ensure a crisp line at 100% devicePixelRatio, and optionally - // clamp to the cell bounds. + /* + * Round to the nearest 0.5 to ensure a crisp line at 100% devicePixelRatio, and optionally + * clamp to the cell bounds. + */ if (doClamp && result[x] !== 0) { const rounded = Math.round(result[x] + 0.5) - 0.5; result[x] = clampToCell ? clamp(rounded, cellWidth, 0) : rounded; @@ -726,8 +730,10 @@ function translateArgs(args: string[], cellWidth: number, cellHeight: number, xO for (let y = 1; y < result.length; y += 2) { // Translate from 0-1 to 0-cellHeight result[y] *= cellHeight; - // Round to the nearest 0.5 to ensure a crisp line at 100% devicePixelRatio, and optionally - // clamp to the cell bounds. + /* + * Round to the nearest 0.5 to ensure a crisp line at 100% devicePixelRatio, and optionally + * clamp to the cell bounds. + */ if (doClamp && result[y] !== 0) { const rounded = Math.round(result[y] + 0.5) - 0.5; result[y] = clampToCell ? clamp(rounded, cellHeight, 0) : rounded; diff --git a/addons/addon-webgl/test/WebglRenderer.test.ts b/addons/addon-webgl/test/WebglRenderer.test.ts index c425fa000..b39521180 100644 --- a/addons/addon-webgl/test/WebglRenderer.test.ts +++ b/addons/addon-webgl/test/WebglRenderer.test.ts @@ -24,9 +24,11 @@ test.beforeAll(async ({ browser }) => { test.afterAll(async () => await ctx.page.close()); test.describe('WebGL Renderer Integration Tests', async () => { - // HACK: webgl2 is often not supported in headless firefox on Linux - // https://github.com/microsoft/playwright/issues/11566 - // also disable safari due to #5852 + /* + * HACK: webgl2 is often not supported in headless firefox on Linux + * https://github.com/microsoft/playwright/issues/11566 + * also disable safari due to #5852 + */ if (platform() === 'linux') { test.skip(({ browserName }) => browserName === 'firefox' || browserName === 'webkit'); } diff --git a/demo/client/client.ts b/demo/client/client.ts index aab723b7e..b1718f64f 100644 --- a/demo/client/client.ts +++ b/demo/client/client.ts @@ -173,9 +173,11 @@ const createNewWindowButtonHandler: () => void = () => { terminalContainer.id = 'terminal-container'; win.document.body.appendChild(terminalContainer); - // Stylesheets are needed to get the terminal in the popout window to render - // correctly. We also need to wait for them to load before creating the - // terminal, otherwise we will not compute the correct metrics when rendering. + /* + * Stylesheets are needed to get the terminal in the popout window to render + * correctly. We also need to wait for them to load before creating the + * terminal, otherwise we will not compute the correct metrics when rendering. + */ let pendingStylesheets = 0; for (const linkNode of document.querySelectorAll('head link[rel=stylesheet]')) { const newLink = document.createElement('link'); @@ -359,8 +361,10 @@ function createTerminal(): Terminal { const resizeObserver = new ResizeObserver(entries => { if (optionsWindow.autoResize) { - // In general this should be debounced to avoid excessive work on the main - // thread by firing the expensive resize action repeatedly + /* + * In general this should be debounced to avoid excessive work on the main + * thread by firing the expensive resize action repeatedly + */ addons.fit.instance!.fit(); } }); @@ -547,8 +551,10 @@ function initAddons(term: Terminal): void { addon.instance = undefined; } if (name === 'ligatures') { - // Recreate webgl when ligatures are toggled so texture atlas picks up any font feature - // settings changes + /* + * Recreate webgl when ligatures are toggled so texture atlas picks up any font feature + * settings changes + */ if (addons.webgl.instance) { preDisposeWebgl(); addons.webgl.instance.dispose(); diff --git a/demo/client/components/window/addonImageWindow.ts b/demo/client/components/window/addonImageWindow.ts index 392b7177b..c089cf5f7 100644 --- a/demo/client/components/window/addonImageWindow.ts +++ b/demo/client/components/window/addonImageWindow.ts @@ -165,16 +165,18 @@ export class AddonImageWindow extends BaseWindow implements IControlWindow { this._terminal.element!.addEventListener('click', (ev: MouseEvent) => { if (!ev.ctrlKey || !imageAddon) return; - // TODO... - // if (ev.altKey) { - // const sel = term.getSelectionPosition(); - // if (sel) { - // addons.image.instance - // .extractCanvasAtBufferRange(term.getSelectionPosition()) - // ?.toBlob(data => window.open(URL.createObjectURL(data), '_blank')); - // return; - // } - // } + /* + * TODO... + * if (ev.altKey) { + * const sel = term.getSelectionPosition(); + * if (sel) { + * addons.image.instance + * .extractCanvasAtBufferRange(term.getSelectionPosition()) + * ?.toBlob(data => window.open(URL.createObjectURL(data), '_blank')); + * return; + * } + * } + */ const pos = (this._terminal as any)._core._mouseService!.getCoords(ev, (this._terminal as any)._core.screenElement!, this._terminal.cols, this._terminal.rows); const x = pos[0] - 1; diff --git a/demo/client/components/window/addonProgressWindow.ts b/demo/client/components/window/addonProgressWindow.ts index 167c9ca28..fe4e51ca7 100644 --- a/demo/client/components/window/addonProgressWindow.ts +++ b/demo/client/components/window/addonProgressWindow.ts @@ -66,11 +66,13 @@ export class AddonProgressWindow extends BaseWindow implements IControlWindow { const colors = { 0: '', 1: 'green', 2: 'red', 3: '', 4: 'yellow' }; function progressHandler({ state, value }: IProgressState): void { - // Simulate windows taskbar hack by windows terminal: - // Since the taskbar has no means to indicate error/pause state other than by coloring - // the current progress, we move 0 to 10% and distribute higher values in the remaining 90 % - // NOTE: This is most likely not what you want to do for other progress indicators, - // that have a proper visual state for error/paused. + /* + * Simulate windows taskbar hack by windows terminal: + * Since the taskbar has no means to indicate error/pause state other than by coloring + * the current progress, we move 0 to 10% and distribute higher values in the remaining 90 % + * NOTE: This is most likely not what you want to do for other progress indicators, + * that have a proper visual state for error/paused. + */ value = Math.min(10 + value * 0.9, 100); document.getElementById('progress-percent')!.style.width = `${value}%`; document.getElementById('progress-percent')!.style.backgroundColor = colors[state]; diff --git a/demo/client/components/window/testWindow.ts b/demo/client/components/window/testWindow.ts index 1ea6b3e67..e66795ab1 100644 --- a/demo/client/components/window/testWindow.ts +++ b/demo/client/components/window/testWindow.ts @@ -435,9 +435,11 @@ function customGlyphAlignmentHandler(term: Terminal): void { } function customGlyphRangesHandler(term: Terminal): void { - // Box Drawing - // 2500-257F - // https://www.unicode.org/charts/PDF/U2500.pdf + /* + * Box Drawing + * 2500-257F + * https://www.unicode.org/charts/PDF/U2500.pdf + */ writeUnicodeTable(term, 'Box Drawing', 0x2500, 0x257F, [ ['Light and heavy solid lines', 0x2500, 0x2503], ['Light and heavy dashed lines', 0x2504, 0x250B], @@ -450,39 +452,49 @@ function customGlyphRangesHandler(term: Terminal): void { ['Light and heavy half lines', 0x2574, 0x257B], ['Mixed light and heavy lines', 0x257C, 0x257F], ]); - // Box Elements - // 2580-259F - // https://www.unicode.org/charts/PDF/U2580.pdf + /* + * Box Elements + * 2580-259F + * https://www.unicode.org/charts/PDF/U2580.pdf + */ writeUnicodeTable(term, 'Box Elements', 0x2580, 0x259F, [ ['Block elements', 0x2580, 0x2590], ['Shade characters', 0x2591, 0x2593], ['Block elements', 0x2594, 0x2595], ['Terminal graphic characters', 0x2596, 0x259F], ]); - // Braille Patterns - // 2800-28FF - // https://www.unicode.org/charts/PDF/U2800.pdf + /* + * Braille Patterns + * 2800-28FF + * https://www.unicode.org/charts/PDF/U2800.pdf + */ writeUnicodeTable(term, 'Braille patterns', 0x2800, 0x28FF, [ ['Braille patterns', 0x2800, 0x28FF], ]); - // Powerline Symbols - // Range: E0A0–E0D4 - // https://github.com/ryanoasis/nerd-fonts + /* + * Powerline Symbols + * Range: E0A0–E0D4 + * https://github.com/ryanoasis/nerd-fonts + */ writeUnicodeTable(term, 'Powerline Symbols', 0xE0A0, 0xE0D4, [ ['Powerline symbols', 0xE0A0, 0xE0B3, [0xE0A4, 0xE0A5, 0xE0A6, 0xE0A7, 0xE0A8, 0xE0A9, 0xE0AA, 0xE0AB, 0xE0AC, 0xE0AD, 0xE0AE, 0xE0AF]], ['Powerline extra symbols', 0xE0B4, 0xE0D4, [0xE0C9, 0xE0CB, 0xE0D3]], ]); - // Progress Indicators - // Range: EE00-EE0B - // https://github.com/tonsky/FiraCode + /* + * Progress Indicators + * Range: EE00-EE0B + * https://github.com/tonsky/FiraCode + */ writeUnicodeTable(term, 'Progress Indicators', 0xEE00, 0xEE0B, [ ['Progress bars', 0xEE00, 0xEE05], ['Progress spinners', 0xEE06, 0xEE0B], ]); - // https://github.com/ryanoasis/nerd-fonts/pull/1733 - // Git Branch Symbols - // F5D0-F60D - // https://github.com/xtermjs/xterm.js/issues/5477 + /* + * https://github.com/ryanoasis/nerd-fonts/pull/1733 + * Git Branch Symbols + * F5D0-F60D + * https://github.com/xtermjs/xterm.js/issues/5477 + */ writeUnicodeTable(term, 'Git Branch Symbols', 0xF5D0, 0xF5FB, [ ['Straight lines', 0xF5D0, 0xF5D5], ['Curved lines', 0xF5D6, 0xF5D9], @@ -490,9 +502,11 @@ function customGlyphRangesHandler(term: Terminal): void { ['Nodes', 0xF5EE, 0xF5FB], ['Extended nodes', 0xF5FC, 0xF60D], ]); - // Symbols for Legacy Computing - // Range: 1FB00–1FBFF - // https://www.unicode.org/charts/PDF/U1FB00.pdf + /* + * Symbols for Legacy Computing + * Range: 1FB00–1FBFF + * https://www.unicode.org/charts/PDF/U1FB00.pdf + */ writeUnicodeTable(term, 'Symbols for Legacy Computing', 0x1FB00, 0x1FBFF, [ ['Block mosaic terminal graphic characters (Sextants)', 0x1FB00, 0x1FB3B], ['Smooth mosaic terminal graphic characters', 0x1FB3C, 0x1FB6F], @@ -690,8 +704,10 @@ function loadTest(term: Terminal, addons: AddonCollection): void { for (let i = 1; i < count + 1; i++) { data[i] = 0x61 + Math.floor(Math.random() * (0x7A - 0x61)); } - // End each line with \r so the cursor remains constant, this is what ls/tree do and improves - // performance significantly due to the cursor DOM element not needing to change + /* + * End each line with \r so the cursor remains constant, this is what ls/tree do and improves + * performance significantly due to the cursor DOM element not needing to change + */ data[data.length - 1] = 0x0D; // \r testData.push(data); } @@ -723,8 +739,10 @@ async function loadTestLongLines(term: Terminal, addons: AddonCollection): Promi for (let i = 1; i < count + 1; i++) { data[i] = 0x61 + Math.floor(Math.random() * (0x7A - 0x61)); } - // End each line with \r so the cursor remains constant, this is what ls/tree do and improves - // performance significantly due to the cursor DOM element not needing to change + /* + * End each line with \r so the cursor remains constant, this is what ls/tree do and improves + * performance significantly due to the cursor DOM element not needing to change + */ data[data.length - 1] = 0x0D; // \r testData.push(data); } @@ -734,8 +752,10 @@ async function loadTestLongLines(term: Terminal, addons: AddonCollection): Promi try { term.write(d); } catch { - // Flush events when cap is hit and try again(workaround for not having flow control in - // demo) + /* + * Flush events when cap is hit and try again(workaround for not having flow control in + * demo) + */ await new Promise(r => term.write('', () => r())); term.write(d); } diff --git a/demo/client/unicodeTable.ts b/demo/client/unicodeTable.ts index 61bba36f2..9755b911a 100644 --- a/demo/client/unicodeTable.ts +++ b/demo/client/unicodeTable.ts @@ -220,8 +220,10 @@ export function writeUnicodeTable(term: Terminal, name: string, start: number, e term.write('\n\r'); - // Render reserved labels that appear after the first label (below the row) - // Show one label per non-contiguous reserved range + /* + * Render reserved labels that appear after the first label (below the row) + * Show one label per non-contiguous reserved range + */ const lateReserved = rowLabels.length > 0 ? rowReserved.filter(r => r.col >= rowLabels[0].col) : rowReserved; diff --git a/demo/server/server.ts b/demo/server/server.ts index ef98eb84c..5bd594dcb 100644 --- a/demo/server/server.ts +++ b/demo/server/server.ts @@ -180,9 +180,11 @@ function startServer(): void { } const send = (USE_BINARY ? bufferUtf8 : buffer)(ws, 3, 262144); - // WARNING: This is a naive implementation that will not throttle the flow of data. This means - // it could flood the communication channel and make the terminal unresponsive. Learn more about - // the problem and how to implement flow control at https://xtermjs.org/docs/guides/flowcontrol/ + /* + * WARNING: This is a naive implementation that will not throttle the flow of data. This means + * it could flood the communication channel and make the terminal unresponsive. Learn more about + * the problem and how to implement flow control at https://xtermjs.org/docs/guides/flowcontrol/ + */ term.onData(function(data) { try { send(data as string & Buffer); @@ -209,8 +211,10 @@ function startServer(): void { app.listen(port, host, 0); } -// HACK: There is an EPIPE error thrown when reloading a page. This only seems to happen on Windows -// and it's unclear why it happens. Suppressing the error here since this is just the demo server. +/* + * HACK: There is an EPIPE error thrown when reloading a page. This only seems to happen on Windows + * and it's unclear why it happens. Suppressing the error here since this is just the demo server. + */ process.on('uncaughtException', (error) => { if (process.platform === 'win32' && error.message === 'read EPIPE') { return; diff --git a/eslint.config.mjs b/eslint.config.mjs index 6c2f80e30..18a4dfb59 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -88,6 +88,7 @@ export default tseslint.config( 'jsdoc/check-param-names': 'warn', 'jsdoc/no-multi-asterisks': 'warn', 'keyword-spacing': 'warn', + 'multiline-comment-style': ['warn', 'starred-block'], 'max-len': ['warn', { code: 1000, comments: 100, diff --git a/src/browser/AccessibilityManager.ts b/src/browser/AccessibilityManager.ts index 6fc421337..5960c8d4f 100644 --- a/src/browser/AccessibilityManager.ts +++ b/src/browser/AccessibilityManager.ts @@ -21,8 +21,10 @@ const enum BoundaryPosition { BOTTOM } -// Turn this on to unhide the accessibility tree and display it under -// (instead of overlapping with) the terminal. +/* + * Turn this on to unhide the accessibility tree and display it under + * (instead of overlapping with) the terminal. + */ const DEBUG = false; export class AccessibilityManager extends Disposable { @@ -223,8 +225,10 @@ export class AccessibilityManager extends Disposable { return; } - // Don't scroll when the last focused item was not the second row (focus is going the other - // direction) + /* + * Don't scroll when the last focused item was not the second row (focus is going the other + * direction) + */ if (e.relatedTarget !== beforeBoundaryElement) { return; } @@ -283,9 +287,11 @@ export class AccessibilityManager extends Disposable { } if (selection.isCollapsed) { - // Only do something when the anchorNode is inside the row container. This - // behavior mirrors what we do with mouse --- if the mouse clicks - // somewhere outside of the terminal, we don't clear the selection. + /* + * Only do something when the anchorNode is inside the row container. This + * behavior mirrors what we do with mouse --- if the mouse clicks + * somewhere outside of the terminal, we don't clear the selection. + */ if (this._rowContainer.contains(selection.anchorNode)) { this._terminal.clearSelection(); } diff --git a/src/browser/Clipboard.ts b/src/browser/Clipboard.ts index aaf8a48e4..513dfa60c 100644 --- a/src/browser/Clipboard.ts +++ b/src/browser/Clipboard.ts @@ -22,8 +22,10 @@ export function bracketTextForPaste(text: string, bracketedPasteMode: boolean): if (!bracketedPasteMode) { return text; } - // Sanitize pasted text to prevent injected escape sequences (e.g. exiting bracketed paste) - // by replacing ESC (\x1b) with its visible representation U+241B (␛). + /* + * Sanitize pasted text to prevent injected escape sequences (e.g. exiting bracketed paste) + * by replacing ESC (\x1b) with its visible representation U+241B (␛). + */ const sanitizedText = text.replace(/\x1b/g, '\u241b'); return `\x1b[200~${sanitizedText}\x1b[201~`; } diff --git a/src/browser/CoreBrowserTerminal.ts b/src/browser/CoreBrowserTerminal.ts index 52eb00025..519d8b1dc 100644 --- a/src/browser/CoreBrowserTerminal.ts +++ b/src/browser/CoreBrowserTerminal.ts @@ -323,8 +323,10 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal { * Binds the desired blur behavior on a given terminal object. */ private _handleTextAreaBlur(): void { - // Text can safely be removed on blur. Doing it earlier could interfere with - // screen readers reading it out. + /* + * Text can safely be removed on blur. Doing it earlier could interfere with + * screen readers reading it out. + */ this.textarea!.value = ''; this.refresh(this.buffer.y, this.buffer.y); if (this.coreService.decPrivateModes.sendFocus) { @@ -350,8 +352,10 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal { const cursorTop = this.buffer.y * this._renderService.dimensions.css.cell.height; const cursorLeft = cursorX * this._renderService.dimensions.css.cell.width; - // Sync the textarea to the exact position of the composition view so the IME knows where the - // text is. + /* + * Sync the textarea to the exact position of the composition view so the IME knows where the + * text is. + */ this.textarea.style.left = cursorLeft + 'px'; this.textarea.style.top = cursorTop + 'px'; this.textarea.style.width = cellWidth + 'px'; @@ -368,8 +372,10 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal { // Bind clipboard functionality this._register(addDisposableListener(this.element!, 'copy', (event: ClipboardEvent) => { - // If mouse events are active it means the selection manager is disabled and - // copy should be handled by the host program. + /* + * If mouse events are active it means the selection manager is disabled and + * copy should be handled by the host program. + */ if (!this.hasSelection()) { return; } @@ -393,12 +399,16 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal { })); } - // Move the textarea under the cursor when middle clicking on Linux to ensure - // middle click to paste selection works. This only appears to work in Chrome - // at the time is writing. + /* + * Move the textarea under the cursor when middle clicking on Linux to ensure + * middle click to paste selection works. This only appears to work in Chrome + * at the time is writing. + */ if (Browser.isLinux) { - // Use auxclick event over mousedown the latter doesn't seem to work. Note - // that the regular click event doesn't fire for the middle mouse button. + /* + * Use auxclick event over mousedown the latter doesn't seem to work. Note + * that the regular click event doesn't fire for the middle mouse button. + */ this._register(addDisposableListener(this.element!, 'auxclick', (event: MouseEvent) => { if (event.button === 1) { moveTextAreaUnderMouseCursor(event, this.textarea!, this.screenElement!); @@ -415,11 +425,13 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal { this._register(addDisposableListener(this.textarea!, 'keydown', (ev: KeyboardEvent) => this._keyDown(ev), true)); this._register(addDisposableListener(this.textarea!, 'keypress', (ev: KeyboardEvent) => this._keyPress(ev), true)); this._register(addDisposableListener(this.textarea!, 'compositionstart', () => { - // Ensure the textarea is synced to the latest cursor location before composition begins. This - // is to workaround a problem where highly dynamic TUIs like agentic CLIs reprint agressively - // would cause the IME to appear in the wrong position. The theory is that when the IME is - // triggered during a partial render the textarea position becomes locked and will not move - // until it is hidden and a custom move occurs. + /* + * Ensure the textarea is synced to the latest cursor location before composition begins. This + * is to workaround a problem where highly dynamic TUIs like agentic CLIs reprint agressively + * would cause the IME to appear in the wrong position. The theory is that when the IME is + * triggered during a partial render the textarea position becomes locked and will not move + * until it is hidden and a custom move occurs. + */ this._syncTextArea(); this._compositionHelper!.compositionstart(); this._compositionHelper!.updateCompositionElements(); @@ -467,8 +479,10 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal { this._register(this.optionsService.onSpecificOptionChange('allowTransparency', value => this.element!.classList.toggle('allow-transparency', value))); parent.appendChild(this.element); - // Performance: Use a document fragment to build the terminal - // viewport and helper elements detached from the DOM + /* + * Performance: Use a document fragment to build the terminal + * viewport and helper elements detached from the DOM + */ const fragment = this._document.createDocumentFragment(); this._viewportElement = this._document.createElement('div'); this._viewportElement.classList.add('xterm-viewport'); @@ -477,8 +491,10 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal { this.screenElement = this._document.createElement('div'); this.screenElement.classList.add('xterm-screen'); this._register(addDisposableListener(this.screenElement, 'mousemove', (ev: MouseEvent) => this.updateCursorStyle(ev))); - // Create the container that will hold helpers like the textarea for - // capturing DOM Events. Then produce the helpers. + /* + * Create the container that will hold helpers like the textarea for + * capturing DOM Events. Then produce the helpers. + */ this._helperContainer = this._document.createElement('div'); this._helperContainer.classList.add('xterm-helpers'); this.screenElement.appendChild(this._helperContainer); @@ -488,8 +504,10 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal { this.textarea.classList.add('xterm-helper-textarea'); this.textarea.setAttribute('aria-label', Strings.promptLabel.get()); if (!Browser.isChromeOS) { - // ChromeVox on ChromeOS does not like this. See - // https://issuetracker.google.com/issues/260170397 + /* + * ChromeVox on ChromeOS does not like this. See + * https://issuetracker.google.com/issues/260170397 + */ this.textarea.setAttribute('aria-multiline', 'false'); } this.textarea.setAttribute('autocorrect', 'off'); @@ -499,8 +517,10 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal { this._register(this.optionsService.onSpecificOptionChange('disableStdin', () => textarea.readOnly = this.optionsService.rawOptions.disableStdin)); this.textarea.readOnly = this.optionsService.rawOptions.disableStdin; - // Register the core browser service before the generic textarea handlers are registered so it - // handles them first. Otherwise the renderers may use the wrong focus state. + /* + * Register the core browser service before the generic textarea handlers are registered so it + * handles them first. Otherwise the renderers may use the wrong focus state. + */ this._coreBrowserService = this._register(this._instantiationService.createInstance(CoreBrowserService, this.textarea, parent.ownerDocument.defaultView ?? window, @@ -598,9 +618,11 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal { this._register(this._selectionService.onSelectionChange(() => this._onSelectionChange.fire())); this._register(this._selectionService.onRequestRedraw(e => this._renderService!.handleSelectionChanged(e.start, e.end, e.columnSelectMode))); this._register(this._selectionService.onLinuxMouseSelection(text => { - // If there's a new selection, put it into the textarea, focus and select it - // in order to register it as a selection on the OS. This event is fired - // only on Linux to enable middle click to paste selection. + /* + * If there's a new selection, put it into the textarea, focus and select it + * in order to register it as a selection on the OS. This event is fired + * only on Linux to enable middle click to paste selection. + */ this.textarea!.value = text; this.textarea!.focus(); this.textarea!.select(); @@ -625,8 +647,10 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal { } if (this.options.screenReaderMode) { - // Note that this must be done *after* the renderer is created in order to - // ensure the correct order of the dprchange event + /* + * Note that this must be done *after* the renderer is created in order to + * ensure the correct order of the dprchange event + */ this._accessibilityManager.value = this._instantiationService.createInstance(AccessibilityManager, this); } this._register(this.optionsService.onSpecificOptionChange('screenReaderMode', e => this._handleScreenReaderModeOptionChange(e))); @@ -651,8 +675,10 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal { // Initialize global actions that need to be taken on the document. this._initGlobal(); - // Listen for mouse events and translate - // them into terminal mouse protocols. + /* + * Listen for mouse events and translate + * them into terminal mouse protocols. + */ this._mouseService.bindMouse({ element: this.element!, screenElement: this.screenElement!, @@ -892,9 +918,11 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal { return true; } - // HACK: Process A-Z in the keypress event to fix an issue with macOS IMEs where lower case - // letters cannot be input while caps lock is on. Skip this hack when using kitty protocol - // or Win32 input mode as they need to send proper sequences for all key events. + /* + * HACK: Process A-Z in the keypress event to fix an issue with macOS IMEs where lower case + * letters cannot be input while caps lock is on. Skip this hack when using kitty protocol + * or Win32 input mode as they need to send proper sequences for all key events. + */ if (!this._keyboardService.useKitty && !this._keyboardService.useWin32InputMode && event.key && !event.ctrlKey && !event.altKey && !event.metaKey && event.key.length === 1) { if (event.key.charCodeAt(0) >= 65 && event.key.charCodeAt(0) <= 90) { return true; @@ -906,9 +934,11 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal { return true; } - // If ctrl+c or enter is being sent, clear out the textarea. This is done so that screen readers - // will announce deleted characters. This will not work 100% of the time but it should cover - // most scenarios. + /* + * If ctrl+c or enter is being sent, clear out the textarea. This is done so that screen readers + * will announce deleted characters. This will not work 100% of the time but it should cover + * most scenarios. + */ if (result.key === C0.ETX || result.key === C0.CR) { this.textarea!.value = ''; } @@ -918,10 +948,12 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal { this._showCursor(); this.coreService.triggerDataEvent(result.key, !wasModifierOnly); - // Cancel events when not in screen reader mode so events don't get bubbled up and handled by - // other listeners. When screen reader mode is enabled, we don't cancel them (unless ctrl or alt - // is also depressed) so that the cursor textarea can be updated, which triggers the screen - // reader to read it. + /* + * Cancel events when not in screen reader mode so events don't get bubbled up and handled by + * other listeners. When screen reader mode is enabled, we don't cancel them (unless ctrl or alt + * is also depressed) so that the cursor textarea can be updated, which triggers the screen + * reader to read it. + */ if (!this.optionsService.rawOptions.screenReaderMode || event.altKey || event.ctrlKey) { event.preventDefault(); event.stopPropagation(); @@ -1010,8 +1042,10 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal { this._keyPressHandled = true; - // The key was handled so clear the dead key state, otherwise certain keystrokes like arrow - // keys could be ignored + /* + * The key was handled so clear the dead key state, otherwise certain keystrokes like arrow + * keys could be ignored + */ this._unprocessedDeadKey = false; return true; @@ -1024,16 +1058,20 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal { * @param ev The input event to be handled. */ protected _inputEvent(ev: InputEvent): boolean { - // Only support emoji IMEs when screen reader mode is disabled as the event must bubble up to - // support reading out character input which can doubling up input characters - // Based on these event traces: https://github.com/xtermjs/xterm.js/issues/3679 + /* + * Only support emoji IMEs when screen reader mode is disabled as the event must bubble up to + * support reading out character input which can doubling up input characters + * Based on these event traces: https://github.com/xtermjs/xterm.js/issues/3679 + */ if (ev.data && ev.inputType === 'insertText' && (!ev.composed || !this._keyDownSeen) && !this.optionsService.rawOptions.screenReaderMode) { if (this._keyPressHandled) { return false; } - // The key was handled so clear the dead key state, otherwise certain keystrokes like arrow - // keys could be ignored + /* + * The key was handled so clear the dead key state, otherwise certain keystrokes like arrow + * keys could be ignored + */ this._unprocessedDeadKey = false; const text = ev.data; @@ -1083,8 +1121,10 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal { for (let i = 1; i < this.rows; i++) { this.buffer.lines.push(this.buffer.getBlankLine(DEFAULT_ATTR_DATA)); } - // IMPORTANT: Fire scroll event before viewport is reset. This ensures embedders get the clear - // scroll event and that the viewport's state will be valid for immediate writes. + /* + * IMPORTANT: Fire scroll event before viewport is reset. This ensures embedders get the clear + * scroll event and that the viewport's state will be valid for immediate writes. + */ this._onScroll.fire({ position: this.buffer.ydisp }); this.refresh(0, this.rows - 1); } diff --git a/src/browser/Linkifier.ts b/src/browser/Linkifier.ts index 31c91daee..e99c41dfa 100644 --- a/src/browser/Linkifier.ts +++ b/src/browser/Linkifier.ts @@ -87,9 +87,11 @@ export class Linkifier extends Disposable implements ILinkifier2 { } private _handleHover(position: IBufferCellPosition): void { - // TODO: This currently does not cache link provider results across wrapped lines, activeLine - // should be something like `activeRange: {startY, endY}` - // Check if we need to clear the link + /* + * TODO: This currently does not cache link provider results across wrapped lines, activeLine + * should be something like `activeRange: {startY, endY}` + * Check if we need to clear the link + */ if (this._activeLine !== position.y || this._wasResized) { this._clearCurrentLink(); this._askForLink(position, false); @@ -125,9 +127,11 @@ export class Linkifier extends Disposable implements ILinkifier2 { const existingReply = this._activeProviderReplies?.get(i); // If there isn't a reply, the provider hasn't responded yet. - // TODO: If there isn't a reply yet it means that the provider is still resolving. Ensuring - // provideLinks isn't triggered again saves ILink.hover firing twice though. This probably - // needs promises to get fixed + /* + * TODO: If there isn't a reply yet it means that the provider is still resolving. Ensuring + * provideLinks isn't triggered again saves ILink.hover firing twice though. This probably + * needs promises to get fixed + */ if (existingReply) { linkProvided = this._checkLinkProviderResult(i, position, linkProvided); } @@ -140,8 +144,10 @@ export class Linkifier extends Disposable implements ILinkifier2 { this._activeProviderReplies?.set(i, linksWithState); linkProvided = this._checkLinkProviderResult(i, position, linkProvided); - // If all providers have responded, remove lower priority links that intersect ranges of - // higher priority links + /* + * If all providers have responded, remove lower priority links that intersect ranges of + * higher priority links + */ if (this._activeProviderReplies?.size === this._linkProviderService.linkProviders.length) { this._removeIntersectingLinks(position.y, this._activeProviderReplies); } @@ -187,8 +193,10 @@ export class Linkifier extends Disposable implements ILinkifier2 { } } - // If all providers with higher priority came back undefined, then this provider's link for - // the position should be used + /* + * If all providers with higher priority came back undefined, then this provider's link for + * the position should be used + */ if (!hasLinkBefore && links) { const linkAtPosition = links.find(link => this._linkAtPosition(link.link, position)); if (linkAtPosition) { @@ -296,15 +304,19 @@ export class Linkifier extends Disposable implements ILinkifier2 { } }); - // Listen to viewport changes to re-render the link under the cursor (only when the line the - // link is on changes) + /* + * Listen to viewport changes to re-render the link under the cursor (only when the line the + * link is on changes) + */ this._linkCacheDisposables.push(this._renderService.onRenderedViewportChange(e => { // Sanity check, this shouldn't happen in practice as this listener would be disposed if (!this._currentLink) { return; } - // When start is 0 a scroll most likely occurred, make sure links above the fold also get - // cleared. + /* + * When start is 0 a scroll most likely occurred, make sure links above the fold also get + * cleared. + */ const start = e.start === 0 ? 0 : e.start + 1 + this._bufferService.buffer.ydisp; const end = this._bufferService.buffer.ydisp + 1 + e.end; // Only clear the link if the viewport change happened on this line diff --git a/src/browser/OscLinkProvider.ts b/src/browser/OscLinkProvider.ts index b6a3cce0a..8ed823203 100644 --- a/src/browser/OscLinkProvider.ts +++ b/src/browser/OscLinkProvider.ts @@ -33,8 +33,10 @@ export class OscLinkProvider implements ILinkProvider { let currentStart = -1; let finishLink = false; for (let x = 0; x < lineLength; x++) { - // Minor optimization, only check for content if there isn't a link in case the link ends with - // a null cell + /* + * Minor optimization, only check for content if there isn't a link in case the link ends with + * a null cell + */ if (currentStart === -1 && !line.hasContent(x)) { continue; } @@ -107,8 +109,10 @@ export class OscLinkProvider implements ILinkProvider { } } - // TODO: Handle fetching and returning other link ranges to underline other links with the same - // id + /* + * TODO: Handle fetching and returning other link ranges to underline other links with the same + * id + */ callback(result); } } diff --git a/src/browser/Terminal.test.ts b/src/browser/Terminal.test.ts index d359fa28c..425955982 100644 --- a/src/browser/Terminal.test.ts +++ b/src/browser/Terminal.test.ts @@ -576,14 +576,16 @@ describe('Terminal', () => { beforeEach(() => { term.clearSelection = () => { }; - // term.compositionHelper = { - // isComposing: false, - // keydown: { - // bind: () => { - // return () => { return true; }; - // } - // } - // }; + /* + * term.compositionHelper = { + * isComposing: false, + * keydown: { + * bind: () => { + * return () => { return true; }; + * } + * } + * }; + */ evKeyDown = { preventDefault: () => { }, stopPropagation: () => { }, @@ -1148,8 +1150,10 @@ describe('Terminal', () => { // FIXME: move to common/CoreTerminal.test once the trimming is moved over describe('marker lifecycle', () => { - // create a 10x5 terminal with markers on every line - // to test marker lifecycle under various terminal actions + /* + * create a 10x5 terminal with markers on every line + * to test marker lifecycle under various terminal actions + */ let markers: IMarker[]; let disposeStack: IMarker[]; let term: TestTerminal; diff --git a/src/browser/TimeBasedDebouncer.ts b/src/browser/TimeBasedDebouncer.ts index 997312901..8d609cb23 100644 --- a/src/browser/TimeBasedDebouncer.ts +++ b/src/browser/TimeBasedDebouncer.ts @@ -43,8 +43,10 @@ export class TimeBasedDebouncer implements IRenderDebouncer { this._rowStart = this._rowStart !== undefined ? Math.min(this._rowStart, rowStart) : rowStart; this._rowEnd = this._rowEnd !== undefined ? Math.max(this._rowEnd, rowEnd) : rowEnd; - // Only refresh if the time since last refresh is above a threshold, otherwise wait for - // enough time to pass before refreshing again. + /* + * Only refresh if the time since last refresh is above a threshold, otherwise wait for + * enough time to pass before refreshing again. + */ const refreshRequestTime: number = performance.now(); if (refreshRequestTime - this._lastRefreshMs >= this._debounceThresholdMS) { // Enough time has lapsed since the last refresh; refresh immediately diff --git a/src/browser/Types.ts b/src/browser/Types.ts index fa08de2e3..9c76fb07d 100644 --- a/src/browser/Types.ts +++ b/src/browser/Types.ts @@ -201,8 +201,10 @@ export const DEFAULT_ANSI_COLORS = Object.freeze((() => { css.toColor('#eeeeec') ]; - // Fill in the remaining 240 ANSI colors. - // Generate colors (16-231) + /* + * Fill in the remaining 240 ANSI colors. + * Generate colors (16-231) + */ const v = [0x00, 0x5f, 0x87, 0xaf, 0xd7, 0xff]; for (let i = 0; i < 216; i++) { const r = v[(i / 36) % 6 | 0]; diff --git a/src/browser/Viewport.ts b/src/browser/Viewport.ts index 3b6ae8301..c629bc7a3 100644 --- a/src/browser/Viewport.ts +++ b/src/browser/Viewport.ts @@ -99,16 +99,20 @@ export class Viewport extends Disposable { this._register(this._bufferService.onResize(() => this.queueSync())); this._register(this._bufferService.buffers.onBufferActivate(() => { - // Reset _latestYDisp when switching buffers to prevent stale scroll position - // from alt buffer contaminating normal buffer scroll position + /* + * Reset _latestYDisp when switching buffers to prevent stale scroll position + * from alt buffer contaminating normal buffer scroll position + */ this._latestYDisp = undefined; this.queueSync(); })); this._register(this._bufferService.onScroll(() => this._sync())); - // Flush deferred viewport sync after a render completes (e.g. after ESU ends - // synchronized output mode). This ensures DOM scroll position updates atomically - // with the canvas render. + /* + * Flush deferred viewport sync after a render completes (e.g. after ESU ends + * synchronized output mode). This ensures DOM scroll position updates atomically + * with the canvas render. + */ this._register(this._renderService.onRender(() => { if (this._needsSyncOnRender) { this._needsSyncOnRender = false; @@ -173,16 +177,20 @@ export class Viewport extends Disposable { if (!this._renderService || this._isSyncing) { return; } - // Defer DOM scroll updates during synchronized output to prevent visible - // scroll position flickering while the canvas content is frozen. + /* + * Defer DOM scroll updates during synchronized output to prevent visible + * scroll position flickering while the canvas content is frozen. + */ if (this._coreService.decPrivateModes.synchronizedOutput) { this._needsSyncOnRender = true; return; } this._isSyncing = true; - // Ignore any onScroll event that happens as a result of dimensions changing as this should - // never cause a scrollLines call, only setScrollPosition can do that. + /* + * Ignore any onScroll event that happens as a result of dimensions changing as this should + * never cause a scrollLines call, only setScrollPosition can do that. + */ this._suppressOnScrollHandler = true; this._scrollableElement.setScrollDimensions({ height: this._renderService.dimensions.css.canvas.height, @@ -190,8 +198,10 @@ export class Viewport extends Disposable { }); this._suppressOnScrollHandler = false; - // If ydisp has been changed by some other component (input/buffer), then stop animating smooth - // scroll and scroll there immediately. + /* + * If ydisp has been changed by some other component (input/buffer), then stop animating smooth + * scroll and scroll there immediately. + */ if (ydisp !== this._latestYDisp) { this._scrollableElement.setScrollPosition({ scrollTop: ydisp * this._renderService.dimensions.css.cell.height diff --git a/src/browser/decorations/BufferDecorationRenderer.ts b/src/browser/decorations/BufferDecorationRenderer.ts index e374af260..fb4e047a7 100644 --- a/src/browser/decorations/BufferDecorationRenderer.ts +++ b/src/browser/decorations/BufferDecorationRenderer.ts @@ -1,7 +1,9 @@ -/*--------------------------------------------------------------------------------------------- +/* + *--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ + *-------------------------------------------------------------------------------------------- + */ import { ICoreBrowserService, IRenderService } from 'browser/services/Services'; import { Disposable, toDisposable } from 'common/Lifecycle'; diff --git a/src/browser/decorations/ColorZoneStore.ts b/src/browser/decorations/ColorZoneStore.ts index d066bedb8..d1795ab8e 100644 --- a/src/browser/decorations/ColorZoneStore.ts +++ b/src/browser/decorations/ColorZoneStore.ts @@ -1,7 +1,9 @@ -/*--------------------------------------------------------------------------------------------- +/* + *--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ + *-------------------------------------------------------------------------------------------- + */ import { IInternalDecoration } from 'common/services/Services'; @@ -32,9 +34,11 @@ interface IMinimalDecorationForColorZone { export class ColorZoneStore implements IColorZoneStore { private _zones: IColorZone[] = []; - // The zone pool is used to keep zone objects from being freed between clearing the color zone - // store and fetching the zones. This helps reduce GC pressure since the color zones are - // accumulated on potentially every scroll event. + /* + * The zone pool is used to keep zone objects from being freed between clearing the color zone + * store and fetching the zones. This helps reduce GC pressure since the color zones are + * accumulated on potentially every scroll event. + */ private _zonePool: IColorZone[] = []; private _zonePoolIndex = 0; diff --git a/src/browser/decorations/OverviewRulerRenderer.ts b/src/browser/decorations/OverviewRulerRenderer.ts index b28b25965..8ea3000b3 100644 --- a/src/browser/decorations/OverviewRulerRenderer.ts +++ b/src/browser/decorations/OverviewRulerRenderer.ts @@ -1,7 +1,9 @@ -/*--------------------------------------------------------------------------------------------- +/* + *--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ + *-------------------------------------------------------------------------------------------- + */ import { ColorZoneStore, IColorZone, IColorZoneStore } from 'browser/decorations/ColorZoneStore'; import { ICoreBrowserService, IRenderService, IThemeService } from 'browser/services/Services'; @@ -12,8 +14,10 @@ const enum Constants { OVERVIEW_RULER_BORDER_WIDTH = 1 } -// Helper objects to avoid excessive calculation and garbage collection during rendering. These are -// static values for each render and can be accessed using the decoration position as the key. +/* + * Helper objects to avoid excessive calculation and garbage collection during rendering. These are + * static values for each render and can be accessed using the decoration position as the key. + */ const drawHeight = { full: 0, left: 0, diff --git a/src/browser/input/CompositionHelper.test.ts b/src/browser/input/CompositionHelper.test.ts index 94b5841af..512d5a773 100644 --- a/src/browser/input/CompositionHelper.test.ts +++ b/src/browser/input/CompositionHelper.test.ts @@ -236,9 +236,11 @@ describe('CompositionHelper', () => { it('Should insert middle composition and subsequent input without appending existing trailing text', (done) => { textarea.value = '一二'; - // screenReaderMode keeps textarea content/selection for assistive technologies (eg. screen - // readers), so the caret can be moved within the textarea (eg. via arrow keys) before - // starting composition. + /* + * screenReaderMode keeps textarea content/selection for assistive technologies (eg. screen + * readers), so the caret can be moved within the textarea (eg. via arrow keys) before + * starting composition. + */ textarea.selectionStart = 1; textarea.selectionEnd = 1; diff --git a/src/browser/input/CompositionHelper.ts b/src/browser/input/CompositionHelper.ts index 283874139..f211e2553 100644 --- a/src/browser/input/CompositionHelper.ts +++ b/src/browser/input/CompositionHelper.ts @@ -72,8 +72,10 @@ export class CompositionHelper { */ public compositionstart(): void { this._isComposing = true; - // It's important to use the selection here instead of textarea length to avoid conflicts with - // screen reader mode + /* + * It's important to use the selection here instead of textarea length to avoid conflicts with + * screen reader mode + */ const start = this._textarea.selectionStart ?? this._textarea.value.length; const end = this._textarea.selectionEnd ?? start; this._compositionPosition.start = Math.min(start, end); @@ -89,8 +91,10 @@ export class CompositionHelper { * @param ev The event. */ public compositionupdate(ev: Pick): void { - // Mark text as LTR, direction=rtl is used in CSS so the end of the text is followed for long - // compositions + /* + * Mark text as LTR, direction=rtl is used in CSS so the end of the text is followed for long + * compositions + */ this._compositionView.textContent = `\u200E${ev.data}\u200E`; this.updateCompositionElements(); setTimeout(() => { @@ -115,22 +119,28 @@ export class CompositionHelper { public keydown(ev: KeyboardEvent): boolean { if (this._isComposing || this._isSendingComposition) { if (ev.keyCode === 20 || ev.keyCode === 229) { - // 20 is CapsLock, 229 is Enter - // Continue composing if the keyCode is the "composition character" + /* + * 20 is CapsLock, 229 is Enter + * Continue composing if the keyCode is the "composition character" + */ return false; } if (ev.keyCode === 16 || ev.keyCode === 17 || ev.keyCode === 18) { // Continue composing if the keyCode is a modifier key return false; } - // Finish composition immediately. This is mainly here for the case where enter is - // pressed and the handler needs to be triggered before the command is executed. + /* + * Finish composition immediately. This is mainly here for the case where enter is + * pressed and the handler needs to be triggered before the command is executed. + */ this._finalizeComposition(false); } if (ev.keyCode === 229) { - // If the "composition character" is used but gets to this point it means a non-composition - // character (eg. numbers and punctuation) was pressed when the IME was active. + /* + * If the "composition character" is used but gets to this point it means a non-composition + * character (eg. numbers and punctuation) was pressed when the IME was active. + */ this._handleAnyTextareaChanges(); return false; } @@ -156,39 +166,49 @@ export class CompositionHelper { const input = this._textarea.value.substring(this._compositionPosition.start, this._compositionPosition.end); this._coreService.triggerDataEvent(input, true); } else { - // Make a deep copy of the composition position here as a new compositionstart event may - // fire before the setTimeout executes. + /* + * Make a deep copy of the composition position here as a new compositionstart event may + * fire before the setTimeout executes. + */ const currentCompositionPosition = { start: this._compositionPosition.start, end: this._compositionPosition.end }; const currentCompositionSuffix = this._compositionSuffix; - // Since composition* events happen before the changes take place in the textarea on most - // browsers, use a setTimeout with 0ms time to allow the native compositionend event to - // complete. This ensures the correct character is retrieved. - // This solution was used because: - // - The compositionend event's data property is unreliable, at least on Chromium - // - The last compositionupdate event's data property does not always accurately describe - // the character, a counter example being Korean where an ending consonsant can move to - // the following character if the following input is a vowel. + /* + * Since composition* events happen before the changes take place in the textarea on most + * browsers, use a setTimeout with 0ms time to allow the native compositionend event to + * complete. This ensures the correct character is retrieved. + * This solution was used because: + * - The compositionend event's data property is unreliable, at least on Chromium + * - The last compositionupdate event's data property does not always accurately describe + * the character, a counter example being Korean where an ending consonsant can move to + * the following character if the following input is a vowel. + */ this._isSendingComposition = true; setTimeout(() => { // Ensure that the input has not already been sent if (this._isSendingComposition) { this._isSendingComposition = false; let input; - // Add length of data already sent due to keydown event, - // otherwise input characters can be duplicated. (Issue #3191) + /* + * Add length of data already sent due to keydown event, + * otherwise input characters can be duplicated. (Issue #3191) + */ currentCompositionPosition.start += this._dataAlreadySent.length; if (this._isComposing) { - // Use the start position of the new composition to get the string - // if a new composition has started. + /* + * Use the start position of the new composition to get the string + * if a new composition has started. + */ input = this._textarea.value.substring(currentCompositionPosition.start, this._compositionPosition.start); } else { - // Keep support for non-composition characters typed immediately after composition end - // while avoiding re-sending the trailing text that was already present - // before composition started. + /* + * Keep support for non-composition characters typed immediately after composition end + * while avoiding re-sending the trailing text that was already present + * before composition started. + */ const value = this._textarea.value; const valueEnd = currentCompositionSuffix.length > 0 && value.endsWith(currentCompositionSuffix) ? value.length - currentCompositionSuffix.length @@ -260,14 +280,18 @@ export class CompositionHelper { this._compositionView.style.lineHeight = cellHeight + 'px'; this._compositionView.style.fontFamily = this._optionsService.rawOptions.fontFamily; this._compositionView.style.fontSize = this._optionsService.rawOptions.fontSize + 'px'; - // Limit the composition view width to the space between the cursor and - // the terminal's right edge, preventing it from overflowing the terminal. + /* + * Limit the composition view width to the space between the cursor and + * the terminal's right edge, preventing it from overflowing the terminal. + */ const maxWidth = this._bufferService.cols * this._renderService.dimensions.css.cell.width - cursorLeft; this._compositionView.style.maxWidth = maxWidth + 'px'; this._compositionView.style.overflow = 'hidden'; this._compositionView.style.direction = 'rtl'; - // Sync the textarea to the exact position of the composition view so the IME knows where the - // text is. + /* + * Sync the textarea to the exact position of the composition view so the IME knows where the + * text is. + */ const compositionViewBounds = this._compositionView.getBoundingClientRect(); this._textarea.style.left = cursorLeft + 'px'; this._textarea.style.top = cursorTop + 'px'; diff --git a/src/browser/input/Mouse.ts b/src/browser/input/Mouse.ts index c40a7cc78..1094cdcf4 100644 --- a/src/browser/input/Mouse.ts +++ b/src/browser/input/Mouse.ts @@ -44,9 +44,11 @@ export function getCoords(window: Pick, event: Pick< coords[0] = Math.ceil((coords[0] + (isSelection ? cssCellWidth / 2 : 0)) / cssCellWidth); coords[1] = Math.ceil(coords[1] / cssCellHeight); - // Ensure coordinates are within the terminal viewport. Note that selections - // need an addition point of precision to cover the end point (as characters - // cover half of one char and half of the next). + /* + * Ensure coordinates are within the terminal viewport. Note that selections + * need an addition point of precision to cover the end point (as characters + * cover half of one char and half of the next). + */ coords[0] = Math.min(Math.max(coords[0], 1), colCount + (isSelection ? 1 : 0)); coords[1] = Math.min(Math.max(coords[1], 1), rowCount); diff --git a/src/browser/public/Terminal.ts b/src/browser/public/Terminal.ts index 75aada048..9255f67f2 100644 --- a/src/browser/public/Terminal.ts +++ b/src/browser/public/Terminal.ts @@ -54,9 +54,11 @@ export class Terminal extends Disposable implements ITerminalApi { } private _checkReadonlyOptions(propName: string): void { - // Throw an error if any constructor only option is modified - // from terminal.options - // Modifications from anywhere else are allowed + /* + * Throw an error if any constructor only option is modified + * from terminal.options + * Modifications from anywhere else are allowed + */ if (CONSTRUCTOR_ONLY_OPTIONS.includes(propName)) { throw new Error(`Option "${propName}" can only be set in the constructor`); } diff --git a/src/browser/renderer/dom/DomRenderer.ts b/src/browser/renderer/dom/DomRenderer.ts index 6f873af7b..943906524 100644 --- a/src/browser/renderer/dom/DomRenderer.ts +++ b/src/browser/renderer/dom/DomRenderer.ts @@ -114,8 +114,10 @@ export class DomRenderer extends Disposable implements IRenderer { this._register(toDisposable(() => { this._element.classList.remove(Constants.TERMINAL_CLASS_PREFIX + this._terminalClass); - // Outside influences such as React unmounts may manipulate the DOM before our disposal. - // https://github.com/xtermjs/xterm.js/issues/2960 + /* + * Outside influences such as React unmounts may manipulate the DOM before our disposal. + * https://github.com/xtermjs/xterm.js/issues/2960 + */ this._rowContainer.remove(); this._selectionContainer.remove(); this._widthCache.dispose(); @@ -184,9 +186,11 @@ export class DomRenderer extends Disposable implements IRenderer { // Base CSS let styles = `${this._terminalSelector} .${Constants.ROW_CONTAINER_CLASS} {` + - // Disabling pointer events circumvents a browser behavior that prevents `click` events from - // being delivered if the target element is replaced during the click. This happened due to - // refresh() being called during the mousedown handler to start a selection. + /* + * Disabling pointer events circumvents a browser behavior that prevents `click` events from + * being delivered if the target element is replaced during the click. This happened due to + * refresh() being called during the mousedown handler to start a selection. + */ ` pointer-events: none;` + ` color: ${colors.foreground.css};` + `}`; @@ -257,9 +261,11 @@ export class DomRenderer extends Disposable implements IRenderer { `${this._terminalSelector} .${Constants.ROW_CONTAINER_CLASS}.${Constants.CURSOR_BLINK_IDLE_CLASS} .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_BLINK_CLASS} {` + ` animation: none !important;` + `}` + - // !important helps fix an issue where the cursor will not render on top of the selection, - // however it's very hard to fix this issue and retain the blink animation without the use of - // !important. So this edge case fails when cursor blink is on. + /* + * !important helps fix an issue where the cursor will not render on top of the selection, + * however it's very hard to fix this issue and retain the blink animation without the use of + * !important. So this edge case fails when cursor blink is on. + */ `${this._terminalSelector} .${Constants.ROW_CONTAINER_CLASS} .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_STYLE_BLOCK_CLASS} {` + ` background-color: ${colors.cursor.css};` + ` color: ${colors.cursorAccent.css};` + diff --git a/src/browser/renderer/dom/DomRendererRowFactory.test.ts b/src/browser/renderer/dom/DomRendererRowFactory.test.ts index 6ca84569a..4bd5b8e7b 100644 --- a/src/browser/renderer/dom/DomRendererRowFactory.test.ts +++ b/src/browser/renderer/dom/DomRendererRowFactory.test.ts @@ -359,8 +359,10 @@ describe('DomRendererRowFactory', () => { }); describe('createRow with merged spans', () => { - // for test purpose assume all in codepoints 0..255 are merging - // const ALL_MERGING = new Uint8Array(FontMetrics.MAX); + /* + * for test purpose assume all in codepoints 0..255 are merging + * const ALL_MERGING = new Uint8Array(FontMetrics.MAX); + */ beforeEach(() => { lineData = createEmptyLineData(10); diff --git a/src/browser/renderer/dom/DomRendererRowFactory.ts b/src/browser/renderer/dom/DomRendererRowFactory.ts index 0cff9c25f..fc03e27b3 100644 --- a/src/browser/renderer/dom/DomRendererRowFactory.ts +++ b/src/browser/renderer/dom/DomRendererRowFactory.ts @@ -115,20 +115,26 @@ export class DomRendererRowFactory { // If true, indicates that the current character(s) to draw were joined. let isJoined = false; - // Indicates whether this cell is part of a joined range that should be ignored as it cannot - // be rendered entirely, like the selection state differs across the range. + /* + * Indicates whether this cell is part of a joined range that should be ignored as it cannot + * be rendered entirely, like the selection state differs across the range. + */ let isValidJoinRange = (x >= skipJoinedCheckUntilX); let lastCharX = x; - // Process any joined character ranges as needed. Because of how the - // ranges are produced, we know that they are valid for the characters - // and attributes of our input. + /* + * Process any joined character ranges as needed. Because of how the + * ranges are produced, we know that they are valid for the characters + * and attributes of our input. + */ let cell: ICellData = this._workCell; if (joinedRanges.length > 0 && x === joinedRanges[0][0] && isValidJoinRange) { const range = joinedRanges.shift()!; - // If the ligature's selection state is not consistent, don't join it. This helps the - // selection render correctly regardless whether they should be joined. + /* + * If the ligature's selection state is not consistent, don't join it. This helps the + * selection render correctly regardless whether they should be joined. + */ const firstSelectionState = this._isCellInSelection(range[0], row); for (i = range[0] + 1; i < range[1]; i++) { isValidJoinRange &&= (firstSelectionState === this._isCellInSelection(i, row)); @@ -140,8 +146,10 @@ export class DomRendererRowFactory { } else { isJoined = true; - // We already know the exact start and end column of the joined range, - // so we get the string and width representing it directly + /* + * We already know the exact start and end column of the joined range, + * so we get the string and width representing it directly + */ cell = new JoinedCellData( this._workCell, lineData.translateToString(true, range[0], range[1]), @@ -243,9 +251,11 @@ export class DomRendererRowFactory { oldIsInSelection = isInSelection; if (isJoined) { - // The DOM renderer colors the background of the cursor but for ligatures all cells are - // joined. The workaround here is to show a cursor around the whole ligature so it shows up, - // the cursor looks the same when on any character of the ligature though + /* + * The DOM renderer colors the background of the cursor but for ligatures all cells are + * joined. The workaround here is to show a cursor around the whole ligature so it shows up, + * the cursor looks the same when on any character of the ligature though + */ if (cursorX >= x && cursorX <= lastCharX) { cursorX = x; } @@ -333,8 +343,10 @@ export class DomRendererRowFactory { classes.push(RowCss.STRIKETHROUGH_CLASS); } - // apply link hover underline late, effectively overrides any previous text-decoration - // settings + /* + * apply link hover underline late, effectively overrides any previous text-decoration + * settings + */ if (isLinkHover) { charElement.style.textDecoration = 'underline'; } @@ -353,8 +365,10 @@ export class DomRendererRowFactory { bgColorMode = temp2; } - // Apply any decoration foreground/background overrides, this must happen after inverse has - // been applied + /* + * Apply any decoration foreground/background overrides, this must happen after inverse has + * been applied + */ let bgOverride: IColor | undefined; let fgOverride: IColor | undefined; let isTop = false; @@ -377,15 +391,19 @@ export class DomRendererRowFactory { // Apply selection if (!isTop && isInSelection) { - // If in the selection, force the element to be above the selection to improve contrast and - // support opaque selections. The applies background is not actually needed here as - // selection is drawn in a seperate container, the main purpose of this to ensuring minimum - // contrast ratio + /* + * If in the selection, force the element to be above the selection to improve contrast and + * support opaque selections. The applies background is not actually needed here as + * selection is drawn in a seperate container, the main purpose of this to ensuring minimum + * contrast ratio + */ bgOverride = this._coreBrowserService.isFocused ? colors.selectionBackgroundOpaque : colors.selectionInactiveBackgroundOpaque; bg = bgOverride.rgba >> 8 & 0xFFFFFF; bgColorMode = Attributes.CM_RGB; - // Since an opaque selection is being rendered, the selection pretends to be a decoration to - // ensure text is drawn above the selection. + /* + * Since an opaque selection is being rendered, the selection pretends to be a decoration to + * ensure text is drawn above the selection. + */ isTop = true; // Apply selection foreground if applicable if (colors.selectionForeground) { @@ -459,9 +477,11 @@ export class DomRendererRowFactory { } } - // apply CSS classes - // slightly faster than using classList by omitting - // checks for doubled entries (code above should not have doublets) + /* + * apply CSS classes + * slightly faster than using classList by omitting + * checks for doubled entries (code above should not have doublets) + */ if (classes.length) { charElement.className = classes.join(' '); classes.length = 0; @@ -504,8 +524,10 @@ export class DomRendererRowFactory { // Calculate and store in cache if (adjustedColor === undefined) { - // Dim cells only require half the contrast, otherwise they wouldn't be distinguishable from - // non-dim cells + /* + * Dim cells only require half the contrast, otherwise they wouldn't be distinguishable from + * non-dim cells + */ const ratio = this._optionsService.rawOptions.minimumContrastRatio / (cell.isDim() ? 2 : 1); adjustedColor = color.ensureContrastRatio(bgOverride ?? bg, fgOverride ?? fg, ratio); cache.setColor((bgOverride ?? bg).rgba, (fgOverride ?? fg).rgba, adjustedColor ?? null); diff --git a/src/browser/renderer/dom/WidthCache.ts b/src/browser/renderer/dom/WidthCache.ts index b598bbd6f..1bf66204d 100644 --- a/src/browser/renderer/dom/WidthCache.ts +++ b/src/browser/renderer/dom/WidthCache.ts @@ -31,16 +31,20 @@ export interface IWidthCacheFontVariantCanvas { } export class WidthCache implements IDisposable { - // flat cache for regular variant up to CacheSettings.FLAT_SIZE - // NOTE: ~4x faster access than holey (serving >>80% of terminal content) - // It has a small memory footprint (only 1MB for full BMP caching), - // still the sweet spot is not reached before touching 32k different codepoints, - // thus we store the remaining <<20% of terminal data in a holey structure. + /* + * flat cache for regular variant up to CacheSettings.FLAT_SIZE + * NOTE: ~4x faster access than holey (serving >>80% of terminal content) + * It has a small memory footprint (only 1MB for full BMP caching), + * still the sweet spot is not reached before touching 32k different codepoints, + * thus we store the remaining <<20% of terminal data in a holey structure. + */ protected _flat = new Float32Array(WidthCacheSettings.FLAT_SIZE); - // holey cache for bold, italic and bold&italic for any string - // FIXME: can grow really big over time (~8.5 MB for full BMP caching), - // so a shared API across terminals is needed + /* + * holey cache for bold, italic and bold&italic for any string + * FIXME: can grow really big over time (~8.5 MB for full BMP caching), + * so a shared API across terminals is needed + */ protected _holey: Map | undefined; private _font = ''; diff --git a/src/browser/renderer/shared/RendererUtils.test.ts b/src/browser/renderer/shared/RendererUtils.test.ts index 4512cb6d9..87f6a3f20 100644 --- a/src/browser/renderer/shared/RendererUtils.test.ts +++ b/src/browser/renderer/shared/RendererUtils.test.ts @@ -13,8 +13,10 @@ describe('RendererUtils', () => { let line = 1; let variantOffset = 0; - // should line 1 - // =,_,=_,=_, + /* + * should line 1 + * =,_,=_,=_, + */ let cells = [cellWidth, cellWidth, doubleCellWidth, doubleCellWidth]; let result = [1, 0, 0, 0]; for (let index = 0; index < cells.length; index++) { @@ -23,8 +25,10 @@ describe('RendererUtils', () => { assert.equal(variantOffset, result[index]); } - // should line 2 - // ==__==__==_,_==__==__==,__==__==__==__==__==__,==__==__==__==__==__==, + /* + * should line 2 + * ==__==__==_,_==__==__==,__==__==__==__==__==__,==__==__==__==__==__==, + */ line = 2; variantOffset = 0; cells = [cellWidth, cellWidth, doubleCellWidth, doubleCellWidth]; @@ -35,8 +39,10 @@ describe('RendererUtils', () => { assert.equal(variantOffset, result[index]); } - // should line 3 - // ===___===__,_===___===_,__===___===___===___==,=___===___===___===___, + /* + * should line 3 + * ===___===__,_===___===_,__===___===___===___==,=___===___===___===___, + */ line = 3; variantOffset = 0; cells = [cellWidth, cellWidth, doubleCellWidth, doubleCellWidth]; diff --git a/src/browser/renderer/shared/RendererUtils.ts b/src/browser/renderer/shared/RendererUtils.ts index 010643648..30925f32f 100644 --- a/src/browser/renderer/shared/RendererUtils.ts +++ b/src/browser/renderer/shared/RendererUtils.ts @@ -13,9 +13,11 @@ export function throwIfFalsy(value: T | undefined | null): T { } export function isPowerlineGlyph(codepoint: number): boolean { - // Only return true for Powerline symbols which require - // different padding and should be excluded from minimum contrast - // ratio standards + /* + * Only return true for Powerline symbols which require + * different padding and should be excluded from minimum contrast + * ratio standards + */ return 0xE0A4 <= codepoint && codepoint <= 0xE0D6; } @@ -48,8 +50,10 @@ export function allowRescaling(codepoint: number | undefined, width: number, gly return ( // Is single cell width width === 1 && - // Glyph exceeds cell bounds, add 50% to avoid hurting readability by rescaling glyphs that - // barely overlap + /* + * Glyph exceeds cell bounds, add 50% to avoid hurting readability by rescaling glyphs that + * barely overlap + */ glyphSizeX > Math.ceil(deviceCellWidth * 1.5) && // Never rescale ascii codepoint !== undefined && codepoint > 0xFF && diff --git a/src/browser/scrollable/abstractScrollbar.ts b/src/browser/scrollable/abstractScrollbar.ts index 64c25d9d8..0463471b5 100644 --- a/src/browser/scrollable/abstractScrollbar.ts +++ b/src/browser/scrollable/abstractScrollbar.ts @@ -1,7 +1,9 @@ -/*--------------------------------------------------------------------------------------------- +/* + *--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ + *-------------------------------------------------------------------------------------------- + */ import * as dom from '../Dom'; import { FastDomNode } from './fastDomNode'; diff --git a/src/browser/scrollable/fastDomNode.ts b/src/browser/scrollable/fastDomNode.ts index 1a795c2be..206416677 100644 --- a/src/browser/scrollable/fastDomNode.ts +++ b/src/browser/scrollable/fastDomNode.ts @@ -1,7 +1,9 @@ -/*--------------------------------------------------------------------------------------------- +/* + *--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ + *-------------------------------------------------------------------------------------------- + */ export class FastDomNode { diff --git a/src/browser/scrollable/globalPointerMoveMonitor.ts b/src/browser/scrollable/globalPointerMoveMonitor.ts index a80e0507f..b2e9b588a 100644 --- a/src/browser/scrollable/globalPointerMoveMonitor.ts +++ b/src/browser/scrollable/globalPointerMoveMonitor.ts @@ -1,7 +1,9 @@ -/*--------------------------------------------------------------------------------------------- +/* + *--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ + *-------------------------------------------------------------------------------------------- + */ import * as dom from '../Dom'; import { DisposableStore, IDisposable, toDisposable } from 'common/Lifecycle'; diff --git a/src/browser/scrollable/horizontalScrollbar.ts b/src/browser/scrollable/horizontalScrollbar.ts index 299a3e473..8e48ea7ff 100644 --- a/src/browser/scrollable/horizontalScrollbar.ts +++ b/src/browser/scrollable/horizontalScrollbar.ts @@ -1,7 +1,9 @@ -/*--------------------------------------------------------------------------------------------- +/* + *--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ + *-------------------------------------------------------------------------------------------- + */ import { AbstractScrollbar, ISimplifiedPointerEvent, IScrollbarHost } from './abstractScrollbar'; import { IScrollableElementResolvedOptions } from './scrollableElementOptions'; diff --git a/src/browser/scrollable/mouseEvent.ts b/src/browser/scrollable/mouseEvent.ts index a12b63d52..482ba82aa 100644 --- a/src/browser/scrollable/mouseEvent.ts +++ b/src/browser/scrollable/mouseEvent.ts @@ -1,7 +1,9 @@ -/*--------------------------------------------------------------------------------------------- +/* + *--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ + *-------------------------------------------------------------------------------------------- + */ import * as platform from 'common/Platform'; diff --git a/src/browser/scrollable/scrollable.ts b/src/browser/scrollable/scrollable.ts index 18275ffe5..9f8cbb6f7 100644 --- a/src/browser/scrollable/scrollable.ts +++ b/src/browser/scrollable/scrollable.ts @@ -1,7 +1,9 @@ -/*--------------------------------------------------------------------------------------------- +/* + *--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ + *-------------------------------------------------------------------------------------------- + */ import { Emitter, IEvent } from 'common/Event'; import { Disposable, IDisposable } from 'common/Lifecycle'; diff --git a/src/browser/scrollable/scrollableElement.ts b/src/browser/scrollable/scrollableElement.ts index 4039d0c4e..a41dde991 100644 --- a/src/browser/scrollable/scrollableElement.ts +++ b/src/browser/scrollable/scrollableElement.ts @@ -1,7 +1,9 @@ -/*--------------------------------------------------------------------------------------------- +/* + *--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ + *-------------------------------------------------------------------------------------------- + */ import * as dom from '../Dom'; import { FastDomNode } from './fastDomNode'; diff --git a/src/browser/scrollable/scrollableElementOptions.ts b/src/browser/scrollable/scrollableElementOptions.ts index 037cd5c11..0e1d06226 100644 --- a/src/browser/scrollable/scrollableElementOptions.ts +++ b/src/browser/scrollable/scrollableElementOptions.ts @@ -1,7 +1,9 @@ -/*--------------------------------------------------------------------------------------------- +/* + *--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ + *-------------------------------------------------------------------------------------------- + */ import { ScrollbarVisibility } from './scrollable'; diff --git a/src/browser/scrollable/scrollbarArrow.ts b/src/browser/scrollable/scrollbarArrow.ts index d543669c1..5784dc8dc 100644 --- a/src/browser/scrollable/scrollbarArrow.ts +++ b/src/browser/scrollable/scrollbarArrow.ts @@ -1,7 +1,9 @@ -/*--------------------------------------------------------------------------------------------- +/* + *--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ + *-------------------------------------------------------------------------------------------- + */ import { GlobalPointerMoveMonitor } from './globalPointerMoveMonitor'; import { Widget } from './widget'; diff --git a/src/browser/scrollable/scrollbarState.ts b/src/browser/scrollable/scrollbarState.ts index f9129f1b0..7b7751f97 100644 --- a/src/browser/scrollable/scrollbarState.ts +++ b/src/browser/scrollable/scrollbarState.ts @@ -1,7 +1,9 @@ -/*--------------------------------------------------------------------------------------------- +/* + *--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ + *-------------------------------------------------------------------------------------------- + */ /** * The minimal size of the slider (such that it can still be clickable). diff --git a/src/browser/scrollable/scrollbarVisibilityController.ts b/src/browser/scrollable/scrollbarVisibilityController.ts index 59d172bce..b56e3524f 100644 --- a/src/browser/scrollable/scrollbarVisibilityController.ts +++ b/src/browser/scrollable/scrollbarVisibilityController.ts @@ -1,7 +1,9 @@ -/*--------------------------------------------------------------------------------------------- +/* + *--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ + *-------------------------------------------------------------------------------------------- + */ import { FastDomNode } from './fastDomNode'; import { TimeoutTimer } from 'common/Async'; diff --git a/src/browser/scrollable/touch.ts b/src/browser/scrollable/touch.ts index 8ffdfe699..a734a79a1 100644 --- a/src/browser/scrollable/touch.ts +++ b/src/browser/scrollable/touch.ts @@ -1,7 +1,9 @@ -/*--------------------------------------------------------------------------------------------- +/* + *--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ + *-------------------------------------------------------------------------------------------- + */ import * as DomUtils from '../Dom'; import { Disposable, IDisposable, toDisposable } from 'common/Lifecycle'; diff --git a/src/browser/scrollable/verticalScrollbar.ts b/src/browser/scrollable/verticalScrollbar.ts index 615f443a0..9f5a090af 100644 --- a/src/browser/scrollable/verticalScrollbar.ts +++ b/src/browser/scrollable/verticalScrollbar.ts @@ -1,7 +1,9 @@ -/*--------------------------------------------------------------------------------------------- +/* + *--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ + *-------------------------------------------------------------------------------------------- + */ import { AbstractScrollbar, ISimplifiedPointerEvent, IScrollbarHost } from './abstractScrollbar'; import { IScrollableElementResolvedOptions } from './scrollableElementOptions'; diff --git a/src/browser/scrollable/widget.ts b/src/browser/scrollable/widget.ts index 80220c9b1..eae4ec185 100644 --- a/src/browser/scrollable/widget.ts +++ b/src/browser/scrollable/widget.ts @@ -1,7 +1,9 @@ -/*--------------------------------------------------------------------------------------------- +/* + *--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ + *-------------------------------------------------------------------------------------------- + */ import * as dom from '../Dom'; import { IMouseEvent, StandardMouseEvent } from './mouseEvent'; diff --git a/src/browser/services/CharSizeService.ts b/src/browser/services/CharSizeService.ts index 6baf9b50a..8d97f233c 100644 --- a/src/browser/services/CharSizeService.ts +++ b/src/browser/services/CharSizeService.ts @@ -61,8 +61,10 @@ abstract class BaseMeasureStategy extends Disposable implements IMeasureStrategy protected _result: IMeasureResult = { width: 0, height: 0 }; protected _validateAndSet(width: number | undefined, height: number | undefined): void { - // If values are 0 then the element is likely currently display:none, in which case we should - // retain the previous value. + /* + * If values are 0 then the element is likely currently display:none, in which case we should + * retain the previous value. + */ if (width !== undefined && width > 0 && height !== undefined && height > 0) { this._result.width = width; this._result.height = height; diff --git a/src/browser/services/CharacterJoinerService.ts b/src/browser/services/CharacterJoinerService.ts index ca4f1984e..d0ded2623 100644 --- a/src/browser/services/CharacterJoinerService.ts +++ b/src/browser/services/CharacterJoinerService.ts @@ -13,8 +13,10 @@ import { ICharacterJoinerService } from 'browser/services/Services'; export class JoinedCellData extends AttributeData implements ICellData { private _width: number; - // .content carries no meaning for joined CellData, simply nullify it - // thus we have to overload all other .content accessors + /* + * .content carries no meaning for joined CellData, simply nullify it + * thus we have to overload all other .content accessors + */ public content: number = 0; public fg: number; public bg: number; @@ -42,8 +44,10 @@ export class JoinedCellData extends AttributeData implements ICellData { } public getCode(): number { - // code always gets the highest possible fake codepoint (read as -1) - // this is needed as code is used by caches as identifier + /* + * code always gets the highest possible fake codepoint (read as -1) + * this is needed as code is used by caches as identifier + */ return 0x1FFFFF; } @@ -101,10 +105,12 @@ export class CharacterJoinerService implements ICharacterJoinerService { const ranges: [number, number][] = []; const lineStr = line.translateToString(true); - // Because some cells can be represented by multiple javascript characters, - // we track the cell and the string indexes separately. This allows us to - // translate the string ranges we get from the joiners back into cell ranges - // for use when rendering + /* + * Because some cells can be represented by multiple javascript characters, + * we track the cell and the string indexes separately. This allows us to + * translate the string ranges we get from the joiners back into cell ranges + * for use when rendering + */ let rangeStartColumn = 0; let currentStringIndex = 0; let rangeStartStringIndex = 0; @@ -121,8 +127,10 @@ export class CharacterJoinerService implements ICharacterJoinerService { // End of range if (this._workCell.fg !== rangeAttrFG || this._workCell.bg !== rangeAttrBG) { - // If we ended up with a sequence of more than one character, - // look for ranges to join. + /* + * If we ended up with a sequence of more than one character, + * look for ranges to join. + */ if (x - rangeStartColumn > 1) { const joinedRanges = this._getJoinedRanges( lineStr, @@ -173,9 +181,11 @@ export class CharacterJoinerService implements ICharacterJoinerService { */ private _getJoinedRanges(line: string, startIndex: number, endIndex: number, lineData: IBufferLine, startCol: number): [number, number][] { const text = line.substring(startIndex, endIndex); - // At this point we already know that there is at least one joiner so - // we can just pull its value and assign it directly rather than - // merging it into an empty array, which incurs unnecessary writes. + /* + * At this point we already know that there is at least one joiner so + * we can just pull its value and assign it directly rather than + * merging it into an empty array, which incurs unnecessary writes. + */ let allJoinedRanges: [number, number][] = []; try { allJoinedRanges = this._characterJoiners[0].handler(text); @@ -220,8 +230,10 @@ export class CharacterJoinerService implements ICharacterJoinerService { const width = line.getWidth(x); const length = line.getString(x).length || WHITESPACE_CELL_CHAR.length; - // We skip zero-width characters when creating the string to join the text - // so we do the same here + /* + * We skip zero-width characters when creating the string to join the text + * so we do the same here + */ if (width === 0) { continue; } @@ -244,10 +256,12 @@ export class CharacterJoinerService implements ICharacterJoinerService { break; } - // Ranges can be on adjacent characters. Because the end index of the - // ranges are exclusive, this means that the index for the start of a - // range can be the same as the end index of the previous range. To - // account for the start of the next range, we check here just in case. + /* + * Ranges can be on adjacent characters. Because the end index of the + * ranges are exclusive, this means that the index for the start of a + * range can be the same as the end index of the previous range. To + * account for the start of the next range, we check here just in case. + */ if (currentRange[0] <= currentStringIndex) { currentRange[0] = x; currentRangeStarted = true; @@ -256,13 +270,17 @@ export class CharacterJoinerService implements ICharacterJoinerService { } } - // Adjust the string index based on the character length to line up with - // the column adjustment + /* + * Adjust the string index based on the character length to line up with + * the column adjustment + */ currentStringIndex += length; } - // If there is still a range left at the end, it must extend all the way to - // the end of the line. + /* + * If there is still a range left at the end, it must extend all the way to + * the end of the line. + */ if (currentRange) { currentRange[1] = this._bufferService.cols; } @@ -288,15 +306,19 @@ export class CharacterJoinerService implements ICharacterJoinerService { } if (newRange[1] <= range[1]) { - // Case 2: New range is either wholly contained within the - // search range or overlaps with the front of it + /* + * Case 2: New range is either wholly contained within the + * search range or overlaps with the front of it + */ range[0] = Math.min(newRange[0], range[0]); return ranges; } if (newRange[0] < range[1]) { - // Case 3: New range either wholly contains the search range - // or overlaps with the end of it + /* + * Case 3: New range either wholly contains the search range + * or overlaps with the end of it + */ range[0] = Math.min(newRange[0], range[0]); inRange = true; } @@ -305,22 +327,28 @@ export class CharacterJoinerService implements ICharacterJoinerService { continue; } else { if (newRange[1] <= range[0]) { - // Case 5: New range extends from previous range but doesn't - // reach the current one + /* + * Case 5: New range extends from previous range but doesn't + * reach the current one + */ ranges[i - 1][1] = newRange[1]; return ranges; } if (newRange[1] <= range[1]) { - // Case 6: New range extends from prvious range into the - // current range + /* + * Case 6: New range extends from prvious range into the + * current range + */ ranges[i - 1][1] = Math.max(newRange[1], range[1]); ranges.splice(i, 1); return ranges; } - // Case 7: New range extends from previous range past the - // end of the current range + /* + * Case 7: New range extends from previous range past the + * end of the current range + */ ranges.splice(i, 1); i--; } diff --git a/src/browser/services/MouseService.test.ts b/src/browser/services/MouseService.test.ts index f395083e3..63e14954a 100644 --- a/src/browser/services/MouseService.test.ts +++ b/src/browser/services/MouseService.test.ts @@ -127,8 +127,10 @@ describe('MouseService _triggerMouseEvent', () => { assert.equal(trigger({ col: 0, row: 0, x: 0, y: 0, button: CoreMouseButton.RIGHT, action: CoreMouseAction.DOWN }), true); assert.equal(trigger({ col: 0, row: 0, x: 0, y: 0, button: CoreMouseButton.WHEEL, action: CoreMouseAction.UP }), true); assert.equal(trigger({ col: 0, row: 0, x: 0, y: 0, button: CoreMouseButton.NONE, action: CoreMouseAction.MOVE }), true); - // should not report in any case - // invalid button + action combinations + /* + * should not report in any case + * invalid button + action combinations + */ assert.equal(trigger({ col: 0, row: 0, x: 0, y: 0, button: CoreMouseButton.WHEEL, action: CoreMouseAction.MOVE }), false); assert.equal(trigger({ col: 0, row: 0, x: 0, y: 0, button: CoreMouseButton.NONE, action: CoreMouseAction.DOWN }), false); assert.equal(trigger({ col: 0, row: 0, x: 0, y: 0, button: CoreMouseButton.NONE, action: CoreMouseAction.UP }), false); diff --git a/src/browser/services/MouseService.ts b/src/browser/services/MouseService.ts index d701796c2..2f35957b7 100644 --- a/src/browser/services/MouseService.ts +++ b/src/browser/services/MouseService.ts @@ -147,8 +147,10 @@ export class MouseService implements IMouseService { return false; } - // exit if we cannot determine valid button/action values - // do nothing for higher buttons than wheel + /* + * exit if we cannot determine valid button/action values + * do nothing for higher buttons than wheel + */ if (action === undefined || but === undefined || but > CoreMouseButton.WHEEL) { return false; } @@ -204,19 +206,23 @@ export class MouseService implements IMouseService { ev.preventDefault(); ctx.focus(); - // Don't send the mouse button to the pty if mouse events are disabled or - // if the selection manager is having selection forced (ie. a modifier is - // held). + /* + * Don't send the mouse button to the pty if mouse events are disabled or + * if the selection manager is having selection forced (ie. a modifier is + * held). + */ if (!this._mouseStateService.areMouseEventsActive || this._selectionService.shouldForceSelection(ev)) { return; } this._sendEvent(ctx, ev); - // Register additional global handlers which should keep reporting outside - // of the terminal element. - // Note: Other emulators also do this for 'mousedown' while a button - // is held, we currently limit 'mousedown' to the terminal only. + /* + * Register additional global handlers which should keep reporting outside + * of the terminal element. + * Note: Other emulators also do this for 'mousedown' while a button + * is held, we currently limit 'mousedown' to the terminal only. + */ if (ctx.requestedEvents.mouseup) { ctx.target.document.addEventListener('mouseup', ctx.requestedEvents.mouseup); } @@ -236,12 +242,14 @@ export class MouseService implements IMouseService { } if (!this._bufferService.buffer.hasScrollback) { - // Convert wheel events into up/down events when the buffer does not have scrollback, this - // enables scrolling in apps hosted in the alt buffer such as vim or tmux even when mouse - // events are not enabled. - // This used implementation used get the actual lines/partial lines scrolled from the - // viewport but since moving to the new viewport implementation has been simplified to - // simply send a single up or down sequence. + /* + * Convert wheel events into up/down events when the buffer does not have scrollback, this + * enables scrolling in apps hosted in the alt buffer such as vim or tmux even when mouse + * events are not enabled. + * This used implementation used get the actual lines/partial lines scrolled from the + * viewport but since moving to the new viewport implementation has been simplified to + * simply send a single up or down sequence. + */ // Do nothing if there's no vertical scroll const deltaY = ev.deltaY; diff --git a/src/browser/services/RenderService.ts b/src/browser/services/RenderService.ts index e999b7eac..433a24eb5 100644 --- a/src/browser/services/RenderService.ts +++ b/src/browser/services/RenderService.ts @@ -88,9 +88,11 @@ export class RenderService extends Disposable implements IRenderService { this._register(this._optionsService.onOptionChange(() => this._handleOptionsChanged())); this._register(this._charSizeService.onCharSizeChange(() => this.handleCharSizeChanged())); - // Do a full refresh whenever any decoration is added or removed. This may not actually result - // in changes but since decorations should be used sparingly or added/removed all in the same - // frame this should have minimal performance impact. + /* + * Do a full refresh whenever any decoration is added or removed. This may not actually result + * in changes but since decorations should be used sparingly or added/removed all in the same + * frame this should have minimal performance impact. + */ this._register(decorationService.onDecorationRegistered(() => this._fullRefresh())); this._register(decorationService.onDecorationRemoved(() => this._fullRefresh())); @@ -124,8 +126,10 @@ export class RenderService extends Disposable implements IRenderService { } private _registerIntersectionObserver(w: Window & typeof globalThis, screenElement: HTMLElement): void { - // Detect whether IntersectionObserver is detected and enable renderer pause - // and resume based on terminal visibility if so + /* + * Detect whether IntersectionObserver is detected and enable renderer pause + * and resume based on terminal visibility if so + */ if ('IntersectionObserver' in w) { const observer = new w.IntersectionObserver(e => this._handleIntersectionChange(e[e.length - 1]), { threshold: 0 }); this._observerDisposable.value = toDisposable(() => { @@ -186,16 +190,20 @@ export class RenderService extends Disposable implements IRenderService { return; } - // Skip rendering if synchronized output mode is enabled. This check must happen here - // (in addition to refreshRows) to handle renders that were queued before the mode was enabled. + /* + * Skip rendering if synchronized output mode is enabled. This check must happen here + * (in addition to refreshRows) to handle renders that were queued before the mode was enabled. + */ if (this._coreService.decPrivateModes.synchronizedOutput) { this._syncOutputHandler.bufferRows(start, end); return; } - // Since this is debounced, a resize event could have happened between the time a refresh was - // requested and when this triggers. Clamp the values of start and end to ensure they're valid - // given the current viewport state. + /* + * Since this is debounced, a resize event could have happened between the time a refresh was + * requested and when this triggers. Clamp the values of start and end to ensure they're valid + * given the current viewport state. + */ start = Math.min(start, this._rowCount - 1); end = Math.min(end, this._rowCount - 1); @@ -277,8 +285,10 @@ export class RenderService extends Disposable implements IRenderService { } public handleDevicePixelRatioChange(): void { - // Force char size measurement as DomMeasureStrategy(getBoundingClientRect) is not stable - // when devicePixelRatio changes + /* + * Force char size measurement as DomMeasureStrategy(getBoundingClientRect) is not stable + * when devicePixelRatio changes + */ this._charSizeService.measure(); if (!this._renderer.value) { diff --git a/src/browser/services/SelectionService.test.ts b/src/browser/services/SelectionService.test.ts index be62ccc6b..822bb6b6d 100644 --- a/src/browser/services/SelectionService.test.ts +++ b/src/browser/services/SelectionService.test.ts @@ -257,8 +257,10 @@ describe('SelectionService', () => { ])); // The a is here to prevent the space being trimmed in selectionText selectionService.selectWordAt([0, 0]); assert.equal(selectionService.selectionText, ' '); - // ZWJ emojis do not combine in the terminal so the family emoji used here consumed 4 cells - // The selection text should retain ZWJ chars despite not combining on the terminal + /* + * ZWJ emojis do not combine in the terminal so the family emoji used here consumed 4 cells + * The selection text should retain ZWJ chars despite not combining on the terminal + */ selectionService.selectWordAt([1, 0]); assert.equal(selectionService.selectionText, '👨‍👩‍👧‍👦'); selectionService.selectWordAt([2, 0]); @@ -473,8 +475,10 @@ describe('SelectionService', () => { }); it('should select a column of text with double character emojis', () => { - // TODO the case this is testing works for me in the demo webapp, - // but doing it programmatically fails. + /* + * TODO the case this is testing works for me in the demo webapp, + * but doing it programmatically fails. + */ buffer.lines.length = 3; buffer.lines.set(0, stringToRow('a ')); buffer.lines.set(1, stringArrayToRow(['😁', ' '])); diff --git a/src/browser/services/SelectionService.ts b/src/browser/services/SelectionService.ts index 39692a871..29ed1b827 100644 --- a/src/browser/services/SelectionService.ts +++ b/src/browser/services/SelectionService.ts @@ -152,8 +152,10 @@ export class SelectionService extends Disposable implements ISelectionService { this._removeMouseDownListeners(); })); - // Clear selection when resizing vertically. This experience could be improved, this is the - // simple option to fix the buggy behavior. https://github.com/xtermjs/xterm.js/issues/5300 + /* + * Clear selection when resizing vertically. This experience could be improved, this is the + * simple option to fix the buggy behavior. https://github.com/xtermjs/xterm.js/issues/5300 + */ this._register(this._bufferService.onResize(e => { if (e.rowsChanged) { this.clearSelection(); @@ -215,8 +217,10 @@ export class SelectionService extends Disposable implements ISelectionService { return ''; } - // For column selection it's not enough to rely on final selection's swapping of reversed - // values, it also needs the x coordinates to swap independently of the y coordinate is needed + /* + * For column selection it's not enough to rely on final selection's swapping of reversed + * values, it also needs the x coordinates to swap independently of the y coordinate is needed + */ const startCol = start[0] < end[0] ? start[0] : end[0]; const endCol = start[0] < end[0] ? end[0] : start[0]; for (let i = start[1]; i <= end[1]; i++) { @@ -251,8 +255,10 @@ export class SelectionService extends Disposable implements ISelectionService { } } - // Format string by replacing non-breaking space chars with regular spaces - // and joining the array into a multi-line string. + /* + * Format string by replacing non-breaking space chars with regular spaces + * and joining the array into a multi-line string. + */ const formattedResult = result.map(line => { return line.replace(ALL_NON_BREAKING_SPACE_REGEX, ' '); }).join(Browser.isWindows ? '\r\n' : '\n'); @@ -281,8 +287,10 @@ export class SelectionService extends Disposable implements ISelectionService { this._refreshAnimationFrame = this._coreBrowserService.window.requestAnimationFrame(() => this._refresh()); } - // If the platform is Linux and the refresh call comes from a mouse event, - // we need to update the selection for middle click to paste selection. + /* + * If the platform is Linux and the refresh call comes from a mouse event, + * we need to update the selection for middle click to paste selection. + */ if (Browser.isLinux && isLinuxMouseSelection) { const selectionText = this.selectionText; if (selectionText.length) { @@ -447,8 +455,10 @@ export class SelectionService extends Disposable implements ISelectionService { */ public handleMouseDown(event: MouseEvent): void { this._mouseDownTimeStamp = event.timeStamp; - // If we have selection, we want the context menu on right click even if the - // terminal is in mouse mode. + /* + * If we have selection, we want the context menu on right click even if the + * terminal is in mouse mode. + */ if (event.button === 2 && this.hasSelection) { return; } @@ -561,8 +571,10 @@ export class SelectionService extends Disposable implements ISelectionService { return; } - // If the mouse is over the second half of a wide character, adjust the - // selection to cover the whole character + /* + * If the mouse is over the second half of a wide character, adjust the + * selection to cover the whole character + */ if (line.hasWidth(this._model.selectionStart[0]) === 0) { this._model.selectionStart[0]++; } @@ -605,19 +617,25 @@ export class SelectionService extends Disposable implements ISelectionService { * @param event The mousemove event. */ private _handleMouseMove(event: MouseEvent): void { - // If the mousemove listener is active it means that a selection is - // currently being made, we should stop propagation to prevent mouse events - // to be sent to the pty. + /* + * If the mousemove listener is active it means that a selection is + * currently being made, we should stop propagation to prevent mouse events + * to be sent to the pty. + */ event.stopImmediatePropagation(); - // Do nothing if there is no selection start, this can happen if the first - // click in the terminal is an incremental click + /* + * Do nothing if there is no selection start, this can happen if the first + * click in the terminal is an incremental click + */ if (!this._model.selectionStart) { return; } - // Record the previous position so we know whether to redraw the selection - // at the end. + /* + * Record the previous position so we know whether to redraw the selection + * at the end. + */ const previousSelectionEnd = this._model.selectionEnd ? [this._model.selectionEnd[0], this._model.selectionEnd[1]] : null; // Set the initial selection end based on the mouse coordinates @@ -641,9 +659,11 @@ export class SelectionService extends Disposable implements ISelectionService { // Determine the amount of scrolling that will happen. this._dragScrollAmount = this._getMouseEventScrollAmount(event); - // If the cursor was above or below the viewport, make sure it's at the - // start or end of the viewport respectively. This should only happen when - // NOT in column select mode. + /* + * If the cursor was above or below the viewport, make sure it's at the + * start or end of the viewport respectively. This should only happen when + * NOT in column select mode. + */ if (this._activeSelectionMode !== SelectionMode.COLUMN) { if (this._dragScrollAmount > 0) { this._model.selectionEnd[0] = this._bufferService.cols; @@ -652,9 +672,11 @@ export class SelectionService extends Disposable implements ISelectionService { } } - // If the character is a wide character include the cell to the right in the - // selection. Note that selections at the very end of the line will never - // have a character. + /* + * If the character is a wide character include the cell to the right in the + * selection. Note that selections at the very end of the line will never + * have a character. + */ const buffer = this._bufferService.buffer; if (this._model.selectionEnd[1] < buffer.lines.length) { const line = buffer.lines.get(this._model.selectionEnd[1]); @@ -683,10 +705,12 @@ export class SelectionService extends Disposable implements ISelectionService { } if (this._dragScrollAmount) { this._onRequestScrollLines.fire({ amount: this._dragScrollAmount, suppressScrollEvent: false }); - // Re-evaluate selection - // If the cursor was above or below the viewport, make sure it's at the - // start or end of the viewport respectively. This should only happen when - // NOT in column select mode. + /* + * Re-evaluate selection + * If the cursor was above or below the viewport, make sure it's at the + * start or end of the viewport respectively. This should only happen when + * NOT in column select mode. + */ const buffer = this._bufferService.buffer; if (this._dragScrollAmount > 0) { if (this._activeSelectionMode !== SelectionMode.COLUMN) { @@ -765,10 +789,12 @@ export class SelectionService extends Disposable implements ISelectionService { private _handleBufferActivate(e: {activeBuffer: IBuffer, inactiveBuffer: IBuffer}): void { this.clearSelection(); - // Only adjust the selection on trim, shiftElements is rarely used (only in - // reverseIndex) and delete in a splice is only ever used when the same - // number of elements was just added. Given this is could actually be - // beneficial to leave the selection as is for these cases. + /* + * Only adjust the selection on trim, shiftElements is rarely used (only in + * reverseIndex) and delete in a splice is only ever used when the same + * number of elements was just added. Given this is could actually be + * beneficial to leave the selection as is for these cases. + */ this._trimListener.dispose(); this._trimListener = e.activeBuffer.lines.onTrim(amount => this._handleTrim(amount)); } @@ -784,13 +810,17 @@ export class SelectionService extends Disposable implements ISelectionService { for (let i = 0; x >= i; i++) { const length = bufferLine.loadCell(i, this._workCell).getChars().length; if (this._workCell.getWidth() === 0) { - // Wide characters aren't included in the line string so decrement the - // index so the index is back on the wide character. + /* + * Wide characters aren't included in the line string so decrement the + * index so the index is back on the wide character. + */ charIndex--; } else if (length > 1 && x !== i) { - // Emojis take up multiple characters, so adjust accordingly. For these - // we don't want ot include the character at the column as we're - // returning the start index in the string, not the end index. + /* + * Emojis take up multiple characters, so adjust accordingly. For these + * we don't want ot include the character at the column as we're + * returning the start index in the string, not the end index. + */ charIndex += length - 1; } } @@ -853,15 +883,19 @@ export class SelectionService extends Disposable implements ISelectionService { endIndex++; } } else { - // Expand until whitespace is hit. This algorithm works by scanning left - // and right from the starting position, keeping both the index format - // (line) and the column format (bufferLine) in sync. When a wide - // character is hit, it is recorded and the column index is adjusted. + /* + * Expand until whitespace is hit. This algorithm works by scanning left + * and right from the starting position, keeping both the index format + * (line) and the column format (bufferLine) in sync. When a wide + * character is hit, it is recorded and the column index is adjusted. + */ let startCol = coords[0]; let endCol = coords[0]; - // Consider the initial position, skip it and increment the wide char - // variable + /* + * Consider the initial position, skip it and increment the wide char + * variable + */ if (bufferLine.getWidth(startCol) === 0) { leftWideCharCount++; startCol--; @@ -887,8 +921,10 @@ export class SelectionService extends Disposable implements ISelectionService { leftWideCharCount++; startCol--; } else if (length > 1) { - // If the next character's string is longer than 1 char (eg. emoji), - // adjust the index + /* + * If the next character's string is longer than 1 char (eg. emoji), + * adjust the index + */ leftLongCharOffset += length - 1; startIndex -= length - 1; } @@ -903,8 +939,10 @@ export class SelectionService extends Disposable implements ISelectionService { rightWideCharCount++; endCol++; } else if (length > 1) { - // If the next character's string is longer than 1 char (eg. emoji), - // adjust the index + /* + * If the next character's string is longer than 1 char (eg. emoji), + * adjust the index + */ rightLongCharOffset += length - 1; endIndex += length - 1; } @@ -916,16 +954,20 @@ export class SelectionService extends Disposable implements ISelectionService { // Incremenet the end index so it is at the start of the next character endIndex++; - // Calculate the start _column_, converting the the string indexes back to - // column coordinates. + /* + * Calculate the start _column_, converting the the string indexes back to + * column coordinates. + */ let start = startIndex // The index of the selection's start char in the line string + charOffset // The difference between the initial char's column and index - leftWideCharCount // The number of wide chars left of the initial char + leftLongCharOffset; // The number of additional chars left of the initial char added by columns with strings longer than 1 (emojis) - // Calculate the length in _columns_, converting the the string indexes back - // to column coordinates. + /* + * Calculate the length in _columns_, converting the the string indexes back + * to column coordinates. + */ let length = Math.min(this._bufferService.cols, // Disallow lengths larger than the terminal cols endIndex // The index of the selection's end char in the line string - startIndex // The index of the selection's start char in the line string @@ -1002,8 +1044,10 @@ export class SelectionService extends Disposable implements ISelectionService { endRow--; } - // Adjust wrapped length value, this only needs to happen when values are reversed as in that - // case we're interested in the start of the word, not the end + /* + * Adjust wrapped length value, this only needs to happen when values are reversed as in that + * case we're interested in the start of the word, not the end + */ if (!this._model.areSelectionValuesReversed()) { while (wordPosition.start + wordPosition.length > this._bufferService.cols) { wordPosition.length -= this._bufferService.cols; @@ -1021,8 +1065,10 @@ export class SelectionService extends Disposable implements ISelectionService { * @param cell The cell to check. */ private _isCharWordSeparator(cell: ICellData): boolean { - // Zero width characters are never separators as they are always to the - // right of wide characters + /* + * Zero width characters are never separators as they are always to the + * right of wide characters + */ if (cell.getWidth() === 0) { return false; } diff --git a/src/common/CircularList.ts b/src/common/CircularList.ts index 3663bfc17..9843a470c 100644 --- a/src/common/CircularList.ts +++ b/src/common/CircularList.ts @@ -52,8 +52,10 @@ export class CircularList extends Disposable implements ICircularList { return; } - // Reconstruct array, starting at index 0. Only transfer values from the - // indexes 0 to length. + /* + * Reconstruct array, starting at index 0. Only transfer values from the + * indexes 0 to length. + */ const newArray = new Array(newMaxLength); for (let i = 0; i < Math.min(newMaxLength, this.length); i++) { newArray[i] = this._array[this._getCyclicIndex(i)]; diff --git a/src/common/Color.ts b/src/common/Color.ts index b3d13fc99..eef573796 100644 --- a/src/common/Color.ts +++ b/src/common/Color.ts @@ -27,9 +27,11 @@ export namespace channels { } export function toRgba(r: number, g: number, b: number, a: number = 0xFF): number { - // Note: The aggregated number is RGBA32 (BE), thus needs to be converted to ABGR32 - // on LE systems, before it can be used for direct 32-bit buffer writes. - // >>> 0 forces an unsigned int + /* + * Note: The aggregated number is RGBA32 (BE), thus needs to be converted to ABGR32 + * on LE systems, before it can be used for direct 32-bit buffer writes. + * >>> 0 forces an unsigned int + */ return (r << 24 | g << 16 | b << 8 | a) >>> 0; } @@ -197,8 +199,10 @@ export namespace css { throw new Error('css.toColor: Unsupported css format'); } - // Validate the color using canvas fillStyle - // See https://html.spec.whatwg.org/multipage/canvas.html#fill-and-stroke-styles + /* + * Validate the color using canvas fillStyle + * See https://html.spec.whatwg.org/multipage/canvas.html#fill-and-stroke-styles + */ $ctx.fillStyle = $litmusColor; $ctx.fillStyle = css; if (typeof $ctx.fillStyle !== 'string') { @@ -213,9 +217,11 @@ export namespace css { throw new Error('css.toColor: Unsupported css format'); } - // Extract the color from the canvas' fillStyle property which exposes the color value in rgba() - // format - // See https://html.spec.whatwg.org/multipage/canvas.html#serialisation-of-a-color + /* + * Extract the color from the canvas' fillStyle property which exposes the color value in rgba() + * format + * See https://html.spec.whatwg.org/multipage/canvas.html#serialisation-of-a-color + */ return { rgba: channels.toRgba($r, $g, $b, $a), css @@ -321,8 +327,10 @@ export namespace rgba { } export function reduceLuminance(bgRgba: number, fgRgba: number, ratio: number): number { - // This is a naive but fast approach to reducing luminance as converting to - // HSL and back is expensive + /* + * This is a naive but fast approach to reducing luminance as converting to + * HSL and back is expensive + */ const bgR = (bgRgba >> 24) & 0xFF; const bgG = (bgRgba >> 16) & 0xFF; const bgB = (bgRgba >> 8) & 0xFF; @@ -341,8 +349,10 @@ export namespace rgba { } export function increaseLuminance(bgRgba: number, fgRgba: number, ratio: number): number { - // This is a naive but fast approach to increasing luminance as converting to - // HSL and back is expensive + /* + * This is a naive but fast approach to increasing luminance as converting to + * HSL and back is expensive + */ const bgR = (bgRgba >> 24) & 0xFF; const bgG = (bgRgba >> 16) & 0xFF; const bgB = (bgRgba >> 8) & 0xFF; diff --git a/src/common/CoreTerminal.ts b/src/common/CoreTerminal.ts index 9bd7faaf5..3e16fe0a2 100644 --- a/src/common/CoreTerminal.ts +++ b/src/common/CoreTerminal.ts @@ -176,8 +176,10 @@ export abstract class CoreTerminal extends Disposable implements ICoreTerminal { x = Math.max(x, BufferServiceConstants.MINIMUM_COLS); y = Math.max(y, BufferServiceConstants.MINIMUM_ROWS); - // Flush pending writes before resize to avoid race conditions where async - // writes are processed with incorrect dimensions + /* + * Flush pending writes before resize to avoid race conditions where async + * writes are processed with incorrect dimensions + */ this._writeBuffer.flushSync(); this._bufferService.resize(x, y); diff --git a/src/common/InputHandler.test.ts b/src/common/InputHandler.test.ts index f9da81a25..cf41d5746 100644 --- a/src/common/InputHandler.test.ts +++ b/src/common/InputHandler.test.ts @@ -613,8 +613,10 @@ describe('InputHandler', () => { await inputHandler.parseP('a'.repeat(bufferService.cols + 9)); // line 1 and 2 for (let i = 3; i < bufferService.rows; ++i) await inputHandler.parseP('a'.repeat(bufferService.cols)); - // params[1] left and above with wrap - // confirm precondition that line 2 is wrapped + /* + * params[1] left and above with wrap + * confirm precondition that line 2 is wrapped + */ assert.equal(bufferService.buffer.lines.get(2)!.isWrapped, true); bufferService.buffer.y = 2; bufferService.buffer.x = 40; @@ -628,8 +630,10 @@ describe('InputHandler', () => { await inputHandler.parseP('a'.repeat(bufferService.cols + 9)); // line 1 and 2 for (let i = 3; i < bufferService.rows; ++i) await inputHandler.parseP('a'.repeat(bufferService.cols)); - // params[1] left and above with wrap - // confirm precondition that line 2 is wrapped + /* + * params[1] left and above with wrap + * confirm precondition that line 2 is wrapped + */ assert.equal(bufferService.buffer.lines.get(2)!.isWrapped, true); bufferService.buffer.y = 1; bufferService.buffer.x = 90; // Cursor is beyond last column diff --git a/src/common/InputHandler.ts b/src/common/InputHandler.ts index 2b7142dd0..1fac29d91 100644 --- a/src/common/InputHandler.ts +++ b/src/common/InputHandler.ts @@ -33,10 +33,12 @@ const GLEVEL: { [key: string]: number } = { '(': 0, ')': 1, '*': 2, '+': 3, '-': /** * Document xterm VT features here that are currently unsupported */ -// @vt: #N DCS DECUDK "User Defined Keys" "DCS Ps ; Ps \| Pt ST" "Definitions for user-defined keys." -// @vt: #N DCS XTGETTCAP "Request Terminfo String" "DCS + q Pt ST" "Request Terminfo String." -// @vt: #N DCS XTSETTCAP "Set Terminfo Data" "DCS + p Pt ST" "Set Terminfo Data." -// @vt: #N OSC 1 "Set Icon Name" "OSC 1 ; Pt BEL" "Set icon name." +/* + * @vt: #N DCS DECUDK "User Defined Keys" "DCS Ps ; Ps \| Pt ST" "Definitions for user-defined keys." + * @vt: #N DCS XTGETTCAP "Request Terminfo String" "DCS + q Pt ST" "Request Terminfo String." + * @vt: #N DCS XTSETTCAP "Set Terminfo Data" "DCS + p Pt ST" "Set Terminfo Data." + * @vt: #N OSC 1 "Set Icon Name" "OSC 1 ; Pt BEL" "Set icon name." + */ /** * Max length of the UTF32 input buffer. Real memory consumption is 4 times higher. @@ -288,13 +290,17 @@ export class InputHandler extends Disposable implements IInputHandler { this._parser.registerOscHandler(1, new OscHandler(data => this.setIconName(data))); // 2 - title this._parser.registerOscHandler(2, new OscHandler(data => this.setTitle(data))); - // 3 - set property X in the form "prop=value" - // 4 - Change Color Number + /* + * 3 - set property X in the form "prop=value" + * 4 - Change Color Number + */ this._parser.registerOscHandler(4, new OscHandler(data => this.setOrReportIndexedColor(data))); - // 5 - Change Special Color Number - // 6 - Enable/disable Special Color Number c - // 7 - current directory? (not in xterm spec, see https://gitlab.com/gnachman/iterm2/issues/3939) - // 8 - create hyperlink (not in xterm spec, see https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda) + /* + * 5 - Change Special Color Number + * 6 - Enable/disable Special Color Number c + * 7 - current directory? (not in xterm spec, see https://gitlab.com/gnachman/iterm2/issues/3939) + * 8 - create hyperlink (not in xterm spec, see https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda) + */ this._parser.registerOscHandler(8, new OscHandler(data => this.setHyperlink(data))); // 10 - Change VT100 text foreground color to Pt. this._parser.registerOscHandler(10, new OscHandler(data => this.setOrReportFgColor(data))); @@ -302,34 +308,40 @@ export class InputHandler extends Disposable implements IInputHandler { this._parser.registerOscHandler(11, new OscHandler(data => this.setOrReportBgColor(data))); // 12 - Change text cursor color to Pt. this._parser.registerOscHandler(12, new OscHandler(data => this.setOrReportCursorColor(data))); - // 13 - Change mouse foreground color to Pt. - // 14 - Change mouse background color to Pt. - // 15 - Change Tektronix foreground color to Pt. - // 16 - Change Tektronix background color to Pt. - // 17 - Change highlight background color to Pt. - // 18 - Change Tektronix cursor color to Pt. - // 19 - Change highlight foreground color to Pt. - // 46 - Change Log File to Pt. - // 50 - Set Font to Pt. - // 51 - reserved for Emacs shell. - // 52 - Manipulate Selection Data. - // 104 ; c - Reset Color Number c. + /* + * 13 - Change mouse foreground color to Pt. + * 14 - Change mouse background color to Pt. + * 15 - Change Tektronix foreground color to Pt. + * 16 - Change Tektronix background color to Pt. + * 17 - Change highlight background color to Pt. + * 18 - Change Tektronix cursor color to Pt. + * 19 - Change highlight foreground color to Pt. + * 46 - Change Log File to Pt. + * 50 - Set Font to Pt. + * 51 - reserved for Emacs shell. + * 52 - Manipulate Selection Data. + * 104 ; c - Reset Color Number c. + */ this._parser.registerOscHandler(104, new OscHandler(data => this.restoreIndexedColor(data))); - // 105 ; c - Reset Special Color Number c. - // 106 ; c; f - Enable/disable Special Color Number c. - // 110 - Reset VT100 text foreground color. + /* + * 105 ; c - Reset Special Color Number c. + * 106 ; c; f - Enable/disable Special Color Number c. + * 110 - Reset VT100 text foreground color. + */ this._parser.registerOscHandler(110, new OscHandler(data => this.restoreFgColor(data))); // 111 - Reset VT100 text background color. this._parser.registerOscHandler(111, new OscHandler(data => this.restoreBgColor(data))); // 112 - Reset text cursor color. this._parser.registerOscHandler(112, new OscHandler(data => this.restoreCursorColor(data))); - // 113 - Reset mouse foreground color. - // 114 - Reset mouse background color. - // 115 - Reset Tektronix foreground color. - // 116 - Reset Tektronix background color. - // 117 - Reset highlight color. - // 118 - Reset Tektronix cursor color. - // 119 - Reset highlight foreground color. + /* + * 113 - Reset mouse foreground color. + * 114 - Reset mouse background color. + * 115 - Reset Tektronix foreground color. + * 116 - Reset Tektronix background color. + * 117 - Reset highlight color. + * 118 - Reset Tektronix cursor color. + * 119 - Reset highlight foreground color. + */ /** * ESC handlers @@ -466,8 +478,10 @@ export class InputHandler extends Disposable implements IInputHandler { } } - // Clear the dirty row service so we know which lines changed as a result of parsing - // Important: do not clear between async calls, otherwise we lost pending update information. + /* + * Clear the dirty row service so we know which lines changed as a result of parsing + * Important: do not clear between async calls, otherwise we lost pending update information. + */ if (!wasPaused) { this._dirtyRowTracker.clearRange(); } @@ -502,8 +516,10 @@ export class InputHandler extends Disposable implements IInputHandler { this._onCursorMove.fire(); } - // Refresh any dirty rows accumulated as part of parsing, fire only for rows within the - // _viewport_ which is relative to ydisp, not relative to ybase. + /* + * Refresh any dirty rows accumulated as part of parsing, fire only for rows within the + * _viewport_ which is relative to ydisp, not relative to ybase. + */ const viewportEnd = this._dirtyRowTracker.end + (this._bufferService.buffer.ybase - this._bufferService.buffer.ydisp); const viewportStart = this._dirtyRowTracker.start + (this._bufferService.buffer.ybase - this._bufferService.buffer.ydisp); if (viewportStart < this._bufferService.rows) { @@ -525,8 +541,10 @@ export class InputHandler extends Disposable implements IInputHandler { const curAttr = this._curAttrData; let bufferRow = this._activeBuffer.lines.get(this._activeBuffer.ybase + this._activeBuffer.y); - // Defensive check: bufferRow can be undefined if a resize occurred mid-write due to async - // scheduling gaps in WriteBuffer. See https://github.com/xtermjs/xterm.js/issues/5597 + /* + * Defensive check: bufferRow can be undefined if a resize occurred mid-write due to async + * scheduling gaps in WriteBuffer. See https://github.com/xtermjs/xterm.js/issues/5597 + */ if (!bufferRow) { return; } @@ -542,15 +560,19 @@ export class InputHandler extends Disposable implements IInputHandler { for (let pos = start; pos < end; ++pos) { code = data[pos]; - // Soft hyphen's (U+00AD) behavior is ambiguous and differs across terminals. We opt to treat - // it as a zero-width hint to text layout engines and simply ignore it. + /* + * Soft hyphen's (U+00AD) behavior is ambiguous and differs across terminals. We opt to treat + * it as a zero-width hint to text layout engines and simply ignore it. + */ if (code === 0xAD) { continue; } - // get charset replacement character - // charset is only defined for ASCII, therefore we only - // search for an replacement char if code < 127 + /* + * get charset replacement character + * charset is only defined for ASCII, therefore we only + * search for an replacement char if code < 127 + */ if (code < 127 && charset) { const ch = charset[String.fromCharCode(code)]; if (ch) { @@ -571,12 +593,16 @@ export class InputHandler extends Disposable implements IInputHandler { this._oscLinkService.addLineToLink(this._getCurrentLinkId(), this._activeBuffer.ybase + this._activeBuffer.y); } - // goto next line if ch would overflow - // NOTE: To avoid costly width checks here, - // the terminal does not allow a cols < 2. + /* + * goto next line if ch would overflow + * NOTE: To avoid costly width checks here, + * the terminal does not allow a cols < 2. + */ if (this._activeBuffer.x + chWidth - oldWidth > cols) { - // autowrap - DECAWM - // automatically wraps to the beginning of the next line + /* + * autowrap - DECAWM + * automatically wraps to the beginning of the next line + */ if (wraparoundMode) { const oldRow = bufferRow; let oldCol = this._activeBuffer.x - oldWidth; @@ -589,8 +615,10 @@ export class InputHandler extends Disposable implements IInputHandler { if (this._activeBuffer.y >= this._bufferService.rows) { this._activeBuffer.y = this._bufferService.rows - 1; } - // The line already exists (eg. the initial viewport), mark it as a - // wrapped line + /* + * The line already exists (eg. the initial viewport), mark it as a + * wrapped line + */ this._activeBuffer.lines.get(this._activeBuffer.ybase + this._activeBuffer.y)!.isWrapped = true; } // row changed, get it again @@ -599,8 +627,10 @@ export class InputHandler extends Disposable implements IInputHandler { return; } if (oldWidth > 0 && bufferRow instanceof BufferLine) { - // Combining character widens 1 column to 2. - // Move old character to next line. + /* + * Combining character widens 1 column to 2. + * Move old character to next line. + */ bufferRow.copyCellsFrom(oldRow as BufferLine, oldCol, 0, oldWidth, false); } @@ -611,22 +641,28 @@ export class InputHandler extends Disposable implements IInputHandler { } else { this._activeBuffer.x = cols - 1; if (chWidth === 2) { - // FIXME: check for xterm behavior - // What to do here? We got a wide char that does not fit into last cell + /* + * FIXME: check for xterm behavior + * What to do here? We got a wide char that does not fit into last cell + */ continue; } } } - // insert combining char at last cursor position - // this._activeBuffer.x should never be 0 for a combining char - // since they always follow a cell consuming char - // therefore we can test for this._activeBuffer.x to avoid overflow left + /* + * insert combining char at last cursor position + * this._activeBuffer.x should never be 0 for a combining char + * since they always follow a cell consuming char + * therefore we can test for this._activeBuffer.x to avoid overflow left + */ if (shouldJoin && this._activeBuffer.x) { const offset = bufferRow.getWidth(this._activeBuffer.x - 1) ? 1 : 2; - // if empty cell after fullwidth, need to go 2 cells back - // it is save to step 2 cells back here - // since an empty cell is only set by fullwidth chars + /* + * if empty cell after fullwidth, need to go 2 cells back + * it is save to step 2 cells back here + * since an empty cell is only set by fullwidth chars + */ bufferRow.addCodepointToCell(this._activeBuffer.x - offset, code, chWidth); for (let delta = chWidth - oldWidth; --delta >= 0;) { @@ -639,9 +675,11 @@ export class InputHandler extends Disposable implements IInputHandler { if (insertMode) { // right shift cells according to the width bufferRow.insertCells(this._activeBuffer.x, chWidth - oldWidth, this._activeBuffer.getNullCell(curAttr)); - // test last cell - since the last cell has only room for - // a halfwidth char any fullwidth shifted there is lost - // and will be set to empty cell + /* + * test last cell - since the last cell has only room for + * a halfwidth char any fullwidth shifted there is lost + * and will be set to empty cell + */ if (bufferRow.getWidth(cols - 1) === 2) { bufferRow.setCellFromCodepoint(cols - 1, NULL_CELL_CODE, NULL_CELL_WIDTH, curAttr); } @@ -650,9 +688,11 @@ export class InputHandler extends Disposable implements IInputHandler { // write current char to buffer and advance cursor bufferRow.setCellFromCodepoint(this._activeBuffer.x++, code, chWidth, curAttr); - // fullwidth char - also set next cell to placeholder stub and advance cursor - // for graphemes bigger than fullwidth we can simply loop to zero - // we already made sure above, that this._activeBuffer.x + chWidth will not overflow right + /* + * fullwidth char - also set next cell to placeholder stub and advance cursor + * for graphemes bigger than fullwidth we can simply loop to zero + * we already made sure above, that this._activeBuffer.x + chWidth will not overflow right + */ if (chWidth > 0) { while (--chWidth) { // other than a regular empty cell a cell following a wide char has no width @@ -750,11 +790,13 @@ export class InputHandler extends Disposable implements IInputHandler { } else if (this._activeBuffer.y >= this._bufferService.rows) { this._activeBuffer.y = this._bufferService.rows - 1; } else { - // There was an explicit line feed (not just a carriage return), so clear the wrapped state of - // the line. This is particularly important on conpty/Windows where revisiting lines to - // reprint is common, especially on resize. Note that the windowsMode wrapped line heuristics - // can mess with this so windowsMode should be disabled, which is recommended on Windows build - // 21376 and above. + /* + * There was an explicit line feed (not just a carriage return), so clear the wrapped state of + * the line. This is particularly important on conpty/Windows where revisiting lines to + * reprint is common, especially on resize. Note that the windowsMode wrapped line heuristics + * can mess with this so windowsMode should be disabled, which is recommended on Windows build + * 21376 and above. + */ this._activeBuffer.lines.get(this._activeBuffer.ybase + this._activeBuffer.y)!.isWrapped = false; } // If the end of the line is hit, prevent this action from wrapping around to the next line. @@ -799,9 +841,11 @@ export class InputHandler extends Disposable implements IInputHandler { return true; } - // reverse wrap-around is enabled - // other than for normal operation mode, reverse wrap-around allows the cursor - // to be at x=cols to be able to address the last cell of a row by BS + /* + * reverse wrap-around is enabled + * other than for normal operation mode, reverse wrap-around allows the cursor + * to be at x=cols to be able to address the last cell of a row by BS + */ this._restrictCursor(this._bufferService.cols); if (this._activeBuffer.x > 0) { @@ -822,17 +866,21 @@ export class InputHandler extends Disposable implements IInputHandler { this._activeBuffer.lines.get(this._activeBuffer.ybase + this._activeBuffer.y)!.isWrapped = false; this._activeBuffer.y--; this._activeBuffer.x = this._bufferService.cols - 1; - // find last taken cell - last cell can have 3 different states: - // - hasContent(true) + hasWidth(1): narrow char - we are done - // - hasWidth(0): second part of wide char - we are done - // - hasContent(false) + hasWidth(1): empty cell due to early wrapping wide char, go one - // cell further back + /* + * find last taken cell - last cell can have 3 different states: + * - hasContent(true) + hasWidth(1): narrow char - we are done + * - hasWidth(0): second part of wide char - we are done + * - hasContent(false) + hasWidth(1): empty cell due to early wrapping wide char, go one + * cell further back + */ const line = this._activeBuffer.lines.get(this._activeBuffer.ybase + this._activeBuffer.y)!; if (line.hasWidth(this._activeBuffer.x) && !line.hasContent(this._activeBuffer.x)) { this._activeBuffer.x--; - // We do this only once, since width=1 + hasContent=false currently happens only once - // before early wrapping of a wide char. - // This needs to be fixed once we support graphemes taking more than 2 cells. + /* + * We do this only once, since width=1 + hasContent=false currently happens only once + * before early wrapping of a wide char. + * This needs to be fixed once we support graphemes taking more than 2 cells. + */ } } } @@ -913,8 +961,10 @@ export class InputHandler extends Disposable implements IInputHandler { * Set relative cursor position. */ private _moveCursor(x: number, y: number): void { - // for relative changes we have to make sure we are within 0 .. cols/rows - 1 - // before calculating the new position + /* + * for relative changes we have to make sure we are within 0 .. cols/rows - 1 + * before calculating the new position + */ this._restrictCursor(); this._setCursor(this._activeBuffer.x + x, this._activeBuffer.y + y); } @@ -1354,8 +1404,10 @@ export class InputHandler extends Disposable implements IInputHandler { const scrollBottomRowsOffset = this._bufferService.rows - 1 - this._activeBuffer.scrollBottom; const scrollBottomAbsolute = this._bufferService.rows - 1 + this._activeBuffer.ybase - scrollBottomRowsOffset + 1; while (param--) { - // test: echo -e '\e[44m\e[1L\e[0m' - // blankLine(true) - xterm/linux behavior + /* + * test: echo -e '\e[44m\e[1L\e[0m' + * blankLine(true) - xterm/linux behavior + */ this._activeBuffer.lines.splice(scrollBottomAbsolute - 1, 1); this._activeBuffer.lines.splice(row, 0, this._activeBuffer.getBlankLine(this._eraseAttrData())); } @@ -1388,8 +1440,10 @@ export class InputHandler extends Disposable implements IInputHandler { j = this._bufferService.rows - 1 - this._activeBuffer.scrollBottom; j = this._bufferService.rows - 1 + this._activeBuffer.ybase - j; while (param--) { - // test: echo -e '\e[44m\e[1M\e[0m' - // blankLine(true) - xterm/linux behavior + /* + * test: echo -e '\e[44m\e[1M\e[0m' + * blankLine(true) - xterm/linux behavior + */ this._activeBuffer.lines.splice(row, 1); this._activeBuffer.lines.splice(j, 0, this._activeBuffer.getBlankLine(this._eraseAttrData())); } @@ -1737,16 +1791,20 @@ export class InputHandler extends Disposable implements IInputHandler { if (params.params[0] > 0) { return true; } - // xterm and urxvt - // seem to spit this - // out around ~370 times (?). + /* + * xterm and urxvt + * seem to spit this + * out around ~370 times (?). + */ if (this._is('xterm')) { this._coreService.triggerDataEvent(C0.ESC + '[>0;276;0c'); } else if (this._is('rxvt-unicode')) { this._coreService.triggerDataEvent(C0.ESC + '[>85;95;0c'); } else if (this._is('linux')) { - // not supported by linux console. - // linux console echoes parameters. + /* + * not supported by linux console. + * linux console echoes parameters. + */ this._coreService.triggerDataEvent(params.params[0] + 'c'); } else if (this._is('screen')) { this._coreService.triggerDataEvent(C0.ESC + '[>83;40003;0c'); @@ -1979,13 +2037,17 @@ export class InputHandler extends Disposable implements IInputHandler { this._mouseStateService.activeProtocol = 'DRAG'; break; case 1003: // any event mouse - // any event - sends motion events, - // even if there is no button held down. + /* + * any event - sends motion events, + * even if there is no button held down. + */ this._mouseStateService.activeProtocol = 'ANY'; break; case 1004: // send focusin/focusout events - // focusin: ^[[I - // focusout: ^[[O + /* + * focusin: ^[[I + * focusout: ^[[O + */ this._coreService.decPrivateModes.sendFocus = true; this._onRequestSendFocus.fire(); break; @@ -2408,10 +2470,12 @@ export class InputHandler extends Disposable implements IInputHandler { * Returns advance for params index. */ private _extractColor(params: IParams, pos: number, attr: IAttributeData): number { - // normalize params - // meaning: [target, CM, ign, val, val, val] - // RGB : [ 38/48, 2, ign, r, g, b] - // P256 : [ 38/48, 5, ign, v, ign, ign] + /* + * normalize params + * meaning: [target, CM, ign, val, val, val] + * RGB : [ 38/48, 2, ign, r, g, b] + * P256 : [ 38/48, 5, ign, v, ign, ign] + */ const accu = [0, 0, -1, 0, 0, 0]; // alignment placeholder for non color space sequences @@ -2477,8 +2541,10 @@ export class InputHandler extends Disposable implements IInputHandler { * 4:5 - dashed underline */ private _processUnderline(style: number, attr: IAttributeData): void { - // treat extended attrs as immutable, thus always clone from old one - // this is needed since the buffer only holds references to it + /* + * treat extended attrs as immutable, thus always clone from old one + * this is needed since the buffer only holds references to it + */ attr.extended = attr.extended.clone(); // default to 1 == single underline @@ -2501,8 +2567,10 @@ export class InputHandler extends Disposable implements IInputHandler { attr.fg = DEFAULT_ATTR_DATA.fg; attr.bg = DEFAULT_ATTR_DATA.bg; attr.extended = attr.extended.clone(); - // Reset underline style and color. Note that we don't want to reset other - // fields such as the url id. + /* + * Reset underline style and color. Note that we don't want to reset other + * fields such as the url id. + */ attr.extended.underlineStyle = UnderlineStyle.NONE; attr.extended.underlineColor &= ~(Attributes.CM_MASK | Attributes.RGB_MASK); attr.updateExtended(); @@ -2641,8 +2709,10 @@ export class InputHandler extends Disposable implements IInputHandler { // blink attr.fg |= FgFlags.BLINK; } else if (p === 7) { - // inverse and positive - // test with: echo -e '\e[31m\e[42mhello\e[7mworld\e[27mhi\e[m' + /* + * inverse and positive + * test with: echo -e '\e[31m\e[42mhello\e[7mworld\e[27mhi\e[m' + */ attr.fg |= FgFlags.INVERSE; } else if (p === 8) { // invisible @@ -2756,8 +2826,10 @@ export class InputHandler extends Disposable implements IInputHandler { // @vt: #P[Only CPR is supported.] CSI DECDSR "DEC Device Status Report" "CSI ? Ps n" "Only CPR is supported (same as DSR)." public deviceStatusPrivate(params: IParams): boolean { - // modern xterm doesnt seem to - // respond to any of these except ?6, 6, and 5 + /* + * modern xterm doesnt seem to + * respond to any of these except ?6, 6, and 5 + */ switch (params.params[0]) { case 6: // cursor position @@ -2766,20 +2838,28 @@ export class InputHandler extends Disposable implements IInputHandler { this._coreService.triggerDataEvent(`${C0.ESC}[?${y};${x}R`); break; case 15: - // no printer - // this.handler(C0.ESC + '[?11n'); + /* + * no printer + * this.handler(C0.ESC + '[?11n'); + */ break; case 25: - // dont support user defined keys - // this.handler(C0.ESC + '[?21n'); + /* + * dont support user defined keys + * this.handler(C0.ESC + '[?21n'); + */ break; case 26: - // north american keyboard - // this.handler(C0.ESC + '[?27;1;0;0n'); + /* + * north american keyboard + * this.handler(C0.ESC + '[?27;1;0;0n'); + */ break; case 53: - // no dec locator/mouse - // this.handler(C0.ESC + '[?50n'); + /* + * no dec locator/mouse + * this.handler(C0.ESC + '[?50n'); + */ break; case 996: // color scheme query (https://contour-terminal.org/vt-extensions/color-palette-update-notifications/) @@ -3401,9 +3481,11 @@ export class InputHandler extends Disposable implements IInputHandler { public reverseIndex(): boolean { this._restrictCursor(); if (this._activeBuffer.y === this._activeBuffer.scrollTop) { - // possibly move the code below to term.reverseScroll(); - // test: echo -ne '\e[1;1H\e[44m\eM\e[0m' - // blankLine(true) is xterm/linux behavior + /* + * possibly move the code below to term.reverseScroll(); + * test: echo -ne '\e[1;1H\e[44m\eM\e[0m' + * blankLine(true) is xterm/linux behavior + */ const scrollRegionHeight = this._activeBuffer.scrollBottom - this._activeBuffer.scrollTop; this._activeBuffer.lines.shiftElements(this._activeBuffer.ybase + this._activeBuffer.y, scrollRegionHeight, 1); this._activeBuffer.lines.set(this._activeBuffer.ybase + this._activeBuffer.y, this._activeBuffer.getBlankLine(this._eraseAttrData())); diff --git a/src/common/Platform.ts b/src/common/Platform.ts index 09ba097e6..ad2d1424c 100644 --- a/src/common/Platform.ts +++ b/src/common/Platform.ts @@ -9,14 +9,18 @@ interface INavigator { platform: string; } -// We're declaring a navigator global here as we expect it in all runtimes (node and browser), but -// we want this module to live in common. +/* + * We're declaring a navigator global here as we expect it in all runtimes (node and browser), but + * we want this module to live in common. + */ declare const navigator: INavigator; declare const process: unknown; -// navigator.userAgent is also checked here because bundling with the process module can cause -// issues otherwise. Note that navigator exists in Node.js 21+ but the userAgent is -// "Node.js/". +/* + * navigator.userAgent is also checked here because bundling with the process module can cause + * issues otherwise. Note that navigator exists in Node.js 21+ but the userAgent is + * "Node.js/". + */ export const isNode = (typeof process !== 'undefined' && 'title' in (process as any) && (typeof navigator === 'undefined' || navigator.userAgent.startsWith('Node.js/'))) ? true : false; const userAgent = (isNode) ? 'node' : navigator.userAgent; const platform = (isNode) ? 'node' : navigator.platform; @@ -44,9 +48,11 @@ export function getSafariVersion(): number { return parseInt(majorVersion[1]); } -// Find the users platform. We use this to interpret the meta key -// and ISO third level shifts. -// http://stackoverflow.com/q/19877924/577598 +/* + * Find the users platform. We use this to interpret the meta key + * and ISO third level shifts. + * http://stackoverflow.com/q/19877924/577598 + */ export const isMac = ['Macintosh', 'MacIntel', 'MacPPC', 'Mac68K'].includes(platform); export const isWindows = ['Windows', 'Win16', 'Win32', 'WinCE'].includes(platform); export const isLinux = platform.indexOf('Linux') >= 0; diff --git a/src/common/SortedList.ts b/src/common/SortedList.ts index c6dc62085..8ae39d3d8 100644 --- a/src/common/SortedList.ts +++ b/src/common/SortedList.ts @@ -191,8 +191,10 @@ export class SortedList { return mid; } } - // key not in list - // still return closest min (also used as insert position) + /* + * key not in list + * still return closest min (also used as insert position) + */ return min; } } diff --git a/src/common/TaskQueue.ts b/src/common/TaskQueue.ts index 3cad0a140..ae9041d3a 100644 --- a/src/common/TaskQueue.ts +++ b/src/common/TaskQueue.ts @@ -83,17 +83,23 @@ abstract class TaskQueue implements ITaskQueue { if (!this._tasks[this._i]()) { this._i++; } - // other than performance.now, performance.now might not be stable (changes on wall clock - // changes), this is not an issue here as a clock change during a short running task is very - // unlikely in case it still happened and leads to negative duration, simply assume 1 msec + /* + * other than performance.now, performance.now might not be stable (changes on wall clock + * changes), this is not an issue here as a clock change during a short running task is very + * unlikely in case it still happened and leads to negative duration, simply assume 1 msec + */ taskDuration = Math.max(1, performance.now() - taskDuration); longestTask = Math.max(taskDuration, longestTask); - // Guess the following task will take a similar time to the longest task in this batch, allow - // additional room to try avoid exceeding the deadline + /* + * Guess the following task will take a similar time to the longest task in this batch, allow + * additional room to try avoid exceeding the deadline + */ deadlineRemaining = deadline.timeRemaining(); if (longestTask * 1.5 > deadlineRemaining) { - // Warn when the time exceeding the deadline is over 20ms, if this happens in practice the - // task should be split into sub-tasks to ensure the UI remains responsive. + /* + * Warn when the time exceeding the deadline is over 20ms, if this happens in practice the + * task should be split into sub-tasks to ensure the UI remains responsive. + */ if (lastDeadlineRemaining - taskDuration < -20) { this._logService.warn(`task queue exceeded allotted deadline by ${Math.abs(Math.round(lastDeadlineRemaining - taskDuration))}ms`); } diff --git a/src/common/Types.ts b/src/common/Types.ts index 5edf63637..fb95f5fe7 100644 --- a/src/common/Types.ts +++ b/src/common/Types.ts @@ -310,8 +310,10 @@ export const enum CoreMouseButton { RIGHT = 2, NONE = 3, WHEEL = 4, - // additional buttons 1..8 - // untested! + /* + * additional buttons 1..8 + * untested! + */ AUX1 = 8, AUX2 = 9, AUX3 = 10, @@ -528,8 +530,10 @@ export interface IInputHandler { /** CSI ' } */ insertColumns(params: IParams): boolean; /** CSI ' ~ */ deleteColumns(params: IParams): boolean; - /** OSC 0 - OSC 2 */ setTitle(data: string): boolean; + /** + * OSC 0 + * OSC 2 + */ setTitle(data: string): boolean; /** OSC 4 */ setOrReportIndexedColor(data: string): boolean; /** OSC 10 */ setOrReportFgColor(data: string): boolean; /** OSC 11 */ setOrReportBgColor(data: string): boolean; @@ -542,24 +546,30 @@ export interface IInputHandler { /** ESC E */ nextLine(): boolean; /** ESC = */ keypadApplicationMode(): boolean; /** ESC > */ keypadNumericMode(): boolean; - /** ESC % G - ESC % @ */ selectDefaultCharset(): boolean; - /** ESC ( C - ESC ) C - ESC * C - ESC + C - ESC - C - ESC . C - ESC / C */ selectCharset(collectAndFlag: string): boolean; + /** + * ESC % G + * ESC % @ + */ selectDefaultCharset(): boolean; + /** + * ESC ( C + * ESC ) C + * ESC * C + * ESC + C + * ESC - C + * ESC . C + * ESC / C + */ selectCharset(collectAndFlag: string): boolean; /** ESC D */ index(): boolean; /** ESC H */ tabSet(): boolean; /** ESC M */ reverseIndex(): boolean; /** ESC c */ fullReset(): boolean; - /** ESC n - ESC o - ESC | - ESC } - ESC ~ */ setgLevel(level: number): boolean; + /** + * ESC n + * ESC o + * ESC | + * ESC } + * ESC ~ + */ setgLevel(level: number): boolean; /** ESC # 8 */ screenAlignmentPattern(): boolean; } diff --git a/src/common/WindowsMode.ts b/src/common/WindowsMode.ts index 7cff094b2..f59343286 100644 --- a/src/common/WindowsMode.ts +++ b/src/common/WindowsMode.ts @@ -7,16 +7,18 @@ import { CHAR_DATA_CODE_INDEX, NULL_CELL_CODE, WHITESPACE_CELL_CODE } from 'comm import { IBufferService } from 'common/services/Services'; export function updateWindowsModeWrappedState(bufferService: IBufferService): void { - // Winpty does not support wraparound mode which means that lines will never - // be marked as wrapped. This causes issues for things like copying a line - // retaining the wrapped new line characters or if consumers are listening - // in on the data stream. - // - // The workaround for this is to listen to every incoming line feed and mark - // the line as wrapped if the last character in the previous line is not a - // space. This is certainly not without its problems, but generally on - // Windows when text reaches the end of the terminal it's likely going to be - // wrapped. + /* + * Winpty does not support wraparound mode which means that lines will never + * be marked as wrapped. This causes issues for things like copying a line + * retaining the wrapped new line characters or if consumers are listening + * in on the data stream. + * + * The workaround for this is to listen to every incoming line feed and mark + * the line as wrapped if the last character in the previous line is not a + * space. This is certainly not without its problems, but generally on + * Windows when text reaches the end of the terminal it's likely going to be + * wrapped. + */ const line = bufferService.buffer.lines.get(bufferService.buffer.ybase + bufferService.buffer.y - 1); const lastChar = line?.get(bufferService.cols - 1); diff --git a/src/common/buffer/Buffer.test.ts b/src/common/buffer/Buffer.test.ts index abfea7b06..f26807a94 100644 --- a/src/common/buffer/Buffer.test.ts +++ b/src/common/buffer/Buffer.test.ts @@ -348,8 +348,10 @@ describe('Buffer', () => { assert.equal(buffer.lines.get(4)!.translateToString(), ' '); }); it('should remove the correct amount of rows when reflowing larger', () => { - // This is a regression test to ensure that successive wrapped lines that are getting - // 3+ lines removed on a reflow actually remove the right lines + /* + * This is a regression test to ensure that successive wrapped lines that are getting + * 3+ lines removed on a reflow actually remove the right lines + */ buffer.fillViewportRows(); buffer.resize(10, 10); buffer.y = 2; @@ -429,10 +431,12 @@ describe('Buffer', () => { buffer.lines.get(2)!.set(i, [0, char, 1, code]); } buffer.y = 3; - // Buffer: - // abcdefghij - // 0123456789 - // abcdefghij + /* + * Buffer: + * abcdefghij + * 0123456789 + * abcdefghij + */ const firstMarker = buffer.addMarker(0); const secondMarker = buffer.addMarker(1); const thirdMarker = buffer.addMarker(2); @@ -492,10 +496,12 @@ describe('Buffer', () => { buffer.lines.get(2)!.set(i, [0, char, 1, code]); } buffer.y = 10; - // Buffer: - // abcdefghij - // 0123456789 - // abcdefghij + /* + * Buffer: + * abcdefghij + * 0123456789 + * abcdefghij + */ const firstMarker = buffer.addMarker(0); const secondMarker = buffer.addMarker(1); const thirdMarker = buffer.addMarker(2); @@ -539,9 +545,11 @@ describe('Buffer', () => { buffer.lines.get(1)!.set(0, [0, 'c', 1, 'c'.charCodeAt(0)]); buffer.lines.get(1)!.set(1, [0, 'd', 1, 'd'.charCodeAt(0)]); buffer.lines.get(1)!.isWrapped = true; - // Buffer: - // "ab " (wrapped) - // "cd" + /* + * Buffer: + * "ab " (wrapped) + * "cd" + */ buffer.resize(5, 10); assert.equal(buffer.ybase, 0); assert.equal(buffer.lines.length, 10); @@ -570,9 +578,11 @@ describe('Buffer', () => { buffer.lines.get(1)!.set(i, [0, '', 0, 0]); } buffer.lines.get(1)!.isWrapped = true; - // Buffer: - // 汉语汉语汉语 (wrapped) - // 汉语汉语汉语 + /* + * Buffer: + * 汉语汉语汉语 (wrapped) + * 汉语汉语汉语 + */ assert.equal(buffer.lines.get(0)!.translateToString(true), '汉语汉语汉语'); assert.equal(buffer.lines.get(1)!.translateToString(true), '汉语汉语汉语'); buffer.resize(13, 10); @@ -597,9 +607,11 @@ describe('Buffer', () => { buffer.lines.get(1)!.set(0, [0, 'c', 1, 'c'.charCodeAt(0)]); buffer.lines.get(1)!.set(1, [0, 'd', 1, 'd'.charCodeAt(0)]); buffer.lines.get(1)!.isWrapped = true; - // Buffer: - // "ab " (wrapped) - // "cd" + /* + * Buffer: + * "ab " (wrapped) + * "cd" + */ buffer.resize(3, 10); assert.equal(buffer.y, 2); assert.equal(buffer.ybase, 0); @@ -631,9 +643,11 @@ describe('Buffer', () => { buffer.lines.get(1)!.set(i, [0, '', 0, 0]); } buffer.lines.get(1)!.isWrapped = true; - // Buffer: - // 汉语汉语汉语 (wrapped) - // 汉语汉语汉语 + /* + * Buffer: + * 汉语汉语汉语 (wrapped) + * 汉语汉语汉语 + */ assert.equal(buffer.lines.get(0)!.translateToString(true), '汉语汉语汉语'); assert.equal(buffer.lines.get(1)!.translateToString(true), '汉语汉语汉语'); buffer.resize(11, 10); @@ -668,17 +682,19 @@ describe('Buffer', () => { describe('reflowLarger cases', () => { beforeEach(() => { - // Setup buffer state: - // 'ab' - // 'cd' (wrapped) - // 'ef' - // 'gh' (wrapped) - // 'ij' - // 'kl' (wrapped) - // ' ' - // ' ' - // ' ' - // ' ' + /* + * Setup buffer state: + * 'ab' + * 'cd' (wrapped) + * 'ef' + * 'gh' (wrapped) + * 'ij' + * 'kl' (wrapped) + * ' ' + * ' ' + * ' ' + * ' ' + */ buffer.fillViewportRows(); buffer.resize(2, 10); buffer.lines.get(0)!.set(0, [0, 'a', 1, 'a'.charCodeAt(0)]); @@ -859,17 +875,19 @@ describe('Buffer', () => { }); describe('reflowSmaller cases', () => { beforeEach(() => { - // Setup buffer state: - // 'abcd' - // 'efgh' (wrapped) - // 'ijkl' - // ' ' - // ' ' - // ' ' - // ' ' - // ' ' - // ' ' - // ' ' + /* + * Setup buffer state: + * 'abcd' + * 'efgh' (wrapped) + * 'ijkl' + * ' ' + * ' ' + * ' ' + * ' ' + * ' ' + * ' ' + * ' ' + */ buffer.fillViewportRows(); buffer.resize(4, 10); buffer.lines.get(0)!.set(0, [0, 'a', 1, 'a'.charCodeAt(0)]); @@ -1301,8 +1319,10 @@ describe('Buffer', () => { assert.equal(line.length, INIT_COLS / 2 - 1); } - // wait for a bit to give IdleTaskQueue a chance to kick in - // and finish memory cleaning + /* + * wait for a bit to give IdleTaskQueue a chance to kick in + * and finish memory cleaning + */ await new Promise(r => setTimeout(r, 30)); // cleanup should have realigned memory with exact bytelength diff --git a/src/common/buffer/Buffer.ts b/src/common/buffer/Buffer.ts index 34ef02f6a..a572418c4 100644 --- a/src/common/buffer/Buffer.ts +++ b/src/common/buffer/Buffer.ts @@ -169,19 +169,25 @@ export class Buffer extends Disposable implements IBuffer { // count bufferlines with overly big memory to be cleaned afterwards let dirtyMemoryLines = 0; - // Increase max length if needed before adjustments to allow space to fill - // as required. + /* + * Increase max length if needed before adjustments to allow space to fill + * as required. + */ const newMaxLength = this._getCorrectBufferLength(newRows); if (newMaxLength > this.lines.maxLength) { this.lines.maxLength = newMaxLength; } - // if (this._cols > newCols) { - // console.log('increase!'); - // } + /* + * if (this._cols > newCols) { + * console.log('increase!'); + * } + */ - // The following adjustments should only happen if the buffer has been - // initialized/filled. + /* + * The following adjustments should only happen if the buffer has been + * initialized/filled. + */ if (this.lines.length > 0) { // Deal with columns increasing (reducing needs to happen after reflow) if (this._cols < newCols) { @@ -197,13 +203,17 @@ export class Buffer extends Disposable implements IBuffer { for (let y = this._rows; y < newRows; y++) { if (this.lines.length < newRows + this.ybase) { if (this._optionsService.rawOptions.windowsPty.backend !== undefined || this._optionsService.rawOptions.windowsPty.buildNumber !== undefined) { - // Just add the new missing rows on Windows as conpty reprints the screen with it's - // view of the world. Once a line enters scrollback for conpty it remains there + /* + * Just add the new missing rows on Windows as conpty reprints the screen with it's + * view of the world. Once a line enters scrollback for conpty it remains there + */ this.lines.push(new BufferLine(this._stringCache, newCols, nullCell, false)); } else { if (this.ybase > 0 && this.lines.length <= this.ybase + this.y + addToY + 1) { - // There is room above the buffer and there are no empty elements below the line, - // scroll up + /* + * There is room above the buffer and there are no empty elements below the line, + * scroll up + */ this.ybase--; addToY++; if (this.ydisp > 0) { @@ -211,8 +221,10 @@ export class Buffer extends Disposable implements IBuffer { this.ydisp--; } } else { - // Add a blank line if there is no buffer left at the top to scroll to, or if there - // are blank lines after the cursor + /* + * Add a blank line if there is no buffer left at the top to scroll to, or if there + * are blank lines after the cursor + */ this.lines.push(new BufferLine(this._stringCache, newCols, nullCell, false)); } } @@ -233,8 +245,10 @@ export class Buffer extends Disposable implements IBuffer { } } - // Reduce max length if needed after adjustments, this is done after as it - // would otherwise cut data from the bottom of the buffer. + /* + * Reduce max length if needed after adjustments, this is done after as it + * would otherwise cut data from the bottom of the buffer. + */ if (newMaxLength < this.lines.maxLength) { // Trim from the top of the buffer and adjust ybase and ydisp. const amountToTrim = this.lines.length - newMaxLength; @@ -275,8 +289,10 @@ export class Buffer extends Disposable implements IBuffer { this._cols = newCols; this._rows = newRows; - // Ensure the cursor position invariant: ybase + y must be within buffer bounds - // This can be violated during reflow or when shrinking rows + /* + * Ensure the cursor position invariant: ybase + y must be within buffer bounds + * This can be violated during reflow or when shrinking rows + */ if (this.lines.length > 0) { const maxY = Math.max(0, this.lines.length - this.ybase - 1); this.y = Math.min(this.y, maxY); @@ -293,8 +309,10 @@ export class Buffer extends Disposable implements IBuffer { private _batchedMemoryCleanup(): boolean { let normalRun = true; if (this._memoryCleanupPosition >= this.lines.length) { - // cleanup made it once through all lines, thus rescan in loop below to also catch shifted - // lines, which should finish rather quick if there are no more cleanups pending + /* + * cleanup made it once through all lines, thus rescan in loop below to also catch shifted + * lines, which should finish rather quick if there are no more cleanups pending + */ this._memoryCleanupPosition = 0; normalRun = false; } @@ -306,9 +324,11 @@ export class Buffer extends Disposable implements IBuffer { return true; } } - // normal runs always need another rescan afterwards - // if we made it here with normalRun=false, we are in a final run - // and can end the cleanup task for sure + /* + * normal runs always need another rescan afterwards + * if we made it here with normalRun=false, we are in a final run + * and can end the cleanup task for sure + */ return normalRun; } @@ -369,8 +389,10 @@ export class Buffer extends Disposable implements IBuffer { private _reflowSmaller(newCols: number, newRows: number): void { const reflowCursorLine = this._optionsService.rawOptions.reflowCursorLine; const nullCell = this.getNullCell(DEFAULT_ATTR_DATA); - // Gather all BufferLines that need to be inserted into the Buffer here so that they can be - // batched up and only committed once + /* + * Gather all BufferLines that need to be inserted into the Buffer here so that they can be + * batched up and only committed once + */ const toInsert = []; let countToInsert = 0; // Go backwards as many lines may be trimmed and this will avoid considering them @@ -389,8 +411,10 @@ export class Buffer extends Disposable implements IBuffer { } if (!reflowCursorLine) { - // If these lines contain the cursor don't touch them, the program will handle fixing up - // wrapped lines with the cursor + /* + * If these lines contain the cursor don't touch them, the program will handle fixing up + * wrapped lines with the cursor + */ const absoluteY = this.ybase + this.y; if (absoluteY >= y && absoluteY < y + wrappedLines.length) { continue; @@ -416,8 +440,10 @@ export class Buffer extends Disposable implements IBuffer { } if (newLines.length > 0) { toInsert.push({ - // countToInsert here gets the actual index, taking into account other inserted items. - // using this we can iterate through the list forwards + /* + * countToInsert here gets the actual index, taking into account other inserted items. + * using this we can iterate through the list forwards + */ start: y + wrappedLines.length + countToInsert, newLines }); @@ -437,8 +463,10 @@ export class Buffer extends Disposable implements IBuffer { while (srcLineIndex >= 0) { const cellsToCopy = Math.min(srcCol, destCol); if (wrappedLines[destLineIndex] === undefined) { - // Sanity check that the line exists, this has been known to fail for an unknown reason - // which would stop the reflow from happening if an exception would throw. + /* + * Sanity check that the line exists, this has been known to fail for an unknown reason + * which would stop the reflow from happening if an exception would throw. + */ break; } wrappedLines[destLineIndex].copyCellsFrom(wrappedLines[srcLineIndex], srcCol - cellsToCopy, destCol - cellsToCopy, cellsToCopy, true); @@ -486,12 +514,16 @@ export class Buffer extends Disposable implements IBuffer { this.savedY = Math.min(this.savedY + linesToAdd, this.ybase + newRows - 1); } - // Rearrange lines in the buffer if there are any insertions, this is done at the end rather - // than earlier so that it's a single O(n) pass through the buffer, instead of O(n^2) from many - // costly calls to CircularList.splice. + /* + * Rearrange lines in the buffer if there are any insertions, this is done at the end rather + * than earlier so that it's a single O(n) pass through the buffer, instead of O(n^2) from many + * costly calls to CircularList.splice. + */ if (toInsert.length > 0) { - // Record buffer insert events and then play them back backwards so that the indexes are - // correct + /* + * Record buffer insert events and then play them back backwards so that the indexes are + * correct + */ const insertEvents: IInsertEvent[] = []; // Record original lines so they don't get overridden when we rearrange the list diff --git a/src/common/buffer/BufferLine.test.ts b/src/common/buffer/BufferLine.test.ts index 3aec28872..7e8f73f4c 100644 --- a/src/common/buffer/BufferLine.test.ts +++ b/src/common/buffer/BufferLine.test.ts @@ -288,9 +288,11 @@ describe('BufferLine', function(): void { assert.equal(line2.isWrapped, line.isWrapped); }); it('should support combining chars', function(): void { - // CHAR_DATA_CODE_INDEX resembles current behavior in InputHandler.print - // --> set code to the last charCodeAt value of the string - // Note: needs to be fixed once the string pointer is in place + /* + * CHAR_DATA_CODE_INDEX resembles current behavior in InputHandler.print + * --> set code to the last charCodeAt value of the string + * Note: needs to be fixed once the string pointer is in place + */ const line = new TestBufferLine(2, createCellData(1, 'e\u0301', 1)); assert.deepEqual(line.toArray(), [[1, 'e\u0301', 1, '\u0301'.charCodeAt(0)], [1, 'e\u0301', 1, '\u0301'.charCodeAt(0)]]); const line2 = new TestBufferLine(5, createCellData(1, 'a', 1), true); @@ -478,9 +480,11 @@ describe('BufferLine', function(): void { }); it('should always return some sane value', function(): void { const columns: number[] = []; - // sanity check - broken line with invalid out of bound null width cells - // this can atm happen with deleting/inserting chars in inputhandler by "breaking" - // fullwidth pairs --> needs to be fixed after settling BufferLine impl + /* + * sanity check - broken line with invalid out of bound null width cells + * this can atm happen with deleting/inserting chars in inputhandler by "breaking" + * fullwidth pairs --> needs to be fixed after settling BufferLine impl + */ const line = new TestBufferLine(10, NULL_CELL_DATA, false); assert.equal(line.translateToString(false, undefined, undefined, columns), ' '); assert.deepEqual(columns, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); @@ -500,8 +504,10 @@ describe('BufferLine', function(): void { const line = new TestBufferLine(3, NULL_CELL_DATA, false); line.addCodepointToCell(0, '\u0301'.charCodeAt(0), 0); const cell = line.loadCell(0, new CellData()); - // chars contains single combining char - // width is set to 1 + /* + * chars contains single combining char + * width is set to 1 + */ assert.deepEqual(cell.getAsCharData(), [DEFAULT_ATTR, '\u0301', 1, 0x0301]); // do not account a single combining char as combined assert.equal(cell.isCombined(), 0); @@ -513,8 +519,10 @@ describe('BufferLine', function(): void { line.setCell(0, cell); line.addCodepointToCell(0, '\u0301'.charCodeAt(0), 0); line.loadCell(0, cell); - // chars contains 3 chars - // width is set to 1 + /* + * chars contains 3 chars + * width is set to 1 + */ assert.deepEqual(cell.getAsCharData(), [123, 'e\u0301\u0301', 1, 0x0301]); // do not account a single combining char as combined assert.equal(cell.isCombined(), Content.IS_COMBINED_MASK); @@ -526,8 +534,10 @@ describe('BufferLine', function(): void { line.setCell(0, cell); line.addCodepointToCell(0, '\u0301'.charCodeAt(0), 0); line.loadCell(0, cell); - // chars contains 2 chars - // width is set to 1 + /* + * chars contains 2 chars + * width is set to 1 + */ assert.deepEqual(cell.getAsCharData(), [123, 'e\u0301', 1, 0x0301]); // do not account a single combining char as combined assert.equal(cell.isCombined(), Content.IS_COMBINED_MASK); diff --git a/src/common/buffer/BufferLine.ts b/src/common/buffer/BufferLine.ts index be200f112..0cef299e1 100644 --- a/src/common/buffer/BufferLine.ts +++ b/src/common/buffer/BufferLine.ts @@ -9,11 +9,13 @@ import { CellData } from 'common/buffer/CellData'; import { Attributes, BgFlags, CHAR_DATA_ATTR_INDEX, CHAR_DATA_CHAR_INDEX, CHAR_DATA_WIDTH_INDEX, Content, NULL_CELL_CHAR, NULL_CELL_CODE, NULL_CELL_WIDTH, WHITESPACE_CELL_CHAR } from 'common/buffer/Constants'; import { stringFromCodePoint } from 'common/input/TextDecoder'; -// Buffer memory layout: -// -// [0]: content `uint32_t` - wcwidth(2) comb(1) codepoint(21) -// [1]: fg `uint32_t` - flags(8) r(8) g(8) b(8) -// [2]: bg `uint32_t` - flags(8) r(8) g(8) b(8) +/* + * Buffer memory layout: + * + * [0]: content `uint32_t` - wcwidth(2) comb(1) codepoint(21) + * [1]: fg `uint32_t` - flags(8) r(8) g(8) b(8) + * [2]: bg `uint32_t` - flags(8) r(8) g(8) b(8) + */ const enum Constants { /** The number of 32 bit array indices taken by one cell. */ @@ -255,15 +257,19 @@ export class BufferLine implements IBufferLine { this._combined[index] += stringFromCodePoint(codePoint); } else { if (content & Content.CODEPOINT_MASK) { - // normal case for combining chars: - // - move current leading char + new one into combined string - // - set combined flag + /* + * normal case for combining chars: + * - move current leading char + new one into combined string + * - set combined flag + */ this._combined[index] = stringFromCodePoint(content & Content.CODEPOINT_MASK) + stringFromCodePoint(codePoint); content &= ~Content.CODEPOINT_MASK; // set codepoint in buffer to 0 content |= Content.IS_COMBINED_MASK; } else { - // should not happen - we actually have no data in the cell yet - // simply set the data in the cell buffer with a width of 1 + /* + * should not happen - we actually have no data in the cell yet + * simply set the data in the cell buffer with a width of 1 + */ content = codePoint | (1 << Content.WIDTH_SHIFT); } } @@ -318,9 +324,11 @@ export class BufferLine implements IBufferLine { } } - // handle fullwidth at pos: - // - reset pos-1 if wide char - // - reset pos if width==0 (previous second cell of a wide char) + /* + * handle fullwidth at pos: + * - reset pos-1 if wide char + * - reset pos if width==0 (previous second cell of a wide char) + */ if (pos && this.getWidth(pos - 1) === 2) { this.setCellFromCodepoint(pos - 1, 0, 1, fillCellData); } diff --git a/src/common/buffer/BufferReflow.ts b/src/common/buffer/BufferReflow.ts index 44aa0976f..5eaeae406 100644 --- a/src/common/buffer/BufferReflow.ts +++ b/src/common/buffer/BufferReflow.ts @@ -23,8 +23,10 @@ export interface INewLayoutResult { * @param reflowCursorLine Whether to reflow the line containing the cursor. */ export function reflowLargerGetLinesToRemove(lines: CircularList, oldCols: number, newCols: number, bufferAbsoluteY: number, nullCell: ICellData, reflowCursorLine: boolean): number[] { - // Gather all BufferLines that need to be removed from the Buffer here so that they can be - // batched up and only committed once + /* + * Gather all BufferLines that need to be removed from the Buffer here so that they can be + * batched up and only committed once + */ const toRemove: number[] = []; for (let y = 0; y < lines.length - 1; y++) { @@ -43,8 +45,10 @@ export function reflowLargerGetLinesToRemove(lines: CircularList, o } if (!reflowCursorLine) { - // If these lines contain the cursor don't touch them, the program will handle fixing up - // wrapped lines with the cursor + /* + * If these lines contain the cursor don't touch them, the program will handle fixing up + * wrapped lines with the cursor + */ if (bufferAbsoluteY >= y && bufferAbsoluteY < i) { y += wrappedLines.length - 1; continue; @@ -180,8 +184,10 @@ export function reflowSmallerGetNewLineLengths(wrappedLines: BufferLine[], oldCo const newLineLengths: number[] = []; const cellsNeeded = wrappedLines.map((l, i) => getWrappedLineTrimmedLength(wrappedLines, i, oldCols)).reduce((p, c) => p + c); - // Use srcCol and srcLine to find the new wrapping point, use that to get the cellsAvailable and - // linesNeeded + /* + * Use srcCol and srcLine to find the new wrapping point, use that to get the cellsAvailable and + * linesNeeded + */ let srcCol = 0; let srcLine = 0; let cellsAvailable = 0; @@ -214,9 +220,11 @@ export function getWrappedLineTrimmedLength(lines: BufferLine[], i: number, cols if (i === lines.length - 1) { return lines[i].getTrimmedLength(); } - // Detect whether the following line starts with a wide character and the end of the current line - // is null, if so then we can be pretty sure the null character should be excluded from the line - // length] + /* + * Detect whether the following line starts with a wide character and the end of the current line + * is null, if so then we can be pretty sure the null character should be excluded from the line + * length] + */ const endsInNull = !(lines[i].hasContent(cols - 1)) && lines[i].getWidth(cols - 1) === 1; const followingLineStartsWithWide = lines[i + 1].getWidth(0) === 2; if (endsInNull && followingLineStartsWithWide) { diff --git a/src/common/buffer/BufferSet.ts b/src/common/buffer/BufferSet.ts index 32a4bf3b4..2bad2171f 100644 --- a/src/common/buffer/BufferSet.ts +++ b/src/common/buffer/BufferSet.ts @@ -43,8 +43,10 @@ export class BufferSet extends Disposable implements IBufferSet { this._normalBuffer.value = this._normal; this._normal.fillViewportRows(); - // The alt buffer should never have scrollback. - // See http://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-The-Alternate-Screen-Buffer + /* + * The alt buffer should never have scrollback. + * See http://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-The-Alternate-Screen-Buffer + */ this._alt = new Buffer(false, this._optionsService, this._bufferService, this._logService); this._altBuffer.value = this._alt; this._activeBuffer = this._normal; @@ -86,9 +88,11 @@ export class BufferSet extends Disposable implements IBufferSet { } this._normal.x = this._alt.x; this._normal.y = this._alt.y; - // The alt buffer should always be cleared when we switch to the normal - // buffer. This frees up memory since the alt buffer should always be new - // when activated. + /* + * The alt buffer should always be cleared when we switch to the normal + * buffer. This frees up memory since the alt buffer should always be new + * when activated. + */ this._alt.clearAllMarkers(); this._alt.clear(); this._activeBuffer = this._normal; @@ -105,8 +109,10 @@ export class BufferSet extends Disposable implements IBufferSet { if (this._activeBuffer === this._alt) { return; } - // Since the alt buffer is always cleared when the normal buffer is - // activated, we want to fill it when switching to it. + /* + * Since the alt buffer is always cleared when the normal buffer is + * activated, we want to fill it when switching to it. + */ this._alt.fillViewportRows(fillAttr); this._alt.x = this._normal.x; this._alt.y = this._normal.y; diff --git a/src/common/buffer/CellData.ts b/src/common/buffer/CellData.ts index 43c4c594b..29e4a0f50 100644 --- a/src/common/buffer/CellData.ts +++ b/src/common/buffer/CellData.ts @@ -65,8 +65,10 @@ export class CellData extends AttributeData implements ICellData { } else if (value[CHAR_DATA_CHAR_INDEX].length === 2) { const code = value[CHAR_DATA_CHAR_INDEX].charCodeAt(0); - // if the 2-char string is a surrogate create single codepoint - // everything else is combined + /* + * if the 2-char string is a surrogate create single codepoint + * everything else is combined + */ if (0xD800 <= code && code <= 0xDBFF) { const second = value[CHAR_DATA_CHAR_INDEX].charCodeAt(1); if (0xDC00 <= second && second <= 0xDFFF) { diff --git a/src/common/input/Keyboard.test.ts b/src/common/input/Keyboard.test.ts index 47f516f0f..4e47cbce0 100644 --- a/src/common/input/Keyboard.test.ts +++ b/src/common/input/Keyboard.test.ts @@ -123,8 +123,10 @@ describe('Keyboard', () => { }); describe('On non-macOS platforms', () => { - // Evalueate alt + arrow key movement, which is a feature of terminal emulators but not VT100 - // http://unix.stackexchange.com/a/108106 + /* + * Evalueate alt + arrow key movement, which is a feature of terminal emulators but not VT100 + * http://unix.stackexchange.com/a/108106 + */ it('should return \\x1b[1;3D for alt+left', () => { assert.equal(testEvaluateKeyboardEvent({ altKey: true, keyCode: 37 }, { isMac: false }).key, '\x1b[1;3D'); // CSI 1;3 D }); diff --git a/src/common/input/Keyboard.ts b/src/common/input/Keyboard.ts index 545b436c8..54bc4a27e 100644 --- a/src/common/input/Keyboard.ts +++ b/src/common/input/Keyboard.ts @@ -43,8 +43,10 @@ export function evaluateKeyboardEvent( ): IKeyboardResult { const result: IKeyboardResult = { type: KeyboardResultType.SEND_KEY, - // Whether to cancel event propagation (NOTE: this may not be needed since the event is - // canceled at the end of keyDown + /* + * Whether to cancel event propagation (NOTE: this may not be needed since the event is + * canceled at the end of keyDown + */ cancel: false, // The new key even to emit key: undefined @@ -100,8 +102,10 @@ export function evaluateKeyboardEvent( case 13: // return/enter if (ev.key === 'c' && ev.ctrlKey) { - // HACK: Safari on iPad, iOS, AppleVisionPro sends key 13 when typing ctrl-c on hardware - // keyboard + /* + * HACK: Safari on iPad, iOS, AppleVisionPro sends key 13 when typing ctrl-c on hardware + * keyboard + */ result.key = C0.ETX; } else { result.key = ev.altKey ? C0.ESC + C0.CR : C0.CR; @@ -171,8 +175,10 @@ export function evaluateKeyboardEvent( case 45: // insert if (!ev.shiftKey && !ev.ctrlKey) { - // or + are used to - // copy-paste on some systems. + /* + * or + are used to + * copy-paste on some systems. + */ result.key = C0.ESC + '[2~'; } break; @@ -346,11 +352,13 @@ export function evaluateKeyboardEvent( } else if (ev.keyCode === 32) { result.key = C0.ESC + (ev.ctrlKey ? C0.NUL : ' '); } else if (ev.key === 'Dead' && ev.code.startsWith('Key')) { - // Reference: https://github.com/xtermjs/xterm.js/issues/3725 - // Alt will produce a "dead key" (initate composition) with some - // of the letters in US layout (e.g. N/E/U). - // It's safe to match against Key* since no other `code` values begin with "Key". - // https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/code/code_values#code_values_on_mac + /* + * Reference: https://github.com/xtermjs/xterm.js/issues/3725 + * Alt will produce a "dead key" (initate composition) with some + * of the letters in US layout (e.g. N/E/U). + * It's safe to match against Key* since no other `code` values begin with "Key". + * https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/code/code_values#code_values_on_mac + */ let keyString = ev.code.slice(3, 4); if (!ev.shiftKey) { keyString = keyString.toLowerCase(); @@ -363,8 +371,10 @@ export function evaluateKeyboardEvent( result.type = KeyboardResultType.SELECT_ALL; } } else if (ev.key && !ev.ctrlKey && !ev.altKey && !ev.metaKey && ev.keyCode >= 48 && ev.key.length === 1) { - // Include only keys that that result in a _single_ character; don't include num lock, - // volume up, etc. + /* + * Include only keys that that result in a _single_ character; don't include num lock, + * volume up, etc. + */ result.key = ev.key; } else if (ev.key && ev.ctrlKey && ev.shiftKey) { switch (ev.code) { diff --git a/src/common/input/KittyKeyboard.test.ts b/src/common/input/KittyKeyboard.test.ts index 2a832d75f..44c4ae2bb 100644 --- a/src/common/input/KittyKeyboard.test.ts +++ b/src/common/input/KittyKeyboard.test.ts @@ -40,8 +40,10 @@ describe('KittyKeyboard', () => { const flags = KittyKeyboardFlags.DISAMBIGUATE_ESCAPE_CODES; it('shift+letter sends plain character in DISAMBIGUATE mode', () => { - // Kitty spec: DISAMBIGUATE only encodes keys ambiguous in legacy encoding - // Shift+a → "A" is not ambiguous, so send plain "A" + /* + * Kitty spec: DISAMBIGUATE only encodes keys ambiguous in legacy encoding + * Shift+a → "A" is not ambiguous, so send plain "A" + */ const result = kitty.evaluate(createEvent({ key: 'A', shiftKey: true }), flags); assert.strictEqual(result.key, 'A'); }); @@ -554,8 +556,10 @@ describe('KittyKeyboard', () => { }); }); - // Enabling REPORT_EVENT_TYPES without DISAMBIGUATE_ESCAPE_CODES doesn't really make sense and - // isn't specified in the spec, but press and repeat events shouldn't get swallowed. + /* + * Enabling REPORT_EVENT_TYPES without DISAMBIGUATE_ESCAPE_CODES doesn't really make sense and + * isn't specified in the spec, but press and repeat events shouldn't get swallowed. + */ describe('REPORT_EVENT_TYPES flag without DISAMBIGUATE_ESCAPE_CODES', () => { const flags = KittyKeyboardFlags.REPORT_EVENT_TYPES; diff --git a/src/common/input/KittyKeyboard.ts b/src/common/input/KittyKeyboard.ts index ff57b4f89..658d4e0d9 100644 --- a/src/common/input/KittyKeyboard.ts +++ b/src/common/input/KittyKeyboard.ts @@ -437,10 +437,12 @@ export class KittyKeyboard { return result; } - // Spec § "Report all keys as escape codes": "Additionally, with this mode, - // events for pressing modifier keys are reported." — i.e. *without* this - // mode, modifier-key press events are suppressed. Kitty's is_modifier_key() - // treats CapsLock/NumLock/ScrollLock as modifier keys for this rule. + /* + * Spec § "Report all keys as escape codes": "Additionally, with this mode, + * events for pressing modifier keys are reported." — i.e. *without* this + * mode, modifier-key press events are suppressed. Kitty's is_modifier_key() + * treats CapsLock/NumLock/ScrollLock as modifier keys for this rule. + */ if (this._isLockKey(ev) && !(flags & KittyKeyboardFlags.REPORT_ALL_KEYS_AS_ESCAPE_CODES)) { return result; } @@ -474,8 +476,10 @@ export class KittyKeyboard { // Special handling for Enter/Tab/Backspace. const specialKey = keyCode === 13 || keyCode === 9 || keyCode === 127; - // Per spec, Enter/Tab/Backspace will not have release events unless "Report all keys as escape - // codes" is also set. + /* + * Per spec, Enter/Tab/Backspace will not have release events unless "Report all keys as escape + * codes" is also set. + */ if (specialKey && eventType === KittyKeyboardEventType.RELEASE && !(flags & KittyKeyboardFlags.REPORT_ALL_KEYS_AS_ESCAPE_CODES)) { return result; } @@ -485,14 +489,18 @@ export class KittyKeyboard { const useCsiU = !!( flags & KittyKeyboardFlags.REPORT_ALL_KEYS_AS_ESCAPE_CODES || (reportEventTypes && eventType === KittyKeyboardEventType.RELEASE) || - // Enabling REPORT_EVENT_TYPES without DISAMBIGUATE_ESCAPE_CODES doesn't really make sense, so - // just make REPORT_EVENT_TYPES imply DISAMBIGUATE_ESCAPE_CODES here for simplicity. - // See: https://github.com/kovidgoyal/kitty/issues/9999 + /* + * Enabling REPORT_EVENT_TYPES without DISAMBIGUATE_ESCAPE_CODES doesn't really make sense, so + * just make REPORT_EVENT_TYPES imply DISAMBIGUATE_ESCAPE_CODES here for simplicity. + * See: https://github.com/kovidgoyal/kitty/issues/9999 + */ ((flags & KittyKeyboardFlags.DISAMBIGUATE_ESCAPE_CODES || reportEventTypes) && ( - // Per spec, Enter/Tab/Backspace "still generate the same bytes as in legacy mode" and - // consider space to be a text-generating key, so these skip the isFunc fast-path and only - // get CSI u when modifiers are present (handled below). + /* + * Per spec, Enter/Tab/Backspace "still generate the same bytes as in legacy mode" and + * consider space to be a text-generating key, so these skip the isFunc fast-path and only + * get CSI u when modifiers are present (handled below). + */ (isFunc && !specialKey) || ( (modifiers > 0 && ev.key.length !== 1) || diff --git a/src/common/input/TextDecoder.ts b/src/common/input/TextDecoder.ts index 7ec9c7cd2..cc503166d 100644 --- a/src/common/input/TextDecoder.ts +++ b/src/common/input/TextDecoder.ts @@ -28,11 +28,13 @@ export function utf32ToString(data: Uint32Array, start: number = 0, end: number for (let i = start; i < end; ++i) { let codepoint = data[i]; if (codepoint > 0xFFFF) { - // JS strings are encoded as UTF16, thus a non BMP codepoint gets converted into a surrogate - // pair conversion rules: - // - subtract 0x10000 from code point, leaving a 20 bit number - // - add high 10 bits to 0xD800 --> first surrogate - // - add low 10 bits to 0xDC00 --> second surrogate + /* + * JS strings are encoded as UTF16, thus a non BMP codepoint gets converted into a surrogate + * pair conversion rules: + * - subtract 0x10000 from code point, leaving a 20 bit number + * - add high 10 bits to 0xD800 --> first surrogate + * - add low 10 bits to 0xDC00 --> second surrogate + */ codepoint -= 0x10000; result += String.fromCharCode((codepoint >> 10) + 0xD800) + String.fromCharCode((codepoint % 0x400) + 0xDC00); } else { diff --git a/src/common/input/UnicodeV6.test.ts b/src/common/input/UnicodeV6.test.ts index 98dc97dc2..95c2f6e2f 100644 --- a/src/common/input/UnicodeV6.test.ts +++ b/src/common/input/UnicodeV6.test.ts @@ -12,8 +12,10 @@ it('wcwidth should match all values from the old implementation', function(): vo // old implementation const wcwidthOld = (function(opts: {nul: number, control: number}): (ucs: number) => number { - // extracted from https://www.cl.cam.ac.uk/%7Emgk25/ucs/wcwidth.c - // combining characters + /* + * extracted from https://www.cl.cam.ac.uk/%7Emgk25/ucs/wcwidth.c + * combining characters + */ const COMBINING_BMP = [ [0x0300, 0x036F], [0x0483, 0x0486], [0x0488, 0x0489], [0x0591, 0x05BD], [0x05BF, 0x05BF], [0x05C1, 0x05C2], @@ -149,18 +151,20 @@ it('wcwidth should match all values from the old implementation', function(): vo } return table; } - // get width from lookup table - // position in container : num / CODEPOINTS_PER_ITEM - // ==> n = table[Math.floor(num / 16)] - // ==> n = table[num >> 4] - // 16 codepoints per number: FFEEDDCCBBAA99887766554433221100 - // position in number : (num % CODEPOINTS_PER_ITEM) * BITWIDTH - // ==> m = (n % 16) * 2 - // ==> m = (num & 15) << 1 - // right shift to position m - // ==> n = n >> m e.g. m=12 000000000000FFEEDDCCBBAA99887766 - // we are only interested in 2 LSBs, cut off higher bits - // ==> n = n & 3 e.g. 000000000000000000000000000000XX + /* + * get width from lookup table + * position in container : num / CODEPOINTS_PER_ITEM + * ==> n = table[Math.floor(num / 16)] + * ==> n = table[num >> 4] + * 16 codepoints per number: FFEEDDCCBBAA99887766554433221100 + * position in number : (num % CODEPOINTS_PER_ITEM) * BITWIDTH + * ==> m = (n % 16) * 2 + * ==> m = (num & 15) << 1 + * right shift to position m + * ==> n = n >> m e.g. m=12 000000000000FFEEDDCCBBAA99887766 + * we are only interested in 2 LSBs, cut off higher bits + * ==> n = n & 3 e.g. 000000000000000000000000000000XX + */ return (num: number): number => { num |= 0; // get asm.js like optimization under V8 if (num < 32) { diff --git a/src/common/input/UnicodeV6.ts b/src/common/input/UnicodeV6.ts index 83265f705..4b6bd55c0 100644 --- a/src/common/input/UnicodeV6.ts +++ b/src/common/input/UnicodeV6.ts @@ -94,8 +94,10 @@ export class UnicodeV6 implements IUnicodeVersionProvider { table.fill(0, 1, 32); table.fill(0, 0x7f, 0xa0); - // apply wide char rules first - // wide chars + /* + * apply wide char rules first + * wide chars + */ table.fill(2, 0x1100, 0x1160); table[0x2329] = 2; table[0x232a] = 2; @@ -109,11 +111,13 @@ export class UnicodeV6 implements IUnicodeVersionProvider { table.fill(2, 0xff00, 0xff61); table.fill(2, 0xffe0, 0xffe7); - // apply combining last to ensure we overwrite - // wrongly wide set chars: - // the original algo evals combining first and falls - // through to wide check so we simply do here the opposite - // combining 0 + /* + * apply combining last to ensure we overwrite + * wrongly wide set chars: + * the original algo evals combining first and falls + * through to wide check so we simply do here the opposite + * combining 0 + */ for (let r = 0; r < BMP_COMBINING.length; ++r) { table.fill(0, BMP_COMBINING[r][0], BMP_COMBINING[r][1] + 1); } diff --git a/src/common/input/Win32InputMode.ts b/src/common/input/Win32InputMode.ts index 72907836d..23f6ade0f 100644 --- a/src/common/input/Win32InputMode.ts +++ b/src/common/input/Win32InputMode.ts @@ -198,8 +198,10 @@ export class Win32InputMode { * Returns 0 for non-character keys. */ private _getUnicodeChar(ev: IKeyboardEvent): number { - // Handle special keys that produce control characters - // Ctrl modifies some of these: Ctrl+Enter=LF, Ctrl+Backspace=DEL + /* + * Handle special keys that produce control characters + * Ctrl modifies some of these: Ctrl+Enter=LF, Ctrl+Backspace=DEL + */ if (ev.ctrlKey && !ev.altKey && !ev.metaKey) { if (ev.key === 'Enter') { return 0x0A; // Line feed (Ctrl+Enter) @@ -245,9 +247,11 @@ export class Win32InputMode { state |= Win32ControlKeyState.SHIFT_PRESSED; } - // Note: We can't distinguish left/right for ctrl/alt in standard browser events, - // so we use the generic pressed flags. The right-side flags are used when - // we can detect them (e.g., via code property). + /* + * Note: We can't distinguish left/right for ctrl/alt in standard browser events, + * so we use the generic pressed flags. The right-side flags are used when + * we can detect them (e.g., via code property). + */ if (ev.ctrlKey) { if (ev.code === 'ControlRight') { state |= Win32ControlKeyState.RIGHT_CTRL_PRESSED; diff --git a/src/common/input/WriteBuffer.ts b/src/common/input/WriteBuffer.ts index b936c9dbf..888db9ac5 100644 --- a/src/common/input/WriteBuffer.ts +++ b/src/common/input/WriteBuffer.ts @@ -90,12 +90,16 @@ export class WriteBuffer extends Disposable { * @deprecated Unreliable, to be removed soon. */ public writeSync(data: string | Uint8Array, maxSubsequentCalls?: number): void { - // stop writeSync recursions with maxSubsequentCalls argument - // This is dangerous to use as it will lose the current data chunk - // and return immediately. + /* + * stop writeSync recursions with maxSubsequentCalls argument + * This is dangerous to use as it will lose the current data chunk + * and return immediately. + */ if (maxSubsequentCalls !== undefined && this._syncCalls > maxSubsequentCalls) { - // comment next line if a whole loop block should only contain x `writeSync` calls - // (total flat vs. deep nested limit) + /* + * comment next line if a whole loop block should only contain x `writeSync` calls + * (total flat vs. deep nested limit) + */ this._syncCalls = 0; return; } @@ -112,18 +116,22 @@ export class WriteBuffer extends Disposable { } this._isSyncWriting = true; - // force sync processing on pending data chunks to avoid in-band data scrambling - // does the same as innerWrite but without event loop - // we have to do it here as single loop steps to not corrupt loop subject - // by another writeSync call triggered from _action + /* + * force sync processing on pending data chunks to avoid in-band data scrambling + * does the same as innerWrite but without event loop + * we have to do it here as single loop steps to not corrupt loop subject + * by another writeSync call triggered from _action + */ let chunk: string | Uint8Array | undefined; while (chunk = this._writeBuffer.shift()) { this._action(chunk); const cb = this._callbacks.shift(); if (cb) cb(); } - // reset to avoid reprocessing of chunks with scheduled innerWrite call - // stopping scheduled innerWrite by offset > length condition + /* + * reset to avoid reprocessing of chunks with scheduled innerWrite call + * stopping scheduled innerWrite by offset > length condition + */ this._pendingData = 0; this._bufferOffset = 0x7FFFFFFF; @@ -141,9 +149,11 @@ export class WriteBuffer extends Disposable { if (!this._writeBuffer.length) { this._bufferOffset = 0; - // If this is the first write call after the user has done some input, - // parse it immediately to minimize input latency, - // otherwise schedule for the next event + /* + * If this is the first write call after the user has done some input, + * parse it immediately to minimize input latency, + * otherwise schedule for the next event + */ if (this._didUserInput) { this._didUserInput = false; this._pendingData += data.length; @@ -234,15 +244,19 @@ export class WriteBuffer extends Disposable { * throughput by eval'ing `startTime` upfront pulling at least one more chunk into the * current microtask queue (executed before setTimeout). */ - // const continuation: (r: boolean) => void = performance.now() - startTime >= - // Constants.WRITE_TIMEOUT_MS - // ? r => setTimeout(() => this._innerWrite(0, r)) - // : r => this._innerWrite(startTime, r); + /* + * const continuation: (r: boolean) => void = performance.now() - startTime >= + * Constants.WRITE_TIMEOUT_MS + * ? r => setTimeout(() => this._innerWrite(0, r)) + * : r => this._innerWrite(startTime, r); + */ - // Handle exceptions synchronously to current band position, idea: - // 1. spawn a single microtask which we allow to throw hard - // 2. spawn a promise immediately resolving to `true` - // (executed on the same queue, thus properly aligned before continuation happens) + /* + * Handle exceptions synchronously to current band position, idea: + * 1. spawn a single microtask which we allow to throw hard + * 2. spawn a promise immediately resolving to `true` + * (executed on the same queue, thus properly aligned before continuation happens) + */ result.catch(err => { queueMicrotask(() => {throw err;}); return Promise.resolve(false); @@ -260,8 +274,10 @@ export class WriteBuffer extends Disposable { } } if (this._writeBuffer.length > this._bufferOffset) { - // Allow renderer to catch up before processing the next batch - // trim already processed chunks if we are above threshold + /* + * Allow renderer to catch up before processing the next batch + * trim already processed chunks if we are above threshold + */ if (this._bufferOffset > Constants.WRITE_BUFFER_LENGTH_THRESHOLD) { this._writeBuffer = this._writeBuffer.slice(this._bufferOffset); this._callbacks = this._callbacks.slice(this._bufferOffset); diff --git a/src/common/input/XParseColor.ts b/src/common/input/XParseColor.ts index ae4a3bcae..2f4a79dc8 100644 --- a/src/common/input/XParseColor.ts +++ b/src/common/input/XParseColor.ts @@ -50,9 +50,11 @@ export function parseColor(data: string): [number, number, number] | undefined { } } - // Named colors are currently not supported due to the large addition to the xterm.js bundle size - // they would add. In order to support named colors, we would need some way of optionally loading - // additional payloads so startup/download time is not bloated (see #3530). + /* + * Named colors are currently not supported due to the large addition to the xterm.js bundle size + * they would add. In order to support named colors, we would need some way of optionally loading + * additional payloads so startup/download time is not bloated (see #3530). + */ } // pad hex output to requested bit width diff --git a/src/common/parser/ApcParser.ts b/src/common/parser/ApcParser.ts index 8f921f8cb..453c71110 100644 --- a/src/common/parser/ApcParser.ts +++ b/src/common/parser/ApcParser.ts @@ -181,8 +181,10 @@ export class ApcHandler implements IApcHandler { } else if (success) { ret = this._handler(this._data); if (ret instanceof Promise) { - // need to hold data until `ret` got resolved - // dont care for errors, data will be freed anyway on next start + /* + * need to hold data until `ret` got resolved + * dont care for errors, data will be freed anyway on next start + */ return ret.then(res => { this._data = ''; this._hitLimit = false; diff --git a/src/common/parser/DcsParser.ts b/src/common/parser/DcsParser.ts index 692200230..849b82709 100644 --- a/src/common/parser/DcsParser.ts +++ b/src/common/parser/DcsParser.ts @@ -147,10 +147,12 @@ export class DcsHandler implements IDcsHandler { constructor(private _handler: (data: string, params: IParams) => boolean | Promise) { } public hook(params: IParams): void { - // since we need to preserve params until `unhook`, we have to clone it - // (only borrowed from parser and spans multiple parser states) - // perf optimization: - // clone only, if we have non empty params, otherwise stick with default + /* + * since we need to preserve params until `unhook`, we have to clone it + * (only borrowed from parser and spans multiple parser states) + * perf optimization: + * clone only, if we have non empty params, otherwise stick with default + */ this._params = (params.length > 1 || params.params[0]) ? params.clone() : EMPTY_PARAMS; this._data = ''; this._hitLimit = false; @@ -174,8 +176,10 @@ export class DcsHandler implements IDcsHandler { } else if (success) { ret = this._handler(this._data, this._params); if (ret instanceof Promise) { - // need to hold data and params until `ret` got resolved - // dont care for errors, data will be freed anyway on next start + /* + * need to hold data and params until `ret` got resolved + * dont care for errors, data will be freed anyway on next start + */ return ret.then(res => { this._params = EMPTY_PARAMS; this._data = ''; diff --git a/src/common/parser/EscapeSequenceParser.test.ts b/src/common/parser/EscapeSequenceParser.test.ts index 2d5a04df7..17cc37ee3 100644 --- a/src/common/parser/EscapeSequenceParser.test.ts +++ b/src/common/parser/EscapeSequenceParser.test.ts @@ -2065,8 +2065,10 @@ async function throwsAsync(fn: () => Promise, message?: string | undefined) } describe('EscapeSequenceParser - async', () => { - // sequences: SGR 1;31 | hello SP | ESC %G | wor | ESC E | ld! | SGR 0 | EXE \r\n | $> | DCS 1;2 a [xyz] ST | OSC 1;foo=bar ST | APC X abc ST | FIN - // needed handlers: CSI m, PRINT, ESC %G, ESC E, EXE \r, EXE \n, OSC 1, APC X + /* + * sequences: SGR 1;31 | hello SP | ESC %G | wor | ESC E | ld! | SGR 0 | EXE \r\n | $> | DCS 1;2 a [xyz] ST | OSC 1;foo=bar ST | APC X abc ST | FIN + * needed handlers: CSI m, PRINT, ESC %G, ESC E, EXE \r, EXE \n, OSC 1, APC X + */ const INPUT = '\x1b[1;31mhello \x1b%Gwor\x1bEld!\x1b[0m\r\n$>\x1bP1;2axyz\x1b\\\x1b]1;foo=bar\x1b\\\x1b_Xabc\x1b\\FIN'; let RESULT: any[]; let parser: TestEscapeSequenceParser; @@ -2155,8 +2157,10 @@ describe('EscapeSequenceParser - async', () => { it('sync parse call does not work anymore', () => { assert.notEqual(!parseSync(parser, INPUT), true); assert.notDeepEqual(callstack, RESULT); - // due to sync calling we should save exactly one saved stack - // proper continuation is not possible anymore, as we lost the promise resolve value + /* + * due to sync calling we should save exactly one saved stack + * proper continuation is not possible anymore, as we lost the promise resolve value + */ assert.equal(parser.trackedStack.length, 1); }); it('improper continuation should throw', async () => { @@ -2222,8 +2226,10 @@ describe('EscapeSequenceParser - async', () => { // split to single char input for (let i = 0; i < INPUT.length; ++i) { - // Note: a single fully awaited parse call always ends in sync mode, - // which re-enables faster sync processing in the higher up callstack + /* + * Note: a single fully awaited parse call always ends in sync mode, + * which re-enables faster sync processing in the higher up callstack + */ await parseP(parser, INPUT[i]); } assert.deepEqual(callstack, RESULT); diff --git a/src/common/parser/EscapeSequenceParser.ts b/src/common/parser/EscapeSequenceParser.ts index d944112db..be4877766 100644 --- a/src/common/parser/EscapeSequenceParser.ts +++ b/src/common/parser/EscapeSequenceParser.ts @@ -15,20 +15,22 @@ import { ApcParser } from 'common/parser/ApcParser'; /** * VT commands done by the parser */ -// @vt: #Y ESC CSI "Control Sequence Introducer" "ESC [" "Start of a CSI sequence." -// @vt: #Y ESC OSC "Operating System Command" "ESC ]" "Start of an OSC sequence." -// @vt: #Y ESC DCS "Device Control String" "ESC P" "Start of a DCS sequence." -// @vt: #Y ESC ST "String Terminator" "ESC \" "Terminator used for string type sequences." -// @vt: #Y ESC PM "Privacy Message" "ESC ^" "Start of a privacy message." -// @vt: #Y ESC APC "Application Program Command" "ESC _" "Start of an APC sequence." -// @vt: #Y C1 CSI "Control Sequence Introducer" "\x9B" "Start of a CSI sequence." -// @vt: #Y C1 OSC "Operating System Command" "\x9D" "Start of an OSC sequence." -// @vt: #Y C1 DCS "Device Control String" "\x90" "Start of a DCS sequence." -// @vt: #Y C1 ST "String Terminator" "\x9C" "Terminator used for string type sequences." -// @vt: #Y C1 PM "Privacy Message" "\x9E" "Start of a privacy message." -// @vt: #Y C1 APC "Application Program Command" "\x9F" "Start of an APC sequence." -// @vt: #Y C0 NUL "Null" "\0, \x00" "NUL is ignored." -// @vt: #Y C0 ESC "Escape" "\e, \x1B" "Start of a sequence. Cancels any other sequence." +/* + * @vt: #Y ESC CSI "Control Sequence Introducer" "ESC [" "Start of a CSI sequence." + * @vt: #Y ESC OSC "Operating System Command" "ESC ]" "Start of an OSC sequence." + * @vt: #Y ESC DCS "Device Control String" "ESC P" "Start of a DCS sequence." + * @vt: #Y ESC ST "String Terminator" "ESC \" "Terminator used for string type sequences." + * @vt: #Y ESC PM "Privacy Message" "ESC ^" "Start of a privacy message." + * @vt: #Y ESC APC "Application Program Command" "ESC _" "Start of an APC sequence." + * @vt: #Y C1 CSI "Control Sequence Introducer" "\x9B" "Start of a CSI sequence." + * @vt: #Y C1 OSC "Operating System Command" "\x9D" "Start of an OSC sequence." + * @vt: #Y C1 DCS "Device Control String" "\x90" "Start of a DCS sequence." + * @vt: #Y C1 ST "String Terminator" "\x9C" "Terminator used for string type sequences." + * @vt: #Y C1 PM "Privacy Message" "\x9E" "Start of a privacy message." + * @vt: #Y C1 APC "Application Program Command" "\x9F" "Start of an APC sequence." + * @vt: #Y C0 NUL "Null" "\0, \x00" "NUL is ignored." + * @vt: #Y C0 ESC "Escape" "\e, \x1B" "Start of a sequence. Cancels any other sequence." + */ /** * Table values are generated like this: @@ -95,8 +97,10 @@ const NON_ASCII_PRINTABLE = 0xA0; * Taken from https://vt100.net/emu/dec_ansi_parser. */ export const VT500_TRANSITION_TABLE = (function (): TransitionTable { - // table size: - // (ParserState.STATE_LENGTH - 1) << TableAccess.INDEX_STATE_SHIFT | NON_ASCII_PRINTABLE + 1 + /* + * table size: + * (ParserState.STATE_LENGTH - 1) << TableAccess.INDEX_STATE_SHIFT | NON_ASCII_PRINTABLE + 1 + */ const table: TransitionTable = new TransitionTable(4257); // range macro for byte @@ -500,9 +504,11 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP this._params.resetZdm(); this._collect = 0; this.precedingJoinState = 0; - // abort pending continuation from async handler - // Here the RESET type indicates, that the next parse call will - // ignore any saved stack, instead continues sync with next codepoint from GROUND + /* + * abort pending continuation from async handler + * Here the RESET type indicates, that the next parse call will + * ignore any saved stack, instead continues sync with next codepoint from GROUND + */ if (this._parseStack.state !== ParserStackType.NONE) { this._parseStack.state = ParserStackType.RESET; this._parseStack.handlers = []; // also release handlers ref @@ -579,8 +585,10 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP // resume from async handler if (this._parseStack.state) { - // allow sync parser reset even in continuation mode - // Note: can be used to recover parser from improper continuation error below + /* + * allow sync parser reset even in continuation mode + * Note: can be used to recover parser from improper continuation error below + */ if (this._parseStack.state === ParserStackType.RESET) { this._parseStack.state = ParserStackType.NONE; start = this._parseStack.chunkPos + 1; // continue with next codepoint in GROUND @@ -605,9 +613,11 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP throw new Error('improper continuation due to previous async handler, giving up parsing'); } - // we have to resume the old handler loop if: - // - return value of the promise was `false` - // - handlers are not exhausted yet + /* + * we have to resume the old handler loop if: + * - return value of the promise was `false` + * - handlers are not exhausted yet + */ const handlers = this._parseStack.handlers; let handlerPos = this._parseStack.handlerPos - 1; switch (this._parseStack.state) { @@ -794,8 +804,10 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP const handlers = this._csiHandlers[this._collect << 8 | code]; let j = handlers ? handlers.length - 1 : -1; for (; j >= 0; j--) { - // true means success and to stop bubbling - // a promise indicates an async handler that needs to finish before progressing + /* + * true means success and to stop bubbling + * a promise indicates an async handler that needs to finish before progressing + */ handlerResult = handlers[j](this._params); if (handlerResult === true) { break; @@ -833,8 +845,10 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP const handlersEsc = this._escHandlers[this._collect << 8 | code]; let jj = handlersEsc ? handlersEsc.length - 1 : -1; for (; jj >= 0; jj--) { - // true means success and to stop bubbling - // a promise indicates an async handler that needs to finish before progressing + /* + * true means success and to stop bubbling + * a promise indicates an async handler that needs to finish before progressing + */ handlerResult = handlersEsc[jj](); if (handlerResult === true) { break; @@ -856,8 +870,10 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP this._dcsParser.hook(this._collect << 8 | code, this._params); break; case ParserAction.DCS_PUT: - // inner loop - exit DCS_PUT: 0x18, 0x1a, 0x1b, 0x7f, 0x80 - 0x9f - // unhook triggered by: 0x1b, 0x9c (success) and 0x18, 0x1a (abort) + /* + * inner loop - exit DCS_PUT: 0x18, 0x1a, 0x1b, 0x7f, 0x80 - 0x9f + * unhook triggered by: 0x1b, 0x9c (success) and 0x18, 0x1a (abort) + */ for (let j = i + 1; ; ++j) { if (j >= length || (code = data[j]) === 0x18 || code === 0x1a || code === 0x1b || (code > 0x7f && code < NON_ASCII_PRINTABLE)) { this._dcsParser.put(data, i, j); @@ -905,8 +921,10 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP this._apcParser.start(this._collect << 8 | code); break; case ParserAction.APC_PUT: - // inner loop - exit APC_PUT: 0x18, 0x1a, 0x1b, 0x9c - // allowed: 00/08 .. 00/13, 02/00 .. 07/14 + NON_ASCII_PRINTABLE + /* + * inner loop - exit APC_PUT: 0x18, 0x1a, 0x1b, 0x9c + * allowed: 00/08 .. 00/13, 02/00 .. 07/14 + NON_ASCII_PRINTABLE + */ for (let j = i + 1; ; ++j) { if (j < length && ( (data[j] >= 0x20 && data[j] < 0x7f) || (data[j] >= 0x08 && data[j] < 0x0e) || data[j] >= NON_ASCII_PRINTABLE diff --git a/src/common/parser/OscParser.ts b/src/common/parser/OscParser.ts index 1f0c8cb23..d348f483b 100644 --- a/src/common/parser/OscParser.ts +++ b/src/common/parser/OscParser.ts @@ -133,9 +133,11 @@ export class OscParser implements IOscParser { } // do nothing if command was faulty if (this._state !== OscState.ABORT) { - // if we are still in ID state and get an early end - // means that the command has no payload thus we still have - // to announce START and send END right after + /* + * if we are still in ID state and get an early end + * means that the command has no payload thus we still have + * to announce START and send END right after + */ if (this._state === OscState.ID) { this._start(); } @@ -166,9 +168,11 @@ export class OscParser implements IOscParser { } j--; } - // cleanup left over handlers - // we always have to call .end for proper cleanup, - // here we use `success` to indicate whether a handler should execute + /* + * cleanup left over handlers + * we always have to call .end for proper cleanup, + * here we use `success` to indicate whether a handler should execute + */ for (; j >= 0; j--) { handlerResult = this._active[j].end(false); if (handlerResult instanceof Promise) { @@ -222,8 +226,10 @@ export class OscHandler implements IOscHandler { } else if (success) { ret = this._handler(this._data); if (ret instanceof Promise) { - // need to hold data until `ret` got resolved - // dont care for errors, data will be freed anyway on next start + /* + * need to hold data until `ret` got resolved + * dont care for errors, data will be freed anyway on next start + */ return ret.then(res => { this._data = ''; this._hitLimit = false; diff --git a/src/common/services/BufferService.ts b/src/common/services/BufferService.ts index 06ac6b10a..ecf71eaf8 100644 --- a/src/common/services/BufferService.ts +++ b/src/common/services/BufferService.ts @@ -103,22 +103,28 @@ export class BufferService extends Disposable implements IBufferService { buffer.ydisp++; } } else { - // When the buffer is full and the user has scrolled up, keep the text - // stable unless ydisp is right at the top + /* + * When the buffer is full and the user has scrolled up, keep the text + * stable unless ydisp is right at the top + */ if (this.isUserScrolling) { buffer.ydisp = Math.max(buffer.ydisp - 1, 0); } } } else { - // scrollTop is non-zero which means no line will be going to the - // scrollback, instead we can just shift them in-place. + /* + * scrollTop is non-zero which means no line will be going to the + * scrollback, instead we can just shift them in-place. + */ const scrollRegionHeight = bottomRow - topRow + 1 /* as it's zero-based */; buffer.lines.shiftElements(topRow + 1, scrollRegionHeight - 1, -1); buffer.lines.set(bottomRow, newLine.clone()); } - // Move the viewport to the bottom of the buffer unless the user is - // scrolling. + /* + * Move the viewport to the bottom of the buffer unless the user is + * scrolling. + */ if (!this.isUserScrolling) { buffer.ydisp = buffer.ybase; } diff --git a/src/common/services/InstantiationService.ts b/src/common/services/InstantiationService.ts index 7e7695485..f11ff7f6e 100644 --- a/src/common/services/InstantiationService.ts +++ b/src/common/services/InstantiationService.ts @@ -4,10 +4,12 @@ * * This was heavily inspired from microsoft/vscode's dependency injection system (MIT). */ -/*--------------------------------------------------------------------------------------------- +/* + *--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ + *-------------------------------------------------------------------------------------------- + */ import { IInstantiationService, IServiceIdentifier } from 'common/services/Services'; import { getServiceDependencies } from 'common/services/ServiceRegistry'; diff --git a/src/common/services/LogService.ts b/src/common/services/LogService.ts index 8bc1e0aaa..eea09f47d 100644 --- a/src/common/services/LogService.ts +++ b/src/common/services/LogService.ts @@ -16,8 +16,10 @@ interface IConsole { warn: LogType; } -// console is available on both node.js and browser contexts but the common -// module doesn't depend on them so we need to explicitly declare it. +/* + * console is available on both node.js and browser contexts but the common + * module doesn't depend on them so we need to explicitly declare it. + */ declare const console: IConsole; const optionsKeyToLogLevel: { [key: string]: LogLevelEnum } = { diff --git a/src/common/services/MouseStateService.ts b/src/common/services/MouseStateService.ts index 21420460a..6316dbfc9 100644 --- a/src/common/services/MouseStateService.ts +++ b/src/common/services/MouseStateService.ts @@ -105,8 +105,10 @@ function eventCode(e: ICoreMouseEvent, isSGR: boolean): number { if (e.action === CoreMouseAction.MOVE) { code |= CoreMouseAction.MOVE; } else if (e.action === CoreMouseAction.UP && !isSGR) { - // special case - only SGR can report button on release - // all others have to go with NONE + /* + * special case - only SGR can report button on release + * all others have to go with NONE + */ code |= CoreMouseButton.NONE; } } @@ -126,10 +128,12 @@ const DEFAULT_ENCODINGS: { [key: string]: CoreMouseEncoding } = { */ DEFAULT: (e: ICoreMouseEvent) => { const params = [eventCode(e, false) + 32, e.col + 32, e.row + 32]; - // supress mouse report if we exceed addressible range - // Note this is handled differently by emulators - // - xterm: sends 0;0 coords instead - // - vte, konsole: no report + /* + * supress mouse report if we exceed addressible range + * Note this is handled differently by emulators + * - xterm: sends 0;0 coords instead + * - vte, konsole: no report + */ if (params[0] > 255 || params[1] > 255 || params[2] > 255) { return ''; } diff --git a/src/common/services/OptionsService.ts b/src/common/services/OptionsService.ts index ec647aaa5..252b38ef9 100644 --- a/src/common/services/OptionsService.ts +++ b/src/common/services/OptionsService.ts @@ -90,8 +90,10 @@ export class OptionsService extends Disposable implements IOptionsService { this.options = { ... defaultOptions }; this._setupOptions(); - // Clear out options that could link outside xterm.js as they could easily cause an embedder - // memory leak + /* + * Clear out options that could link outside xterm.js as they could easily cause an embedder + * memory leak + */ this._register(toDisposable(() => { this.rawOptions.linkHandler = null; this.rawOptions.documentOverride = null; diff --git a/src/common/services/ServiceRegistry.ts b/src/common/services/ServiceRegistry.ts index af51d1641..fef07d54c 100644 --- a/src/common/services/ServiceRegistry.ts +++ b/src/common/services/ServiceRegistry.ts @@ -4,10 +4,12 @@ * * This was heavily inspired from microsoft/vscode's dependency injection system (MIT). */ -/*--------------------------------------------------------------------------------------------- +/* + *--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ + *-------------------------------------------------------------------------------------------- + */ import { IServiceIdentifier } from 'common/services/Services'; diff --git a/src/common/services/UnicodeService.ts b/src/common/services/UnicodeService.ts index d0dd8584d..a04bf00a1 100644 --- a/src/common/services/UnicodeService.ts +++ b/src/common/services/UnicodeService.ts @@ -78,16 +78,20 @@ export class UnicodeService implements IUnicodeService { // surrogate pair first if (0xD800 <= code && code <= 0xDBFF) { if (++i >= length) { - // this should not happen with strings retrieved from - // Buffer.translateToString as it converts from UTF-32 - // and therefore always should contain the second part - // for any other string we still have to handle it somehow: - // simply treat the lonely surrogate first as a single char (UCS-2 behavior) + /* + * this should not happen with strings retrieved from + * Buffer.translateToString as it converts from UTF-32 + * and therefore always should contain the second part + * for any other string we still have to handle it somehow: + * simply treat the lonely surrogate first as a single char (UCS-2 behavior) + */ return result + this.wcwidth(code); } const second = s.charCodeAt(i); - // convert surrogate pair to high codepoint only for valid second part (UTF-16) - // otherwise treat them independently (UCS-2 behavior) + /* + * convert surrogate pair to high codepoint only for valid second part (UTF-16) + * otherwise treat them independently (UCS-2 behavior) + */ if (0xDC00 <= second && second <= 0xDFFF) { code = (code - 0xD800) * 0x400 + second - 0xDC00 + 0x10000; } else { diff --git a/src/headless/public/Terminal.ts b/src/headless/public/Terminal.ts index 0d4e6e93b..d9d604917 100644 --- a/src/headless/public/Terminal.ts +++ b/src/headless/public/Terminal.ts @@ -58,9 +58,11 @@ export class Terminal extends Disposable implements ITerminalApi { } private _checkReadonlyOptions(propName: string): void { - // Throw an error if any constructor only option is modified - // from terminal.options - // Modifications from anywhere else are allowed + /* + * Throw an error if any constructor only option is modified + * from terminal.options + * Modifications from anywhere else are allowed + */ if (CONSTRUCTOR_ONLY_OPTIONS.includes(propName)) { throw new Error(`Option "${propName}" can only be set in the constructor`); } diff --git a/test/playwright/InputHandler.test.ts b/test/playwright/InputHandler.test.ts index eec2938a8..451346f3a 100644 --- a/test/playwright/InputHandler.test.ts +++ b/test/playwright/InputHandler.test.ts @@ -227,9 +227,11 @@ test.describe('InputHandler Integration Tests', () => { await ctx.proxy.write('\x1b[2S'); await pollFor(ctx.page, () => getLinesAsArray(5), ['3', '4', '5', '', '']); }); - // This is intentionally not implemented here are image support lives within an addon - // test.skip('CSI ? Pi ; Pa ; Pv S - XTSMGRAPHICS: Set or request graphics attribute, xterm', async () => { - // }); + /* + * This is intentionally not implemented here are image support lives within an addon + * test.skip('CSI ? Pi ; Pa ; Pv S - XTSMGRAPHICS: Set or request graphics attribute, xterm', async () => { + * }); + */ test('CSI Ps T - SD: Scroll down Ps lines (default = 1), VT420', async () => { await ctx.proxy.write('1\r\n2\r\n3\r\n4\r\n5'); await pollFor(ctx.page, () => getLinesAsArray(5), ['1', '2', '3', '4', '5']); @@ -349,8 +351,10 @@ test.describe('InputHandler Integration Tests', () => { await pollFor(ctx.page, () => getLinesAsArray(3), ['aoo', '', ' b']); }); test('CSI Ps g - TBC: Tab Clear (default = 0)', async () => { - // Default: Clear tab stop at cursor position - // Move to column 9 (first tab stop), clear it, go back to column 1, tab should skip to column 17 + /* + * Default: Clear tab stop at cursor position + * Move to column 9 (first tab stop), clear it, go back to column 1, tab should skip to column 17 + */ await ctx.proxy.write('\x1b[9G\x1b[g\x1b[1G\ta'); await pollFor(ctx.page, () => getLinesAsArray(1), [' a']); // Ps=3: Clear all tab stops @@ -523,8 +527,10 @@ test.describe('InputHandler Integration Tests', () => { test('Ps = 6 9 - Enable left and right margin mode (DECLRMM), VT420 and up', async () => { await assertNoModeChange('\x1b[?69h'); }); - // test.skip('Ps = 8 0 - Enable Sixel Display Mode (DECSDM), VT330, VT340, VT382', async () => { - // }); + /* + * test.skip('Ps = 8 0 - Enable Sixel Display Mode (DECSDM), VT330, VT340, VT382', async () => { + * }); + */ test('Ps = 9 5 - Do not clear screen when DECCOLM is set/reset (DECNCSM), VT510 and up', async () => { await assertNoModeChange('\x1b[?95h'); }); @@ -881,9 +887,11 @@ test.describe('InputHandler Integration Tests', () => { test('Ps = 6 9 - Disable left and right margin mode (DECLRMM), VT420 and up.', async () => { await assertNoModeChange('\x1b[?69l'); }); - // This is intentionally not implemented here are image support lives within an addon - // test.skip('Ps = 8 0 - Disable Sixel Display Mode (DECSDM), VT330, VT340, VT382. Turns on "Sixel Scrolling". See the section Sixel Graphics and mode 8 4 5 2 .', async () => { - // }); + /* + * This is intentionally not implemented here are image support lives within an addon + * test.skip('Ps = 8 0 - Disable Sixel Display Mode (DECSDM), VT330, VT340, VT382. Turns on "Sixel Scrolling". See the section Sixel Graphics and mode 8 4 5 2 .', async () => { + * }); + */ test('Ps = 9 5 - Clear screen when DECCOLM is set/reset (DECNCSM), VT510 and up.', async () => { await assertNoModeChange('\x1b[?95l'); }); diff --git a/test/playwright/MouseTracking.test.ts b/test/playwright/MouseTracking.test.ts index 355bac3ba..fc3a112e5 100644 --- a/test/playwright/MouseTracking.test.ts +++ b/test/playwright/MouseTracking.test.ts @@ -12,13 +12,17 @@ test.beforeAll(async ({ browser }) => { }); test.afterAll(async () => await ctx.page.close()); -// adjusted to work inside devcontainer -// see https://github.com/xtermjs/xterm.js/issues/2379 +/* + * adjusted to work inside devcontainer + * see https://github.com/xtermjs/xterm.js/issues/2379 + */ const width = 1280; const height = 960; -// adjust terminal row/col size so we can test -// >80 up to 223 and >255 +/* + * adjust terminal row/col size so we can test + * >80 up to 223 and >255 + */ const fontSize = 6; const cols = 260; const rows = 50; @@ -40,9 +44,11 @@ async function getReports(encoding: string): Promise { return reports.map((report: number[]) => parseReport(encoding, report)); } -// translate cell positions into pixel offset -// always adds +2 in each direction so we dont end up in the wrong cell -// due to rounding issues +/* + * translate cell positions into pixel offset + * always adds +2 in each direction so we dont end up in the wrong cell + * due to rounding issues + */ async function cellPos(col: number, row: number): Promise { const coords: any = await ctx.page.evaluate(` (function() { @@ -165,10 +171,12 @@ function parseReport(encoding: string, msg: number[]): { state: any, row: number test.describe('Mouse Tracking Tests', () => { test.beforeAll(async () => { await ctx.page.setViewportSize({ width, height }); - // patch terminal to get the onData calls - // we encode the msg here to an array of codes to not lose bytes - // (transmission strips non utf8 bytes) - // also resize so we can properly test the edge cases + /* + * patch terminal to get the onData calls + * we encode the msg here to an array of codes to not lose bytes + * (transmission strips non utf8 bytes) + * also resize so we can properly test the edge cases + */ await ctx.page.evaluate(` window.term.onData(e => window.calls.push( Array.from(e).map(el => el.charCodeAt(0)) )); window.term.onBinary(e => window.calls.push( Array.from(e).map(el => el.charCodeAt(0)) )); @@ -214,8 +222,10 @@ test.describe('Mouse Tracking Tests', () => { await mouseUp('left'); await pollFor(ctx.page, () => getReports(encoding), [{ col: 51, row: 11, state: { action: 'press', button: 'left', modifier: { control: false, shift: false, meta: false } } }]); - // test at max rows/cols - // capped at 223 (1-based) + /* + * test at max rows/cols + * capped at 223 (1-based) + */ await mouseMove(223 - 1, rows - 1); await mouseDown('left'); await mouseUp('left'); @@ -227,24 +237,28 @@ test.describe('Mouse Tracking Tests', () => { await mouseUp('left'); await pollFor(ctx.page, () => getReports(encoding), []); - // button press/move/release tests - // left button + /* + * button press/move/release tests + * left button + */ await mouseMove(43, 24); await getReports(encoding); // clear reports await mouseDown('left'); await mouseMove(44, 24); await mouseUp('left'); await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'press', button: 'left', modifier: { control: false, shift: false, meta: false } } }]); - // middle button - // bug: default action not cancelled (adds data to getReports from clipboard under X11) - // await mouseMove(43, 24); - // await getReports(encoding); // clear reports - // await mouseDown('middle'); - // await mouseMove(44, 24); - // await mouseUp('middle'); - // await pollFor(ctx.page, () => getReports(encoding), [{col: 44, row: 25, state: {action: 'press', button: 'middle', modifier: {control: false, shift: false, meta: false}}}]); - // right button - // bug: default action not cancelled (popup shown) + /* + * middle button + * bug: default action not cancelled (adds data to getReports from clipboard under X11) + * await mouseMove(43, 24); + * await getReports(encoding); // clear reports + * await mouseDown('middle'); + * await mouseMove(44, 24); + * await mouseUp('middle'); + * await pollFor(ctx.page, () => getReports(encoding), [{col: 44, row: 25, state: {action: 'press', button: 'middle', modifier: {control: false, shift: false, meta: false}}}]); + * right button + * bug: default action not cancelled (popup shown) + */ await mouseMove(43, 24); await getReports(encoding); // clear reports await mouseDown('right'); @@ -252,16 +266,20 @@ test.describe('Mouse Tracking Tests', () => { await mouseUp('right'); await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'press', button: 'right', modifier: { control: false, shift: false, meta: false } } }]); - // wheel - // await mouseMove(43, 24); - // await getReports(encoding); // clear reports - // await wheelUp(); - // await pollFor(ctx.page, () => getReports(encoding), []); - // await wheelDown(); - // await pollFor(ctx.page, () => getReports(encoding), []); + /* + * wheel + * await mouseMove(43, 24); + * await getReports(encoding); // clear reports + * await wheelUp(); + * await pollFor(ctx.page, () => getReports(encoding), []); + * await wheelDown(); + * await pollFor(ctx.page, () => getReports(encoding), []); + */ - // modifiers - // CTRL + /* + * modifiers + * CTRL + */ await mouseMove(43, 24); await getReports(encoding); // clear reports await ctx.page.keyboard.down('Control'); @@ -284,10 +302,12 @@ test.describe('Mouse Tracking Tests', () => { await ctx.page.keyboard.up('Alt'); await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'press', button: 'left', modifier: { control: false, shift: false, meta: false } } }]); - // SHIFT - // note: caught by selection manager - // bug? Why not caught by selection manger on macos? - // bug: no modifier reported + /* + * SHIFT + * note: caught by selection manager + * bug? Why not caught by selection manger on macos? + * bug: no modifier reported + */ await mouseMove(43, 24); await getReports(encoding); // clear reports await ctx.page.keyboard.down('Shift'); // defaults to ShiftLeft @@ -304,8 +324,10 @@ test.describe('Mouse Tracking Tests', () => { ]); } - // all modifiers - // bug: Shift not working - reporting totally wrong coords and modifiers - selection manager again? + /* + * all modifiers + * bug: Shift not working - reporting totally wrong coords and modifiers - selection manager again? + */ await mouseMove(43, 24); await getReports(encoding); // clear reports await ctx.page.keyboard.down('Control'); @@ -351,24 +373,28 @@ test.describe('Mouse Tracking Tests', () => { await mouseUp('left'); await pollFor(ctx.page, () => getReports(encoding), [{ col: cols, row: rows, state: { action: 'press', button: 'left', modifier: { control: false, shift: false, meta: false } } }]); - // button press/move/release tests - // left button + /* + * button press/move/release tests + * left button + */ await mouseMove(43, 24); await getReports(encoding); // clear reports await mouseDown('left'); await mouseMove(44, 24); await mouseUp('left'); await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'press', button: 'left', modifier: { control: false, shift: false, meta: false } } }]); - // middle button - // bug: default action not cancelled (adds data to getReports from clipboard under X11) - // await mouseMove(43, 24); - // await getReports(encoding); // clear reports - // await mouseDown('middle'); - // await mouseMove(44, 24); - // await mouseUp('middle'); - // await pollFor(ctx.page, () => getReports(encoding), [{col: 44, row: 25, state: {action: 'press', button: 'middle', modifier: {control: false, shift: false, meta: false}}}]); - // right button - // bug: default action not cancelled (popup shown) + /* + * middle button + * bug: default action not cancelled (adds data to getReports from clipboard under X11) + * await mouseMove(43, 24); + * await getReports(encoding); // clear reports + * await mouseDown('middle'); + * await mouseMove(44, 24); + * await mouseUp('middle'); + * await pollFor(ctx.page, () => getReports(encoding), [{col: 44, row: 25, state: {action: 'press', button: 'middle', modifier: {control: false, shift: false, meta: false}}}]); + * right button + * bug: default action not cancelled (popup shown) + */ await mouseMove(43, 24); await getReports(encoding); // clear reports await mouseDown('right'); @@ -376,16 +402,20 @@ test.describe('Mouse Tracking Tests', () => { await mouseUp('right'); await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'press', button: 'right', modifier: { control: false, shift: false, meta: false } } }]); - // wheel - // await mouseMove(43, 24); - // await getReports(encoding); // clear reports - // await wheelUp(); - // await pollFor(ctx.page, () => getReports(encoding), []); - // await wheelDown(); - // await pollFor(ctx.page, () => getReports(encoding), []); + /* + * wheel + * await mouseMove(43, 24); + * await getReports(encoding); // clear reports + * await wheelUp(); + * await pollFor(ctx.page, () => getReports(encoding), []); + * await wheelDown(); + * await pollFor(ctx.page, () => getReports(encoding), []); + */ - // modifiers - // CTRL + /* + * modifiers + * CTRL + */ await mouseMove(43, 24); await getReports(encoding); // clear reports await ctx.page.keyboard.down('Control'); @@ -408,8 +438,10 @@ test.describe('Mouse Tracking Tests', () => { await ctx.page.keyboard.up('Alt'); await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'press', button: 'left', modifier: { control: false, shift: false, meta: false } } }]); - // SHIFT - // note: caught by selection manager + /* + * SHIFT + * note: caught by selection manager + */ await mouseMove(43, 24); await getReports(encoding); // clear reports await ctx.page.keyboard.down('Shift'); // defaults to ShiftLeft @@ -426,8 +458,10 @@ test.describe('Mouse Tracking Tests', () => { ]); } - // all modifiers - // bug: Shift not working - reporting totally wrong coords and modifiers - selection manager again? + /* + * all modifiers + * bug: Shift not working - reporting totally wrong coords and modifiers - selection manager again? + */ await mouseMove(43, 24); await getReports(encoding); // clear reports await ctx.page.keyboard.down('Control'); @@ -483,8 +517,10 @@ test.describe('Mouse Tracking Tests', () => { { col: 51, row: 11, state: { action: 'release', button: '', modifier: { control: false, shift: false, meta: false } } } ]); - // test at max rows/cols - // capped at 223 (1-based) + /* + * test at max rows/cols + * capped at 223 (1-based) + */ await mouseMove(223 - 1, rows - 1); await mouseDown('left'); await mouseUp('left'); @@ -493,8 +529,10 @@ test.describe('Mouse Tracking Tests', () => { { col: 223, row: rows, state: { action: 'release', button: '', modifier: { control: false, shift: false, meta: false } } } ]); - // button press/move/release tests - // left button + /* + * button press/move/release tests + * left button + */ await mouseMove(43, 24); await getReports(encoding); // clear reports await mouseDown('left'); @@ -504,16 +542,18 @@ test.describe('Mouse Tracking Tests', () => { { col: 44, row: 25, state: { action: 'press', button: 'left', modifier: { control: false, shift: false, meta: false } } }, { col: 45, row: 25, state: { action: 'release', button: '', modifier: { control: false, shift: false, meta: false } } } ]); - // middle button - // bug: default action not cancelled (adds data to getReports from clipboard under X11) - // await mouseMove(43, 24); - // await getReports(encoding); // clear reports - // await mouseDown('middle'); - // await mouseMove(44, 24); - // await mouseUp('middle'); - // await pollFor(ctx.page, () => getReports(encoding), [{col: 44, row: 25, state: {action: 'press', button: 'middle', modifier: {control: false, shift: false, meta: false}}}]); - // right button - // bug: default action not cancelled (popup shown) + /* + * middle button + * bug: default action not cancelled (adds data to getReports from clipboard under X11) + * await mouseMove(43, 24); + * await getReports(encoding); // clear reports + * await mouseDown('middle'); + * await mouseMove(44, 24); + * await mouseUp('middle'); + * await pollFor(ctx.page, () => getReports(encoding), [{col: 44, row: 25, state: {action: 'press', button: 'middle', modifier: {control: false, shift: false, meta: false}}}]); + * right button + * bug: default action not cancelled (popup shown) + */ await mouseMove(43, 24); await getReports(encoding); // clear reports await mouseDown('right'); @@ -524,16 +564,20 @@ test.describe('Mouse Tracking Tests', () => { { col: 45, row: 25, state: { action: 'release', button: '', modifier: { control: false, shift: false, meta: false } } } ]); - // wheel - // await mouseMove(43, 24); - // await getReports(encoding); // clear reports - // await wheelUp(); - // await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'up', button: 'wheel', modifier: { control: false, shift: false, meta: false } } }]); - // await wheelDown(); - // await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: false, meta: false } } }]); + /* + * wheel + * await mouseMove(43, 24); + * await getReports(encoding); // clear reports + * await wheelUp(); + * await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'up', button: 'wheel', modifier: { control: false, shift: false, meta: false } } }]); + * await wheelDown(); + * await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: false, meta: false } } }]); + */ - // modifiers - // CTRL + /* + * modifiers + * CTRL + */ await mouseMove(43, 24); await getReports(encoding); // clear reports await ctx.page.keyboard.down('Control'); @@ -563,31 +607,35 @@ test.describe('Mouse Tracking Tests', () => { // { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: false, meta: true } } } ]); - // SHIFT - // note: press/release caught by selection manager - // bug: modifier not reported for passed events - // await mouseMove(43, 24); - // await getReports(encoding); // clear reports - // await ctx.page.keyboard.down('Shift'); // defaults to ShiftLeft - // await mouseDown('left'); - // await mouseMove(44, 24); - // await mouseUp('left'); - // // await wheelDown(); - // await ctx.page.keyboard.up('Shift'); - // // if (noShift) { - // // await pollFor(ctx.page, () => getReports(encoding), [ - // // // { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: true, meta: false } } } - // // ]); - // // } else { - // await pollFor(ctx.page, () => getReports(encoding), [ - // { col: 44, row: 25, state: { action: 'press', button: 'left', modifier: { control: false, shift: true, meta: false } } }, - // { col: 45, row: 25, state: { action: 'release', button: '', modifier: { control: false, shift: true, meta: false } } }, - // // { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: true, meta: false } } } - // ]); - // // } + /* + * SHIFT + * note: press/release caught by selection manager + * bug: modifier not reported for passed events + * await mouseMove(43, 24); + * await getReports(encoding); // clear reports + * await ctx.page.keyboard.down('Shift'); // defaults to ShiftLeft + * await mouseDown('left'); + * await mouseMove(44, 24); + * await mouseUp('left'); + * // await wheelDown(); + * await ctx.page.keyboard.up('Shift'); + * // if (noShift) { + * // await pollFor(ctx.page, () => getReports(encoding), [ + * // // { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: true, meta: false } } } + * // ]); + * // } else { + * await pollFor(ctx.page, () => getReports(encoding), [ + * { col: 44, row: 25, state: { action: 'press', button: 'left', modifier: { control: false, shift: true, meta: false } } }, + * { col: 45, row: 25, state: { action: 'release', button: '', modifier: { control: false, shift: true, meta: false } } }, + * // { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: true, meta: false } } } + * ]); + * // } + */ - // all modifiers - // bug: Shift not working - selection manager? + /* + * all modifiers + * bug: Shift not working - selection manager? + */ await mouseMove(43, 24); await getReports(encoding); // clear reports await ctx.page.keyboard.down('Control'); @@ -647,8 +695,10 @@ test.describe('Mouse Tracking Tests', () => { { col: cols, row: rows, state: { action: 'release', button: 'left', modifier: { control: false, shift: false, meta: false } } } ]); - // button press/move/release tests - // left button + /* + * button press/move/release tests + * left button + */ await mouseMove(43, 24); await getReports(encoding); // clear reports await mouseDown('left'); @@ -658,16 +708,18 @@ test.describe('Mouse Tracking Tests', () => { { col: 44, row: 25, state: { action: 'press', button: 'left', modifier: { control: false, shift: false, meta: false } } }, { col: 45, row: 25, state: { action: 'release', button: 'left', modifier: { control: false, shift: false, meta: false } } } ]); - // middle button - // bug: default action not cancelled (adds data to getReports from clipboard under X11) - // await mouseMove(43, 24); - // await getReports(encoding); // clear reports - // await mouseDown('middle'); - // await mouseMove(44, 24); - // await mouseUp('middle'); - // await pollFor(ctx.page, () => getReports(encoding), [{col: 44, row: 25, state: {action: 'press', button: 'middle', modifier: {control: false, shift: false, meta: false}}}]); - // right button - // bug: default action not cancelled (popup shown) + /* + * middle button + * bug: default action not cancelled (adds data to getReports from clipboard under X11) + * await mouseMove(43, 24); + * await getReports(encoding); // clear reports + * await mouseDown('middle'); + * await mouseMove(44, 24); + * await mouseUp('middle'); + * await pollFor(ctx.page, () => getReports(encoding), [{col: 44, row: 25, state: {action: 'press', button: 'middle', modifier: {control: false, shift: false, meta: false}}}]); + * right button + * bug: default action not cancelled (popup shown) + */ await mouseMove(43, 24); await getReports(encoding); // clear reports await mouseDown('right'); @@ -678,16 +730,20 @@ test.describe('Mouse Tracking Tests', () => { { col: 45, row: 25, state: { action: 'release', button: 'right', modifier: { control: false, shift: false, meta: false } } } ]); - // wheel - // await mouseMove(43, 24); - // await getReports(encoding); // clear reports - // await wheelUp(); - // await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'up', button: 'wheel', modifier: { control: false, shift: false, meta: false } } }]); - // await wheelDown(); - // await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: false, meta: false } } }]); + /* + * wheel + * await mouseMove(43, 24); + * await getReports(encoding); // clear reports + * await wheelUp(); + * await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'up', button: 'wheel', modifier: { control: false, shift: false, meta: false } } }]); + * await wheelDown(); + * await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: false, meta: false } } }]); + */ - // modifiers - // CTRL + /* + * modifiers + * CTRL + */ await mouseMove(43, 24); await getReports(encoding); // clear reports await ctx.page.keyboard.down('Control'); @@ -717,30 +773,34 @@ test.describe('Mouse Tracking Tests', () => { // { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: false, meta: true } } } ]); - // SHIFT - // note: press/release caught by selection manager - // await mouseMove(43, 24); - // await getReports(encoding); // clear reports - // await ctx.page.keyboard.down('Shift'); // defaults to ShiftLeft - // await mouseDown('left'); - // await mouseMove(44, 24); - // await mouseUp('left'); - // await wheelDown(); - // await ctx.page.keyboard.up('Shift'); - // if (noShift) { - // await pollFor(ctx.page, () => getReports(encoding), [ - // { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: true, meta: false } } } - // ]); - // } else { - // await pollFor(ctx.page, () => getReports(encoding), [ - // { col: 44, row: 25, state: { action: 'press', button: 'left', modifier: { control: false, shift: true, meta: false } } }, - // { col: 45, row: 25, state: { action: 'release', button: 'left', modifier: { control: false, shift: true, meta: false } } }, - // { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: true, meta: false } } } - // ]); - // } + /* + * SHIFT + * note: press/release caught by selection manager + * await mouseMove(43, 24); + * await getReports(encoding); // clear reports + * await ctx.page.keyboard.down('Shift'); // defaults to ShiftLeft + * await mouseDown('left'); + * await mouseMove(44, 24); + * await mouseUp('left'); + * await wheelDown(); + * await ctx.page.keyboard.up('Shift'); + * if (noShift) { + * await pollFor(ctx.page, () => getReports(encoding), [ + * { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: true, meta: false } } } + * ]); + * } else { + * await pollFor(ctx.page, () => getReports(encoding), [ + * { col: 44, row: 25, state: { action: 'press', button: 'left', modifier: { control: false, shift: true, meta: false } } }, + * { col: 45, row: 25, state: { action: 'release', button: 'left', modifier: { control: false, shift: true, meta: false } } }, + * { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: true, meta: false } } } + * ]); + * } + */ - // all modifiers - // bug: Shift not working - selection manager? + /* + * all modifiers + * bug: Shift not working - selection manager? + */ await mouseMove(43, 24); await getReports(encoding); // clear reports await ctx.page.keyboard.down('Control'); @@ -800,8 +860,10 @@ test.describe('Mouse Tracking Tests', () => { { col: 51, row: 11, state: { action: 'release', button: '', modifier: { control: false, shift: false, meta: false } } } ]); - // test at max rows/cols - // capped at 223 (1-based) + /* + * test at max rows/cols + * capped at 223 (1-based) + */ await mouseMove(223 - 1, rows - 1); await mouseDown('left'); await mouseUp('left'); @@ -810,8 +872,10 @@ test.describe('Mouse Tracking Tests', () => { { col: 223, row: rows, state: { action: 'release', button: '', modifier: { control: false, shift: false, meta: false } } } ]); - // button press/move/release tests - // left button + /* + * button press/move/release tests + * left button + */ await mouseMove(43, 24); await getReports(encoding); // clear reports await mouseDown('left'); @@ -822,16 +886,18 @@ test.describe('Mouse Tracking Tests', () => { { col: 45, row: 25, state: { action: 'move', button: 'left', modifier: { control: false, shift: false, meta: false } } }, { col: 45, row: 25, state: { action: 'release', button: '', modifier: { control: false, shift: false, meta: false } } } ]); - // middle button - // bug: default action not cancelled (adds data to getReports from clipboard under X11) - // await mouseMove(43, 24); - // await getReports(encoding); // clear reports - // await mouseDown('middle'); - // await mouseMove(44, 24); - // await mouseUp('middle'); - // await pollFor(ctx.page, () => getReports(encoding), [{col: 44, row: 25, state: {action: 'press', button: 'middle', modifier: {control: false, shift: false, meta: false}}}]); - // right button - // bug: default action not cancelled (popup shown) + /* + * middle button + * bug: default action not cancelled (adds data to getReports from clipboard under X11) + * await mouseMove(43, 24); + * await getReports(encoding); // clear reports + * await mouseDown('middle'); + * await mouseMove(44, 24); + * await mouseUp('middle'); + * await pollFor(ctx.page, () => getReports(encoding), [{col: 44, row: 25, state: {action: 'press', button: 'middle', modifier: {control: false, shift: false, meta: false}}}]); + * right button + * bug: default action not cancelled (popup shown) + */ await mouseMove(43, 24); await getReports(encoding); // clear reports await mouseDown('right'); @@ -843,16 +909,20 @@ test.describe('Mouse Tracking Tests', () => { { col: 45, row: 25, state: { action: 'release', button: '', modifier: { control: false, shift: false, meta: false } } } ]); - // wheel - // await mouseMove(43, 24); - // await getReports(encoding); // clear reports - // await wheelUp(); - // await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'up', button: 'wheel', modifier: { control: false, shift: false, meta: false } } }]); - // await wheelDown(); - // await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: false, meta: false } } }]); + /* + * wheel + * await mouseMove(43, 24); + * await getReports(encoding); // clear reports + * await wheelUp(); + * await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'up', button: 'wheel', modifier: { control: false, shift: false, meta: false } } }]); + * await wheelDown(); + * await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: false, meta: false } } }]); + */ - // modifiers - // CTRL + /* + * modifiers + * CTRL + */ await mouseMove(43, 24); await getReports(encoding); // clear reports await ctx.page.keyboard.down('Control'); @@ -884,31 +954,35 @@ test.describe('Mouse Tracking Tests', () => { // { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: false, meta: true } } } ]); - // SHIFT - // note: press/release/drag caught by selection manager - // await mouseMove(43, 24); - // await getReports(encoding); // clear reports - // await ctx.page.keyboard.down('Shift'); // defaults to ShiftLeft - // await mouseDown('left'); - // await mouseMove(44, 24); - // await mouseUp('left'); - // await wheelDown(); - // await ctx.page.keyboard.up('Shift'); - // if (noShift) { - // await pollFor(ctx.page, () => getReports(encoding), [ - // { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: true, meta: false } } } - // ]); - // } else { - // await pollFor(ctx.page, () => getReports(encoding), [ - // { col: 44, row: 25, state: { action: 'press', button: 'left', modifier: { control: false, shift: true, meta: false } } }, - // { col: 45, row: 25, state: { action: 'move', button: 'left', modifier: { control: false, shift: true, meta: false } } }, - // { col: 45, row: 25, state: { action: 'release', button: '', modifier: { control: false, shift: true, meta: false } } }, - // { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: true, meta: false } } } - // ]); - // } + /* + * SHIFT + * note: press/release/drag caught by selection manager + * await mouseMove(43, 24); + * await getReports(encoding); // clear reports + * await ctx.page.keyboard.down('Shift'); // defaults to ShiftLeft + * await mouseDown('left'); + * await mouseMove(44, 24); + * await mouseUp('left'); + * await wheelDown(); + * await ctx.page.keyboard.up('Shift'); + * if (noShift) { + * await pollFor(ctx.page, () => getReports(encoding), [ + * { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: true, meta: false } } } + * ]); + * } else { + * await pollFor(ctx.page, () => getReports(encoding), [ + * { col: 44, row: 25, state: { action: 'press', button: 'left', modifier: { control: false, shift: true, meta: false } } }, + * { col: 45, row: 25, state: { action: 'move', button: 'left', modifier: { control: false, shift: true, meta: false } } }, + * { col: 45, row: 25, state: { action: 'release', button: '', modifier: { control: false, shift: true, meta: false } } }, + * { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: true, meta: false } } } + * ]); + * } + */ - // all modifiers - // bug: Shift not working + /* + * all modifiers + * bug: Shift not working + */ await mouseMove(43, 24); await getReports(encoding); // clear reports await ctx.page.keyboard.down('Control'); @@ -938,8 +1012,10 @@ test.describe('Mouse Tracking Tests', () => { await mouseMove(0, 0); await ctx.proxy.write('\x1b[?1002h\x1b[?1006h'); - // test at 0,0 - // bug: release is fired immediately + /* + * test at 0,0 + * bug: release is fired immediately + */ await mouseDown('left'); await pollFor(ctx.page, () => getReports(encoding), [ { col: 1, row: 1, state: { action: 'press', button: 'left', modifier: { control: false, shift: false, meta: false } } } @@ -970,8 +1046,10 @@ test.describe('Mouse Tracking Tests', () => { { col: cols, row: rows, state: { action: 'release', button: 'left', modifier: { control: false, shift: false, meta: false } } } ]); - // button press/move/release tests - // left button + /* + * button press/move/release tests + * left button + */ await mouseMove(43, 24); await getReports(encoding); // clear reports await mouseDown('left'); @@ -982,16 +1060,18 @@ test.describe('Mouse Tracking Tests', () => { { col: 45, row: 25, state: { action: 'move', button: 'left', modifier: { control: false, shift: false, meta: false } } }, { col: 45, row: 25, state: { action: 'release', button: 'left', modifier: { control: false, shift: false, meta: false } } } ]); - // middle button - // bug: default action not cancelled (adds data to getReports from clipboard under X11) - // await mouseMove(43, 24); - // await getReports(encoding); // clear reports - // await mouseDown('middle'); - // await mouseMove(44, 24); - // await mouseUp('middle'); - // await pollFor(ctx.page, () => getReports(encoding), [{col: 44, row: 25, state: {action: 'press', button: 'middle', modifier: {control: false, shift: false, meta: false}}}]); - // right button - // bug: default action not cancelled (popup shown) + /* + * middle button + * bug: default action not cancelled (adds data to getReports from clipboard under X11) + * await mouseMove(43, 24); + * await getReports(encoding); // clear reports + * await mouseDown('middle'); + * await mouseMove(44, 24); + * await mouseUp('middle'); + * await pollFor(ctx.page, () => getReports(encoding), [{col: 44, row: 25, state: {action: 'press', button: 'middle', modifier: {control: false, shift: false, meta: false}}}]); + * right button + * bug: default action not cancelled (popup shown) + */ await mouseMove(43, 24); await getReports(encoding); // clear reports await mouseDown('right'); @@ -1003,16 +1083,20 @@ test.describe('Mouse Tracking Tests', () => { { col: 45, row: 25, state: { action: 'release', button: 'right', modifier: { control: false, shift: false, meta: false } } } ]); - // wheel - // await mouseMove(43, 24); - // await getReports(encoding); // clear reports - // await wheelUp(); - // await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'up', button: 'wheel', modifier: { control: false, shift: false, meta: false } } }]); - // await wheelDown(); - // await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: false, meta: false } } }]); + /* + * wheel + * await mouseMove(43, 24); + * await getReports(encoding); // clear reports + * await wheelUp(); + * await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'up', button: 'wheel', modifier: { control: false, shift: false, meta: false } } }]); + * await wheelDown(); + * await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: false, meta: false } } }]); + */ - // modifiers - // CTRL + /* + * modifiers + * CTRL + */ await mouseMove(43, 24); await getReports(encoding); // clear reports await ctx.page.keyboard.down('Control'); @@ -1044,31 +1128,35 @@ test.describe('Mouse Tracking Tests', () => { // { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: false, meta: true } } } ]); - // SHIFT - // note: press/release/drag caught by selection manager - // await mouseMove(43, 24); - // await getReports(encoding); // clear reports - // await ctx.page.keyboard.down('Shift'); // defaults to ShiftLeft - // await mouseDown('left'); - // await mouseMove(44, 24); - // await mouseUp('left'); - // await wheelDown(); - // await ctx.page.keyboard.up('Shift'); - // if (noShift) { - // await pollFor(ctx.page, () => getReports(encoding), [ - // { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: true, meta: false } } } - // ]); - // } else { - // await pollFor(ctx.page, () => getReports(encoding), [ - // { col: 44, row: 25, state: { action: 'press', button: 'left', modifier: { control: false, shift: true, meta: false } } }, - // { col: 45, row: 25, state: { action: 'move', button: 'left', modifier: { control: false, shift: true, meta: false } } }, - // { col: 45, row: 25, state: { action: 'release', button: 'left', modifier: { control: false, shift: true, meta: false } } }, - // { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: true, meta: false } } } - // ]); - // } + /* + * SHIFT + * note: press/release/drag caught by selection manager + * await mouseMove(43, 24); + * await getReports(encoding); // clear reports + * await ctx.page.keyboard.down('Shift'); // defaults to ShiftLeft + * await mouseDown('left'); + * await mouseMove(44, 24); + * await mouseUp('left'); + * await wheelDown(); + * await ctx.page.keyboard.up('Shift'); + * if (noShift) { + * await pollFor(ctx.page, () => getReports(encoding), [ + * { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: true, meta: false } } } + * ]); + * } else { + * await pollFor(ctx.page, () => getReports(encoding), [ + * { col: 44, row: 25, state: { action: 'press', button: 'left', modifier: { control: false, shift: true, meta: false } } }, + * { col: 45, row: 25, state: { action: 'move', button: 'left', modifier: { control: false, shift: true, meta: false } } }, + * { col: 45, row: 25, state: { action: 'release', button: 'left', modifier: { control: false, shift: true, meta: false } } }, + * { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: true, meta: false } } } + * ]); + * } + */ - // all modifiers - // bug: this is totally broken with wrong coords and messed up modifiers + /* + * all modifiers + * bug: this is totally broken with wrong coords and messed up modifiers + */ await mouseMove(43, 24); await getReports(encoding); // clear reports await ctx.page.keyboard.down('Control'); @@ -1128,8 +1216,10 @@ test.describe('Mouse Tracking Tests', () => { { col: 51, row: 11, state: { action: 'release', button: '', modifier: { control: false, shift: false, meta: false } } } ]); - // test at max rows/cols - // capped at 223 (1-based) + /* + * test at max rows/cols + * capped at 223 (1-based) + */ await mouseMove(223 - 1, rows - 1); await mouseDown('left'); await mouseUp('left'); @@ -1139,8 +1229,10 @@ test.describe('Mouse Tracking Tests', () => { { col: 223, row: rows, state: { action: 'release', button: '', modifier: { control: false, shift: false, meta: false } } } ]); - // button press/move/release tests - // left button + /* + * button press/move/release tests + * left button + */ await mouseMove(43, 24); await mouseDown('left'); await mouseMove(44, 24); @@ -1151,16 +1243,18 @@ test.describe('Mouse Tracking Tests', () => { { col: 45, row: 25, state: { action: 'move', button: 'left', modifier: { control: false, shift: false, meta: false } } }, { col: 45, row: 25, state: { action: 'release', button: '', modifier: { control: false, shift: false, meta: false } } } ]); - // middle button - // bug: default action not cancelled (adds data to getReports from clipboard under X11) - // await mouseMove(43, 24); - // await getReports(encoding); // clear reports - // await mouseDown('middle'); - // await mouseMove(44, 24); - // await mouseUp('middle'); - // await pollFor(ctx.page, () => getReports(encoding), [{col: 44, row: 25, state: {action: 'press', button: 'middle', modifier: {control: false, shift: false, meta: false}}}]); - // right button - // bug: default action not cancelled (popup shown) + /* + * middle button + * bug: default action not cancelled (adds data to getReports from clipboard under X11) + * await mouseMove(43, 24); + * await getReports(encoding); // clear reports + * await mouseDown('middle'); + * await mouseMove(44, 24); + * await mouseUp('middle'); + * await pollFor(ctx.page, () => getReports(encoding), [{col: 44, row: 25, state: {action: 'press', button: 'middle', modifier: {control: false, shift: false, meta: false}}}]); + * right button + * bug: default action not cancelled (popup shown) + */ await mouseMove(43, 24); await mouseDown('right'); await mouseMove(44, 24); @@ -1172,16 +1266,20 @@ test.describe('Mouse Tracking Tests', () => { { col: 45, row: 25, state: { action: 'release', button: '', modifier: { control: false, shift: false, meta: false } } } ]); - // wheel - // await mouseMove(43, 24); - // await getReports(encoding); // clear reports - // await wheelUp(); - // await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'up', button: 'wheel', modifier: { control: false, shift: false, meta: false } } }]); - // await wheelDown(); - // await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: false, meta: false } } }]); + /* + * wheel + * await mouseMove(43, 24); + * await getReports(encoding); // clear reports + * await wheelUp(); + * await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'up', button: 'wheel', modifier: { control: false, shift: false, meta: false } } }]); + * await wheelDown(); + * await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: false, meta: false } } }]); + */ - // modifiers - // CTRL + /* + * modifiers + * CTRL + */ await ctx.page.keyboard.down('Control'); await mouseMove(43, 24); await mouseDown('left'); @@ -1213,32 +1311,36 @@ test.describe('Mouse Tracking Tests', () => { // { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: false, meta: true } } } ]); - // SHIFT - // note: press/release/drag caught by selection manager - // await ctx.page.keyboard.down('Shift'); - // await mouseMove(43, 24); - // await mouseDown('left'); - // await mouseMove(44, 24); - // await mouseUp('left'); - // await wheelDown(); - // await ctx.page.keyboard.up('Shift'); - // if (noShift) { - // await pollFor(ctx.page, () => getReports(encoding), [ - // { col: 44, row: 25, state: { action: 'move', button: '', modifier: { control: false, shift: true, meta: false } } }, - // { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: true, meta: false } } } - // ]); - // } else { - // await pollFor(ctx.page, () => getReports(encoding), [ - // { col: 44, row: 25, state: { action: 'move', button: '', modifier: { control: false, shift: true, meta: false } } }, - // { col: 44, row: 25, state: { action: 'press', button: 'left', modifier: { control: false, shift: true, meta: false } } }, - // { col: 45, row: 25, state: { action: 'move', button: 'left', modifier: { control: false, shift: true, meta: false } } }, - // { col: 45, row: 25, state: { action: 'release', button: '', modifier: { control: false, shift: true, meta: false } } }, - // { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: true, meta: false } } } - // ]); - // } + /* + * SHIFT + * note: press/release/drag caught by selection manager + * await ctx.page.keyboard.down('Shift'); + * await mouseMove(43, 24); + * await mouseDown('left'); + * await mouseMove(44, 24); + * await mouseUp('left'); + * await wheelDown(); + * await ctx.page.keyboard.up('Shift'); + * if (noShift) { + * await pollFor(ctx.page, () => getReports(encoding), [ + * { col: 44, row: 25, state: { action: 'move', button: '', modifier: { control: false, shift: true, meta: false } } }, + * { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: true, meta: false } } } + * ]); + * } else { + * await pollFor(ctx.page, () => getReports(encoding), [ + * { col: 44, row: 25, state: { action: 'move', button: '', modifier: { control: false, shift: true, meta: false } } }, + * { col: 44, row: 25, state: { action: 'press', button: 'left', modifier: { control: false, shift: true, meta: false } } }, + * { col: 45, row: 25, state: { action: 'move', button: 'left', modifier: { control: false, shift: true, meta: false } } }, + * { col: 45, row: 25, state: { action: 'release', button: '', modifier: { control: false, shift: true, meta: false } } }, + * { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: true, meta: false } } } + * ]); + * } + */ - // all modifiers - // bug: Shift not working + /* + * all modifiers + * bug: Shift not working + */ await ctx.page.keyboard.down('Control'); await ctx.page.keyboard.down('Alt'); // await ctx.page.keyboard.down('Shift'); @@ -1292,9 +1394,11 @@ test.describe('Mouse Tracking Tests', () => { { col: 51, row: 11, state: { action: 'release', button: 'left', modifier: { control: false, shift: false, meta: false } } } ]); - // test at max rows/cols - // bug: we are capped at col 95 currently - // fix: allow values up to 223, any bigger should drop to 0 + /* + * test at max rows/cols + * bug: we are capped at col 95 currently + * fix: allow values up to 223, any bigger should drop to 0 + */ await mouseMove(cols - 1, rows - 1); await mouseDown('left'); await mouseUp('left'); @@ -1304,8 +1408,10 @@ test.describe('Mouse Tracking Tests', () => { { col: cols, row: rows, state: { action: 'release', button: 'left', modifier: { control: false, shift: false, meta: false } } } ]); - // button press/move/release tests - // left button + /* + * button press/move/release tests + * left button + */ await mouseMove(43, 24); await mouseDown('left'); await mouseMove(44, 24); @@ -1316,16 +1422,18 @@ test.describe('Mouse Tracking Tests', () => { { col: 45, row: 25, state: { action: 'move', button: 'left', modifier: { control: false, shift: false, meta: false } } }, { col: 45, row: 25, state: { action: 'release', button: 'left', modifier: { control: false, shift: false, meta: false } } } ]); - // middle button - // bug: default action not cancelled (adds data to getReports from clipboard under X11) - // await mouseMove(43, 24); - // await getReports(encoding); // clear reports - // await mouseDown('middle'); - // await mouseMove(44, 24); - // await mouseUp('middle'); - // await pollFor(ctx.page, () => getReports(encoding), [{col: 44, row: 25, state: {action: 'press', button: 'middle', modifier: {control: false, shift: false, meta: false}}}]); - // right button - // bug: default action not cancelled (popup shown) + /* + * middle button + * bug: default action not cancelled (adds data to getReports from clipboard under X11) + * await mouseMove(43, 24); + * await getReports(encoding); // clear reports + * await mouseDown('middle'); + * await mouseMove(44, 24); + * await mouseUp('middle'); + * await pollFor(ctx.page, () => getReports(encoding), [{col: 44, row: 25, state: {action: 'press', button: 'middle', modifier: {control: false, shift: false, meta: false}}}]); + * right button + * bug: default action not cancelled (popup shown) + */ await mouseMove(43, 24); await mouseDown('right'); await mouseMove(44, 24); @@ -1337,16 +1445,20 @@ test.describe('Mouse Tracking Tests', () => { { col: 45, row: 25, state: { action: 'release', button: 'right', modifier: { control: false, shift: false, meta: false } } } ]); - // wheel - // await mouseMove(43, 24); - // await getReports(encoding); // clear reports - // await wheelUp(); - // await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'up', button: 'wheel', modifier: { control: false, shift: false, meta: false } } }]); - // await wheelDown(); - // await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: false, meta: false } } }]); + /* + * wheel + * await mouseMove(43, 24); + * await getReports(encoding); // clear reports + * await wheelUp(); + * await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'up', button: 'wheel', modifier: { control: false, shift: false, meta: false } } }]); + * await wheelDown(); + * await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: false, meta: false } } }]); + */ - // modifiers - // CTRL + /* + * modifiers + * CTRL + */ await ctx.page.keyboard.down('Control'); await mouseMove(43, 24); await mouseDown('left'); @@ -1378,32 +1490,36 @@ test.describe('Mouse Tracking Tests', () => { // { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: false, meta: true } } } ]); - // SHIFT - // note: press/release/drag caught by selection manager - // await ctx.page.keyboard.down('Shift'); - // await mouseMove(43, 24); - // await mouseDown('left'); - // await mouseMove(44, 24); - // await mouseUp('left'); - // await wheelDown(); - // await ctx.page.keyboard.up('Shift'); - // if (noShift) { - // await pollFor(ctx.page, () => getReports(encoding), [ - // { col: 44, row: 25, state: { action: 'move', button: '', modifier: { control: false, shift: true, meta: false } } }, - // { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: true, meta: false } } } - // ]); - // } else { - // await pollFor(ctx.page, () => getReports(encoding), [ - // { col: 44, row: 25, state: { action: 'move', button: '', modifier: { control: false, shift: true, meta: false } } }, - // { col: 44, row: 25, state: { action: 'press', button: 'left', modifier: { control: false, shift: true, meta: false } } }, - // { col: 45, row: 25, state: { action: 'move', button: 'left', modifier: { control: false, shift: true, meta: false } } }, - // { col: 45, row: 25, state: { action: 'release', button: 'left', modifier: { control: false, shift: true, meta: false } } }, - // { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: true, meta: false } } } - // ]); - // } + /* + * SHIFT + * note: press/release/drag caught by selection manager + * await ctx.page.keyboard.down('Shift'); + * await mouseMove(43, 24); + * await mouseDown('left'); + * await mouseMove(44, 24); + * await mouseUp('left'); + * await wheelDown(); + * await ctx.page.keyboard.up('Shift'); + * if (noShift) { + * await pollFor(ctx.page, () => getReports(encoding), [ + * { col: 44, row: 25, state: { action: 'move', button: '', modifier: { control: false, shift: true, meta: false } } }, + * { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: true, meta: false } } } + * ]); + * } else { + * await pollFor(ctx.page, () => getReports(encoding), [ + * { col: 44, row: 25, state: { action: 'move', button: '', modifier: { control: false, shift: true, meta: false } } }, + * { col: 44, row: 25, state: { action: 'press', button: 'left', modifier: { control: false, shift: true, meta: false } } }, + * { col: 45, row: 25, state: { action: 'move', button: 'left', modifier: { control: false, shift: true, meta: false } } }, + * { col: 45, row: 25, state: { action: 'release', button: 'left', modifier: { control: false, shift: true, meta: false } } }, + * { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: true, meta: false } } } + * ]); + * } + */ - // all modifiers - // bug: Shift not working + /* + * all modifiers + * bug: Shift not working + */ await ctx.page.keyboard.down('Control'); await ctx.page.keyboard.down('Alt'); // await ctx.page.keyboard.down('Shift'); diff --git a/test/playwright/SharedRendererTests.ts b/test/playwright/SharedRendererTests.ts index db5c4c073..75b330abd 100644 --- a/test/playwright/SharedRendererTests.ts +++ b/test/playwright/SharedRendererTests.ts @@ -378,8 +378,10 @@ export function injectSharedRendererTests(ctx: ISharedRendererTestContext): void const r = parseInt(cssColor.slice(1, 3), 16); const g = parseInt(cssColor.slice(3, 5), 16); const b = parseInt(cssColor.slice(5, 7), 16); - // It's difficult to assert the exact color due to rounding, just ensure the color differs - // to the regular color + /* + * It's difficult to assert the exact color due to rounding, just ensure the color differs + * to the regular color + */ await pollFor(ctx.value.page, async () => { const c = await getCellColor(ctx.value, x + 1, y + 1); return ( @@ -783,9 +785,11 @@ export function injectSharedRendererTests(ctx: ISharedRendererTestContext): void await pollFor(ctx.value.page, () => getCellColor(ctx.value, 6, 2), [0xad, 0x7f, 0xa8, 255]); await pollFor(ctx.value.page, () => getCellColor(ctx.value, 7, 2), [0x34, 0xe2, 0xe2, 255]); await pollFor(ctx.value.page, () => getCellColor(ctx.value, 8, 2), [0xee, 0xee, 0xec, 255]); - // Setting and check for minimum contrast values, note that these are not - // exact to the contrast ratio, if the increase luminance algorithm - // changes then these will probably fail + /* + * Setting and check for minimum contrast values, note that these are not + * exact to the contrast ratio, if the increase luminance algorithm + * changes then these will probably fail + */ await ctx.value.page.evaluate(`window.term.options.minimumContrastRatio = 10;`); frameDetails = undefined; await pollFor(ctx.value.page, () => getCellColor(ctx.value, 1, 1), [176, 180, 180, 255]); @@ -852,9 +856,11 @@ export function injectSharedRendererTests(ctx: ISharedRendererTestContext): void await pollFor(ctx.value.page, () => getCellColor(ctx.value, 6, 2), [0xad, 0x7f, 0xa8, 255]); await pollFor(ctx.value.page, () => getCellColor(ctx.value, 7, 2), [0x34, 0xe2, 0xe2, 255]); await pollFor(ctx.value.page, () => getCellColor(ctx.value, 8, 2), [0xee, 0xee, 0xec, 255]); - // Setting and check for minimum contrast values, note that these are not - // exact to the contrast ratio, if the increase luminance algorithm - // changes then these will probably fail + /* + * Setting and check for minimum contrast values, note that these are not + * exact to the contrast ratio, if the increase luminance algorithm + * changes then these will probably fail + */ await ctx.value.page.evaluate(`window.term.options.minimumContrastRatio = 10;`); frameDetails = undefined; await pollFor(ctx.value.page, () => getCellColor(ctx.value, 1, 1), [46, 52, 54, 255]); @@ -935,9 +941,11 @@ export function injectSharedRendererTests(ctx: ISharedRendererTestContext): void await pollForApproximate(ctx.value.page, marginOfError, () => getFgColor(6, 2), [Math.floor((255 + 0xad) / 2), Math.floor((255 + 0x7f) / 2), Math.floor((255 + 0xa8) / 2), 255]); await pollForApproximate(ctx.value.page, marginOfError, () => getFgColor(7, 2), [Math.floor((255 + 0x34) / 2), Math.floor((255 + 0xe2) / 2), Math.floor((255 + 0xe2) / 2), 255]); await pollForApproximate(ctx.value.page, marginOfError, () => getFgColor(8, 2), [Math.floor((255 + 0xee) / 2), Math.floor((255 + 0xee) / 2), Math.floor((255 + 0xec) / 2), 255]); - // Setting and check for minimum contrast values, note that these are not - // exact to the contrast ratio, if the increase luminance algorithm - // changes then these will probably fail + /* + * Setting and check for minimum contrast values, note that these are not + * exact to the contrast ratio, if the increase luminance algorithm + * changes then these will probably fail + */ await waitForRenderAfter(ctx.value, () => ctx.value.page.evaluate(`window.term.options.minimumContrastRatio = 10;`)); await pollForApproximate(ctx.value.page, marginOfError, () => getFgColor(1, 1), [150, 153, 154, 255]); await pollForApproximate(ctx.value.page, marginOfError, () => getFgColor(2, 1), [229, 127, 127, 255]); diff --git a/test/playwright/Terminal.test.ts b/test/playwright/Terminal.test.ts index 2d1133cce..387197c6d 100644 --- a/test/playwright/Terminal.test.ts +++ b/test/playwright/Terminal.test.ts @@ -837,8 +837,10 @@ test.describe('API Integration Tests', () => { test.describe('registerLinkProvider', () => { test('should fire provideLinks when hovering cells', async () => { await openTerminal(ctx); - // Focus the terminal as the cursor will show and trigger a rerender, which can clear the - // active link + /* + * Focus the terminal as the cursor will show and trigger a rerender, which can clear the + * active link + */ await ctx.proxy.focus(); await ctx.page.evaluate(` window.calls = []; @@ -859,8 +861,10 @@ test.describe('API Integration Tests', () => { test('should fire hover and leave events on the link', async () => { await openTerminal(ctx); - // Focus the terminal as the cursor will show and trigger a rerender, which can clear the - // active link + /* + * Focus the terminal as the cursor will show and trigger a rerender, which can clear the + * active link + */ await ctx.page.evaluate('window.term.focus()'); await ctx.proxy.write('foo bar baz'); // Wait for renderer to catch up as links are cleared on render @@ -897,8 +901,10 @@ test.describe('API Integration Tests', () => { test('should work fine when hover and leave callbacks are not provided', async () => { await openTerminal(ctx); - // Focus the terminal as the cursor will show and trigger a rerender, which can clear the - // active link + /* + * Focus the terminal as the cursor will show and trigger a rerender, which can clear the + * active link + */ await ctx.page.evaluate('window.term.focus()'); await ctx.proxy.write('foo bar baz'); // Wait for renderer to catch up as links are cleared on render @@ -940,8 +946,10 @@ test.describe('API Integration Tests', () => { test('should fire activate events when clicking the link', async () => { await openTerminal(ctx); - // Focus the terminal as the cursor will show and trigger a rerender, which can clear the - // active link + /* + * Focus the terminal as the cursor will show and trigger a rerender, which can clear the + * active link + */ await ctx.page.evaluate('window.term.focus()'); await ctx.proxy.write('a b c'); // Wait for renderer to catch up as links are cleared on render @@ -982,8 +990,10 @@ test.describe('API Integration Tests', () => { test('should work when multiple links are provided on the same line', async () => { await openTerminal(ctx); - // Focus the terminal as the cursor will show and trigger a rerender, which can clear the - // active link + /* + * Focus the terminal as the cursor will show and trigger a rerender, which can clear the + * active link + */ await ctx.page.evaluate('window.term.focus()'); await ctx.proxy.write('foo bar baz'); // Wait for renderer to catch up as links are cleared on render @@ -1031,8 +1041,10 @@ test.describe('API Integration Tests', () => { test('should dispose links when hovering away', async () => { await openTerminal(ctx); - // Focus the terminal as the cursor will show and trigger a rerender, which can clear the - // active link + /* + * Focus the terminal as the cursor will show and trigger a rerender, which can clear the + * active link + */ await ctx.page.evaluate('window.term.focus()'); await ctx.proxy.write('foo bar baz'); // Wait for renderer to catch up as links are cleared on render diff --git a/test/playwright/TestUtils.ts b/test/playwright/TestUtils.ts index 8a76c8820..76a5eb200 100644 --- a/test/playwright/TestUtils.ts +++ b/test/playwright/TestUtils.ts @@ -459,8 +459,10 @@ export async function openTerminal( } catch {} } `); - // HACK: Tests may have side effects that could cause the terminal not to be removed. This - // assertion catches this case early. + /* + * HACK: Tests may have side effects that could cause the terminal not to be removed. This + * assertion catches this case early. + */ strictEqual(await ctx.page.evaluate(`document.querySelector('#terminal-container').children.length`), 0, 'there must be no terminals on the page'); let script = ` @@ -501,8 +503,10 @@ export async function openTerminal( await ctx.page.evaluate(script); - // HACK: This is a soft layer breaker that's temporarily included until unicode graphemes have - // more complete integration tests. See https://github.com/xtermjs/xterm.js/pull/4519#discussion_r1285234453 + /* + * HACK: This is a soft layer breaker that's temporarily included until unicode graphemes have + * more complete integration tests. See https://github.com/xtermjs/xterm.js/pull/4519#discussion_r1285234453 + */ if (testOptions.loadUnicodeGraphemesAddon) { await ctx.page.evaluate(` window.unicode = new UnicodeGraphemesAddon(); From df3422fb0bd193c895d0a25d6e6762d167ca700c Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Tue, 26 May 2026 20:32:23 +0000 Subject: [PATCH 12/47] Use jsdoc/multiline-blocks instead of multiline-comment-style Replace multiline-comment-style (starred-block), which also forces multiline // comments into block comments, with jsdoc/multiline-blocks. That rule only enforces starred JSDoc layout for /** blocks (no text on the opening line) and leaves multiline // comments unchanged for internal documentation. Fix multiline JSDoc in Types.ts. Fixes #5899 Co-authored-by: Daniel Imms --- addons/addon-fit/test/FitAddon.test.ts | 32 +- addons/addon-image/src/IIPHeaderParser.ts | 6 +- addons/addon-image/src/IIPMetrics.ts | 6 +- addons/addon-image/src/ImageAddon.ts | 30 +- addons/addon-image/src/ImageRenderer.ts | 36 +- addons/addon-image/src/ImageStorage.ts | 48 +- addons/addon-image/src/SixelHandler.ts | 12 +- .../src/kitty/KittyGraphicsHandler.ts | 146 ++-- .../src/kitty/KittyGraphicsTypes.ts | 30 +- .../src/kitty/KittyImageStorage.ts | 46 +- addons/addon-image/test/ImageAddon.test.ts | 6 +- addons/addon-image/test/KittyGraphics.test.ts | 90 +- addons/addon-ligatures/src/font.ts | 16 +- .../src/fontLigatures/index.ts | 28 +- .../src/fontLigatures/merge.ts | 40 +- .../src/fontLigatures/mergeRange.ts | 30 +- .../src/fontLigatures/processors/6-2.ts | 6 +- .../src/fontLigatures/processors/6-3.ts | 6 +- .../src/fontLigatures/processors/helper.ts | 12 +- .../fontLigatures/processors/substitution.ts | 6 +- addons/addon-ligatures/src/index.ts | 24 +- addons/addon-ligatures/src/parse.ts | 18 +- addons/addon-search/src/SearchEngine.ts | 18 +- addons/addon-search/src/SearchLineCache.ts | 6 +- addons/addon-serialize/src/SerializeAddon.ts | 104 +-- .../test/SerializeAddon.test.ts | 28 +- .../src/UnicodeGraphemeProvider.ts | 8 +- .../test/WebFontsAddon.test.ts | 18 +- addons/addon-web-links/src/WebLinkProvider.ts | 18 +- addons/addon-web-links/src/WebLinksAddon.ts | 24 +- addons/addon-webgl/src/CellColorResolver.ts | 18 +- addons/addon-webgl/src/CharAtlasCache.ts | 6 +- addons/addon-webgl/src/CharAtlasUtils.ts | 6 +- addons/addon-webgl/src/Constants.ts | 8 +- .../src/CursorBlinkStateManager.ts | 22 +- addons/addon-webgl/src/DevicePixelObserver.ts | 10 +- addons/addon-webgl/src/GlyphRenderer.ts | 60 +- addons/addon-webgl/src/RectangleRenderer.ts | 8 +- addons/addon-webgl/src/TextureAtlas.ts | 206 ++--- addons/addon-webgl/src/WebglRenderer.ts | 164 ++-- .../customGlyphs/CustomGlyphDefinitions.ts | 68 +- .../src/customGlyphs/CustomGlyphRasterizer.ts | 18 +- addons/addon-webgl/test/WebglRenderer.test.ts | 8 +- demo/client/client.ts | 20 +- .../components/window/addonImageWindow.ts | 22 +- .../components/window/addonProgressWindow.ts | 12 +- demo/client/components/window/testWindow.ts | 76 +- demo/client/unicodeTable.ts | 6 +- demo/server/server.ts | 14 +- eslint.config.mjs | 2 +- src/browser/AccessibilityManager.ts | 20 +- src/browser/Clipboard.ts | 6 +- src/browser/CoreBrowserTerminal.ts | 140 ++- src/browser/Linkifier.ts | 40 +- src/browser/OscLinkProvider.ts | 12 +- src/browser/Terminal.test.ts | 24 +- src/browser/TimeBasedDebouncer.ts | 6 +- src/browser/Types.ts | 6 +- src/browser/Viewport.ts | 32 +- .../decorations/BufferDecorationRenderer.ts | 6 +- src/browser/decorations/ColorZoneStore.ts | 14 +- .../decorations/OverviewRulerRenderer.ts | 12 +- src/browser/input/CompositionHelper.test.ts | 8 +- src/browser/input/CompositionHelper.ts | 86 +- src/browser/input/Mouse.ts | 8 +- src/browser/public/Terminal.ts | 8 +- src/browser/renderer/dom/DomRenderer.ts | 22 +- .../dom/DomRendererRowFactory.test.ts | 6 +- .../renderer/dom/DomRendererRowFactory.ts | 76 +- src/browser/renderer/dom/WidthCache.ts | 20 +- .../renderer/shared/RendererUtils.test.ts | 18 +- src/browser/renderer/shared/RendererUtils.ts | 14 +- src/browser/scrollable/abstractScrollbar.ts | 6 +- src/browser/scrollable/fastDomNode.ts | 6 +- .../scrollable/globalPointerMoveMonitor.ts | 6 +- src/browser/scrollable/horizontalScrollbar.ts | 6 +- src/browser/scrollable/mouseEvent.ts | 6 +- src/browser/scrollable/scrollable.ts | 6 +- src/browser/scrollable/scrollableElement.ts | 6 +- .../scrollable/scrollableElementOptions.ts | 6 +- src/browser/scrollable/scrollbarArrow.ts | 6 +- src/browser/scrollable/scrollbarState.ts | 6 +- .../scrollbarVisibilityController.ts | 6 +- src/browser/scrollable/touch.ts | 6 +- src/browser/scrollable/verticalScrollbar.ts | 6 +- src/browser/scrollable/widget.ts | 6 +- src/browser/services/CharSizeService.ts | 6 +- .../services/CharacterJoinerService.ts | 94 +- src/browser/services/MouseService.test.ts | 6 +- src/browser/services/MouseService.ts | 38 +- src/browser/services/RenderService.ts | 34 +- src/browser/services/SelectionService.test.ts | 12 +- src/browser/services/SelectionService.ts | 158 ++-- src/common/CircularList.ts | 6 +- src/common/Color.ts | 34 +- src/common/CoreTerminal.ts | 6 +- src/common/InputHandler.test.ts | 12 +- src/common/InputHandler.ts | 324 +++---- src/common/Platform.ts | 22 +- src/common/SortedList.ts | 6 +- src/common/TaskQueue.ts | 20 +- src/common/Types.ts | 30 +- src/common/WindowsMode.ts | 22 +- src/common/buffer/Buffer.test.ts | 112 +-- src/common/buffer/Buffer.ts | 102 +-- src/common/buffer/BufferLine.test.ts | 34 +- src/common/buffer/BufferLine.ts | 34 +- src/common/buffer/BufferReflow.ts | 26 +- src/common/buffer/BufferSet.ts | 20 +- src/common/buffer/CellData.ts | 6 +- src/common/input/Keyboard.test.ts | 6 +- src/common/input/Keyboard.ts | 36 +- src/common/input/KittyKeyboard.test.ts | 12 +- src/common/input/KittyKeyboard.ts | 32 +- src/common/input/TextDecoder.ts | 12 +- src/common/input/UnicodeV6.test.ts | 32 +- src/common/input/UnicodeV6.ts | 18 +- src/common/input/Win32InputMode.ts | 14 +- src/common/input/WriteBuffer.ts | 64 +- src/common/input/XParseColor.ts | 8 +- src/common/parser/ApcParser.ts | 6 +- src/common/parser/DcsParser.ts | 16 +- .../parser/EscapeSequenceParser.test.ts | 18 +- src/common/parser/EscapeSequenceParser.ts | 82 +- src/common/parser/OscParser.ts | 22 +- src/common/services/BufferService.ts | 18 +- src/common/services/InstantiationService.ts | 6 +- src/common/services/LogService.ts | 6 +- src/common/services/MouseStateService.ts | 16 +- src/common/services/OptionsService.ts | 6 +- src/common/services/ServiceRegistry.ts | 6 +- src/common/services/UnicodeService.ts | 18 +- src/headless/public/Terminal.ts | 8 +- test/playwright/InputHandler.test.ts | 28 +- test/playwright/MouseTracking.test.ts | 810 ++++++++---------- test/playwright/SharedRendererTests.ts | 30 +- test/playwright/Terminal.test.ts | 36 +- test/playwright/TestUtils.ts | 12 +- 138 files changed, 1834 insertions(+), 3028 deletions(-) diff --git a/addons/addon-fit/test/FitAddon.test.ts b/addons/addon-fit/test/FitAddon.test.ts index d82d3640a..6a7c41fa8 100644 --- a/addons/addon-fit/test/FitAddon.test.ts +++ b/addons/addon-fit/test/FitAddon.test.ts @@ -26,24 +26,18 @@ test.describe('FitAddon', () => { `); }); - /* - * test.beforeEach(async function(): Promise { - * await ctx.page.evaluate(`document.querySelector('#terminal-container').style.display=''`); - * await openTerminal(page); - * }); - */ + // test.beforeEach(async function(): Promise { + // await ctx.page.evaluate(`document.querySelector('#terminal-container').style.display=''`); + // await openTerminal(page); + // }); - /* - * after(async () => { - * await browser.close(); - * }); - */ + // after(async () => { + // await browser.close(); + // }); - /* - * afterEach(async function(): Promise { - * await ctx.proxy.dispose(); - * }); - */ + // afterEach(async function(): Promise { + // await ctx.proxy.dispose(); + // }); test('no terminal', async function(): Promise { await ctx.page.evaluate(`window.fit2 = new FitAddon();`); @@ -85,10 +79,8 @@ test.describe('FitAddon', () => { await ctx.page.evaluate(`window.term.open(document.querySelector('#terminal-container'))`); await setDimensions(); const dimensions: { cols: number, rows: number } | undefined = await ctx.page.evaluate(`window.fit.proposeDimensions()`); - /* - * The value of dims will be undefined if the char measure strategy falls back to the DOM - * method, so only assert if it's not undefined. - */ + // The value of dims will be undefined if the char measure strategy falls back to the DOM + // method, so only assert if it's not undefined. if (dimensions) { ok(dimensions.cols > 85); ok(dimensions.cols < 88); diff --git a/addons/addon-image/src/IIPHeaderParser.ts b/addons/addon-image/src/IIPHeaderParser.ts index 74e753289..af4e9a68d 100644 --- a/addons/addon-image/src/IIPHeaderParser.ts +++ b/addons/addon-image/src/IIPHeaderParser.ts @@ -24,10 +24,8 @@ export interface IHeaderFields { height?: string; // Optional, defaults to 1 respecting aspect ratio (width takes precedence). preserveAspectRatio?: number; - /* - * Optional, defaults to 0. If set to 1, the file will be displayed inline, else downloaded - * (download not supported). - */ + // Optional, defaults to 0. If set to 1, the file will be displayed inline, else downloaded + // (download not supported). inline?: number; } diff --git a/addons/addon-image/src/IIPMetrics.ts b/addons/addon-image/src/IIPMetrics.ts index 1937a2740..b377cf061 100644 --- a/addons/addon-image/src/IIPMetrics.ts +++ b/addons/addon-image/src/IIPMetrics.ts @@ -23,10 +23,8 @@ export function imageType(d: Uint8Array): IMetrics { return UNSUPPORTED_TYPE; } const d32 = new Uint32Array(d.buffer, d.byteOffset, 6); - /* - * PNG: 89 50 4E 47 0D 0A 1A 0A (8 first bytes == magic number for PNG) - * + first chunk must be IHDR - */ + // PNG: 89 50 4E 47 0D 0A 1A 0A (8 first bytes == magic number for PNG) + // + first chunk must be IHDR if (d32[0] === 0x474E5089 && d32[1] === 0x0A1A0A0D && d32[3] === 0x52444849) { return { mime: 'image/png', diff --git a/addons/addon-image/src/ImageAddon.ts b/addons/addon-image/src/ImageAddon.ts index 930a74bd8..7e0f29d3f 100644 --- a/addons/addon-image/src/ImageAddon.ts +++ b/addons/addon-image/src/ImageAddon.ts @@ -137,13 +137,11 @@ export class ImageAddon implements ITerminalAddon, IImageApi { // enable size reports if (this._opts.enableSizeReports) { - /* - * const windowOptions = terminal.getOption('windowOptions'); - * windowOptions.getWinSizePixels = true; - * windowOptions.getCellSizePixels = true; - * windowOptions.getWinSizeChars = true; - * terminal.setOption('windowOptions', windowOptions); - */ + // const windowOptions = terminal.getOption('windowOptions'); + // windowOptions.getWinSizePixels = true; + // windowOptions.getCellSizePixels = true; + // windowOptions.getWinSizeChars = true; + // terminal.setOption('windowOptions', windowOptions); const windowOps = terminal.options.windowOptions ?? {}; windowOps.getWinSizePixels = true; windowOps.getCellSizePixels = true; @@ -292,13 +290,11 @@ export class ImageAddon implements ITerminalAddon, IImageApi { if (params[0]) { return true; } - /* - * reported features: - * 62 - VT220 - * 4 - SIXEL support - * 9 - charsets - * 22 - ANSI colors - */ + // reported features: + // 62 - VT220 + // 4 - SIXEL support + // 9 - charsets + // 22 - ANSI colors if (this._opts.sixelSupport) { this._report(`\x1b[?62;4;9;22c`); return true; @@ -356,10 +352,8 @@ export class ImageAddon implements ITerminalAddon, IImageApi { let width = this._renderer?.dimensions?.css.canvas.width; let height = this._renderer?.dimensions?.css.canvas.height; if (!width || !height) { - /* - * for some reason we have no working image renderer - * --> fallback to default cell size - */ + // for some reason we have no working image renderer + // --> fallback to default cell size const cellSize = CELL_SIZE_DEFAULT; width = (this._terminal?.cols || 80) * cellSize.width; height = (this._terminal?.rows || 24) * cellSize.height; diff --git a/addons/addon-image/src/ImageRenderer.ts b/addons/addon-image/src/ImageRenderer.ts index b00002ca2..b36cdd004 100644 --- a/addons/addon-image/src/ImageRenderer.ts +++ b/addons/addon-image/src/ImageRenderer.ts @@ -197,12 +197,10 @@ export class ImageRenderer extends Disposable implements IDisposable { const finalWidth = count * width + sx > img.width ? img.width - sx : count * width; const finalHeight = sy + height > img.height ? img.height - sy : height; - /* - * Floor all pixel offsets to get stable tile mapping without any overflows. - * Note: For not pixel perfect aligned cells like in the DOM renderer - * this will move a tile slightly to the top/left (subpixel range, thus ignore it). - * FIX #34: avoid striping on displays with pixelDeviceRatio != 1 by ceiling height and width - */ + // Floor all pixel offsets to get stable tile mapping without any overflows. + // Note: For not pixel perfect aligned cells like in the DOM renderer + // this will move a tile slightly to the top/left (subpixel range, thus ignore it). + // FIX #34: avoid striping on displays with pixelDeviceRatio != 1 by ceiling height and width ctx.drawImage( img, Math.floor(sx), Math.floor(sy), Math.ceil(finalWidth), Math.ceil(finalHeight), @@ -344,27 +342,21 @@ export class ImageRenderer extends Disposable implements IDisposable { ); canvas.classList.add(`xterm-image-layer-${layer}`); const screenElement = this._terminal._core.screenElement; - /* - * Use isolation to create a stacking context without overriding z-index, - * which would conflict with integrators (e.g. VS Code) that set their - * own z-index on the screen element. - */ + // Use isolation to create a stacking context without overriding z-index, + // which would conflict with integrators (e.g. VS Code) that set their + // own z-index on the screen element. screenElement.style.isolation = 'isolate'; if (layer === 'bottom') { - /* - * Use z-index:-1 so it paints behind non-positioned text elements. - * The screen element needs to be a stacking context (via isolation) - * to contain the negative z-index, otherwise it would go behind the - * entire terminal. - */ + // Use z-index:-1 so it paints behind non-positioned text elements. + // The screen element needs to be a stacking context (via isolation) + // to contain the negative z-index, otherwise it would go behind the + // entire terminal. canvas.style.zIndex = '-1'; screenElement.insertBefore(canvas, screenElement.firstChild); } else { - /* - * Explicit z-index ensures the image canvas reliably stacks above - * the text layer (DOM renderer rows). z-index: 0 is below the - * selection overlay (z-index: 1). - */ + // Explicit z-index ensures the image canvas reliably stacks above + // the text layer (DOM renderer rows). z-index: 0 is below the + // selection overlay (z-index: 1). canvas.style.zIndex = '0'; screenElement.appendChild(canvas); } diff --git a/addons/addon-image/src/ImageStorage.ts b/addons/addon-image/src/ImageStorage.ts index 3e3a7c298..8c89a16b0 100644 --- a/addons/addon-image/src/ImageStorage.ts +++ b/addons/addon-image/src/ImageStorage.ts @@ -154,10 +154,8 @@ export class ImageStorage implements IDisposable { for (const spec of this._images.values()) { spec.marker?.dispose(); } - /* - * NOTE: marker.dispose above already calls ImageBitmap.close - * therefore we can just wipe the map here - */ + // NOTE: marker.dispose above already calls ImageBitmap.close + // therefore we can just wipe the map here this._images.clear(); this._renderer.clearAll(); } @@ -308,10 +306,8 @@ export class ImageStorage implements IDisposable { this._delImg(id); } - /* - * eviction marker: - * delete the image when the marker gets disposed - */ + // eviction marker: + // delete the image when the marker gets disposed const endMarker = this._terminal.registerMarker(0); endMarker?.onDispose(() => { const spec = this._images.get(imageId); @@ -320,10 +316,8 @@ export class ImageStorage implements IDisposable { } }); - /* - * since markers do not work on alternate for some reason, - * we evict images here manually - */ + // since markers do not work on alternate for some reason, + // we evict images here manually if (this._terminal.buffer.active.type === 'alternate') { this._evictOnAlternate(); } @@ -422,12 +416,10 @@ export class ImageStorage implements IDisposable { const drawCalls: { imgSpec: IImageSpec, tileId: number, col: number, row: number, count: number }[] = []; const placeholderCalls: { col: number, row: number, count: number }[] = []; - /* - * walk all cells in viewport and collect tiles found - * Note: We check _extendedAttrs directly (not just HAS_EXTENDED flag) - * because text writes clear the BG flag but leave image tile data intact. - * This lets top-layer images survive text overwrites (kitty C=1 behavior). - */ + // walk all cells in viewport and collect tiles found + // Note: We check _extendedAttrs directly (not just HAS_EXTENDED flag) + // because text writes clear the BG flag but leave image tile data intact. + // This lets top-layer images survive text overwrites (kitty C=1 behavior). for (let row = start; row <= end; ++row) { const line = buffer.lines.get(row + buffer.ydisp) as IBufferLineExt; if (!line) return; @@ -500,10 +492,8 @@ export class ImageStorage implements IDisposable { return; } - /* - * handle only viewport width enlargements, exit all other cases - * TODO: needs patch for tile counter - */ + // handle only viewport width enlargements, exit all other cases + // TODO: needs patch for tile counter if (this._viewportMetrics.cols >= metrics.cols) { this._viewportMetrics = metrics; return; @@ -591,10 +581,8 @@ export class ImageStorage implements IDisposable { } } - /* - * TODO: Do we need some blob offloading tricks here to avoid early eviction? - * also see https://stackoverflow.com/questions/28307789/is-there-any-limitation-on-javascript-max-blob-size - */ + // TODO: Do we need some blob offloading tricks here to avoid early eviction? + // also see https://stackoverflow.com/questions/28307789/is-there-any-limitation-on-javascript-max-blob-size private _evictOldest(room: number): number { const used = this._getStoredPixels(); let current = used; @@ -617,11 +605,9 @@ export class ImageStorage implements IDisposable { const old = line._extendedAttrs[x]; if (old) { if (old.imageId !== undefined) { - /* - * found an old ExtendedAttrsImage, since we know that - * they are always isolated instances (single cell usage), - * we can re-use it and just update their id entries - */ + // found an old ExtendedAttrsImage, since we know that + // they are always isolated instances (single cell usage), + // we can re-use it and just update their id entries const oldSpec = this._images.get(old.imageId); if (oldSpec) { // early eviction for in-viewport overwrites diff --git a/addons/addon-image/src/SixelHandler.ts b/addons/addon-image/src/SixelHandler.ts index da4798bf6..1af2d85bc 100644 --- a/addons/addon-image/src/SixelHandler.ts +++ b/addons/addon-image/src/SixelHandler.ts @@ -113,17 +113,13 @@ export class SixelHandler implements IDcsHandler, IResetHandler { * Some helpers to extract current terminal colors. */ -/* - * get currently active background color from terminal - * also respect INVERSE setting - */ +// get currently active background color from terminal +// also respect INVERSE setting function extractActiveBg(attr: AttributeData, colors: ReadonlyColorSet | undefined): RGBA8888 { let bg = 0; if (!colors) { - /* - * FIXME: theme service is prolly not available yet, - * happens if .open() was not called yet (bug in core?) - */ + // FIXME: theme service is prolly not available yet, + // happens if .open() was not called yet (bug in core?) return bg; } if (attr.isInverse()) { diff --git a/addons/addon-image/src/kitty/KittyGraphicsHandler.ts b/addons/addon-image/src/kitty/KittyGraphicsHandler.ts index 8ad8ef794..de889dfff 100644 --- a/addons/addon-image/src/kitty/KittyGraphicsHandler.ts +++ b/addons/addon-image/src/kitty/KittyGraphicsHandler.ts @@ -62,11 +62,9 @@ export class KittyGraphicsHandler implements IApcHandler, IResetHandler, IDispos // Storage related states private _pendingTransmissions: Map = new Map(); - /* - * Tracks the pending key of the most recently started chunked upload. - * Per spec, subsequent chunks only need m= (and optionally q=), without i=. - * When a chunk arrives with no i=, this key is used to find the pending upload. - */ + // Tracks the pending key of the most recently started chunked upload. + // Per spec, subsequent chunks only need m= (and optionally q=), without i=. + // When a chunk arrives with no i=, this key is used to find the pending upload. private _lastPendingKey: number | undefined; constructor( @@ -175,11 +173,9 @@ export class KittyGraphicsHandler implements IApcHandler, IResetHandler, IDispos private _streamPayload(data: Uint32Array, start: number, end: number): void { if (this._aborted) return; - /* - * Check size limit (compare encoded bytes against pre-calculated limit) - * Include cumulative size from pending transmission for multi-chunk images. - * Per spec, subsequent chunks may omit i=, so fall back to _lastPendingKey. - */ + // Check size limit (compare encoded bytes against pre-calculated limit) + // Include cumulative size from pending transmission for multi-chunk images. + // Per spec, subsequent chunks may omit i=, so fall back to _lastPendingKey. const pendingKey = this._parsedCommand?.id ?? this._lastPendingKey ?? 0; const pending = this._pendingTransmissions.get(pendingKey); const previousEncodedSize = pending?.totalEncodedSize ?? 0; @@ -289,11 +285,9 @@ export class KittyGraphicsHandler implements IApcHandler, IResetHandler, IDispos } this._activeDecoder = null; - /* - * Handle command first — handlers create Blob/ImageData from imageBytes, - * which copies the data. Only then is it safe to release the decoder's - * wasm memory that imageBytes points into. - */ + // Handle command first — handlers create Blob/ImageData from imageBytes, + // which copies the data. Only then is it safe to release the decoder's + // wasm memory that imageBytes points into. const result = this._handleCommandWithBytesAndCmd(finalCmd, imageBytes, decodeError); if (decoder) { decoder.release(); @@ -331,12 +325,10 @@ export class KittyGraphicsHandler implements IApcHandler, IResetHandler, IDispos case KittyAction.PLACEMENT: return this._handlePlacement(cmd); default: - /* - * TODO: Implement remaining actions when needed: - * - a=f (frame): animation frame operations - * - a=a (animation): animation control - * - a=c (compose): compose images - */ + // TODO: Implement remaining actions when needed: + // - a=f (frame): animation frame operations + // - a=a (animation): animation control + // - a=c (compose): compose images if (cmd.id !== undefined) { this._sendResponse(cmd.id, 'EINVAL:unsupported action', cmd.quiet ?? 0); } @@ -350,10 +342,8 @@ export class KittyGraphicsHandler implements IApcHandler, IResetHandler, IDispos switch (action) { case KittyAction.TRANSMIT: { const result = this._handleTransmit(cmd, bytes, decodeError); - /* - * Only send response when _handleTransmit didn't already respond - * (it handles unsupported transmission medium responses internally) - */ + // Only send response when _handleTransmit didn't already respond + // (it handles unsupported transmission medium responses internally) if ((cmd.transmission ?? 'd') === 'd' && cmd.id !== undefined) { if (decodeError) { this._sendResponse(cmd.id, 'EINVAL:invalid base64 data', cmd.quiet ?? 0); @@ -371,12 +361,10 @@ export class KittyGraphicsHandler implements IApcHandler, IResetHandler, IDispos // a=p ignores any payload — image data was already transmitted return this._handlePlacement(cmd); default: - /* - * TODO: Implement remaining actions when needed: - * - a=f (frame): animation frame operations - * - a=a (animation): animation control - * - a=c (compose): compose images - */ + // TODO: Implement remaining actions when needed: + // - a=f (frame): animation frame operations + // - a=a (animation): animation control + // - a=c (compose): compose images if (cmd.id !== undefined) { this._sendResponse(cmd.id, 'EINVAL:unsupported action', cmd.quiet ?? 0); } @@ -402,18 +390,16 @@ export class KittyGraphicsHandler implements IApcHandler, IResetHandler, IDispos } private _handleTransmit(cmd: IKittyCommand, bytes: Uint8Array, decodeError: boolean): boolean { - /* - * TODO: Support file-based transmission modes (t=f, t=t, t=s) - * Currently only supports direct transmission (t=d, the default). - * - t=f (file): Payload is base64-encoded file path. Terminal reads image from that path. - * - t=t (temp file): Payload is base64-encoded path in temp directory. Terminal reads, deletes. - * - t=s: Payload is base64-encoded POSIX shm name. Terminal reads from shared memory. - * These modes require filesystem/IPC access not available in browsers. For Node.js/Electron: - * 1. Check cmd.transmission (t key) before treating bytes as image data - * 2. For t=f/t/s: decode bytes as UTF-8 string (the path/name), then read file contents - * 3. For t=d: treat bytes as image data (current behavior) - * When implementing, also update _handleQuery to accept these transmission mediums. - */ + // TODO: Support file-based transmission modes (t=f, t=t, t=s) + // Currently only supports direct transmission (t=d, the default). + // - t=f (file): Payload is base64-encoded file path. Terminal reads image from that path. + // - t=t (temp file): Payload is base64-encoded path in temp directory. Terminal reads, deletes. + // - t=s: Payload is base64-encoded POSIX shm name. Terminal reads from shared memory. + // These modes require filesystem/IPC access not available in browsers. For Node.js/Electron: + // 1. Check cmd.transmission (t key) before treating bytes as image data + // 2. For t=f/t/s: decode bytes as UTF-8 string (the path/name), then read file contents + // 3. For t=d: treat bytes as image data (current behavior) + // When implementing, also update _handleQuery to accept these transmission mediums. const transmission = cmd.transmission ?? 'd'; if (transmission !== 'd') { if (cmd.id !== undefined) { @@ -463,11 +449,9 @@ export class KittyGraphicsHandler implements IApcHandler, IResetHandler, IDispos const id = cmd.id ?? 0; const quiet = cmd.quiet ?? 0; - /* - * Per spec: reject unsupported transmission mediums (only t=d is supported atm) - * TODO: When filesystem support is added (Node.js/Electron), update this to accept - * t=f (file), t=t (temp file), and t=s (shared memory) and respond OK for queries. - */ + // Per spec: reject unsupported transmission mediums (only t=d is supported atm) + // TODO: When filesystem support is added (Node.js/Electron), update this to accept + // t=f (file), t=t (temp file), and t=s (shared memory) and respond OK for queries. const transmission = cmd.transmission ?? 'd'; if (transmission !== 'd') { this._sendResponse(id, 'EINVAL:unsupported transmission medium', quiet); @@ -516,11 +500,9 @@ export class KittyGraphicsHandler implements IApcHandler, IResetHandler, IDispos // Per spec: default delete selector is 'a' (delete all visible placements) const selector = cmd.deleteSelector ?? 'a'; - /* - * TODO: Distinguish lowercase (delete placements only) from uppercase - * (delete placements + free stored image data). Currently both variants - * free everything since we don't separate stored data from placements. - */ + // TODO: Distinguish lowercase (delete placements only) from uppercase + // (delete placements + free stored image data). Currently both variants + // free everything since we don't separate stored data from placements. switch (selector) { case 'a': case 'A': @@ -529,12 +511,10 @@ export class KittyGraphicsHandler implements IApcHandler, IResetHandler, IDispos break; case 'i': case 'I': - /* - * TODO: When placement id tracking is implemented (see TODO in - * KittyImageStorage), d=i with p= should delete only that - * specific placement, while d=i without p should delete all - * placements for the image. - */ + // TODO: When placement id tracking is implemented (see TODO in + // KittyImageStorage), d=i with p= should delete only that + // specific placement, while d=i without p should delete all + // placements for the image. if (cmd.id !== undefined) { const pending = this._pendingTransmissions.get(cmd.id); if (pending) { @@ -596,10 +576,8 @@ export class KittyGraphicsHandler implements IApcHandler, IResetHandler, IDispos const cw = this._renderer.dimensions?.css.cell.width || CELL_SIZE_DEFAULT.width; const ch = this._renderer.dimensions?.css.cell.height || CELL_SIZE_DEFAULT.height; - /* - * Per spec: c/r default to image's natural cell dimensions. - * If only one of c/r is specified, compute the other from image aspect ratio. - */ + // Per spec: c/r default to image's natural cell dimensions. + // If only one of c/r is specified, compute the other from image aspect ratio. let imgCols: number; let imgRows: number; if (cmd.columns !== undefined && cmd.rows !== undefined) { @@ -635,12 +613,10 @@ export class KittyGraphicsHandler implements IApcHandler, IResetHandler, IDispos const savedY = buffer.y; const savedYbase = buffer.ybase; - /* - * Determine layer based on z-index: negative = behind text, 0+ = on top. - * When z<0 we always use the bottom layer even without allowTransparency — - * the image will simply be hidden behind the opaque text background, which - * is the correct behavior (client asked for "behind text"). - */ + // Determine layer based on z-index: negative = behind text, 0+ = on top. + // When z<0 we always use the bottom layer even without allowTransparency — + // the image will simply be hidden behind the opaque text background, which + // is the correct behavior (client asked for "behind text"). const wantsBottom = cmd.zIndex !== undefined && cmd.zIndex < 0; const layer: ImageLayer = wantsBottom ? 'bottom' : 'top'; @@ -654,12 +630,10 @@ export class KittyGraphicsHandler implements IApcHandler, IResetHandler, IDispos const xOffset = Math.min(Math.max(0, cmd.xOffset ?? 0), cw - 1); const yOffset = Math.min(Math.max(0, cmd.yOffset ?? 0), ch - 1); if (xOffset !== 0 || yOffset !== 0) { - /* - * Per spec: X/Y is not added to c/r area. When c/r are explicit, the - * total placement area remains c*cw × r*ch pixels and the offset image - * is clipped to fit. When c/r are unset, the padded canvas determines - * the natural cell dimensions. - */ + // Per spec: X/Y is not added to c/r area. When c/r are explicit, the + // total placement area remains c*cw × r*ch pixels and the offset image + // is clipped to fit. When c/r are unset, the padded canvas determines + // the natural cell dimensions. const canvasW = (cmd.columns !== undefined) ? Math.round(imgCols * cw) : bitmap.width + xOffset; const canvasH = (cmd.rows !== undefined) ? Math.round(imgRows * ch) : bitmap.height + yOffset; const offsetCanvas = ImageRenderer.createCanvas(window.document, canvasW, canvasH); @@ -690,11 +664,9 @@ export class KittyGraphicsHandler implements IApcHandler, IResetHandler, IDispos this._kittyStorage.addImage(image.id, bitmap, true, layer, zIndex); bitmap = undefined; // ownership transferred to storage - /* - * Kitty cursor movement - * Per spec: cursor placed at first column after last image column, - * on the last row of the image. C=1 means don't move cursor. - */ + // Kitty cursor movement + // Per spec: cursor placed at first column after last image column, + // on the last row of the image. C=1 means don't move cursor. if (cmd.cursorMovement === 1) { // C=1: restore cursor to position before image was placed const scrolled = buffer.ybase - savedYbase; @@ -702,10 +674,8 @@ export class KittyGraphicsHandler implements IApcHandler, IResetHandler, IDispos // Can't restore cursor to scrollback? buffer.y = Math.max(savedY - scrolled, 0); } else { - /* - * Default (C=0): advance cursor horizontally past the image - * addImage already positioned cursor on the last row via lineFeeds - */ + // Default (C=0): advance cursor horizontally past the image + // addImage already positioned cursor on the last row via lineFeeds buffer.x = Math.min(savedX + imgCols, this._coreTerminal.cols); } } catch (e) { @@ -766,11 +736,9 @@ export class KittyGraphicsHandler implements IApcHandler, IResetHandler, IDispos return createImageBitmap(new ImageData(new Uint8ClampedArray(bytes.buffer as ArrayBuffer, bytes.byteOffset, pixelCount * KittyPixelConstants.BYTES_PER_PIXEL_RGBA), width, height)); } - /* - * RGB→RGBA: interleave alpha using uint32 block processing (4 pixels per iteration). - * 3 uint32 reads + 4 uint32 writes per 4 pixels vs 28 byte reads/writes — ~6x faster. - * Assumes little-endian (all modern browsers/Node.js). - */ + // RGB→RGBA: interleave alpha using uint32 block processing (4 pixels per iteration). + // 3 uint32 reads + 4 uint32 writes per 4 pixels vs 28 byte reads/writes — ~6x faster. + // Assumes little-endian (all modern browsers/Node.js). const data = new Uint8ClampedArray(pixelCount * KittyPixelConstants.BYTES_PER_PIXEL_RGBA); const src32 = new Uint32Array(bytes.buffer, bytes.byteOffset, Math.floor(bytes.byteLength / 4)); const dst32 = new Uint32Array(data.buffer); diff --git a/addons/addon-image/src/kitty/KittyGraphicsTypes.ts b/addons/addon-image/src/kitty/KittyGraphicsTypes.ts index da94675d8..448ebcc05 100644 --- a/addons/addon-image/src/kitty/KittyGraphicsTypes.ts +++ b/addons/addon-image/src/kitty/KittyGraphicsTypes.ts @@ -7,10 +7,8 @@ import type Base64Decoder from 'xterm-wasm-parts/lib/base64/Base64Decoder.wasm'; -/* - * Kitty graphics protocol action types. - * See: https://sw.kovidgoyal.net/kitty/graphics-protocol/#control-data-reference under key 'a'. - */ +// Kitty graphics protocol action types. +// See: https://sw.kovidgoyal.net/kitty/graphics-protocol/#control-data-reference under key 'a'. export const enum KittyAction { TRANSMIT = 't', TRANSMIT_DISPLAY = 'T', @@ -19,29 +17,23 @@ export const enum KittyAction { DELETE = 'd' } -/* - * Kitty graphics protocol format types. - * See: https://sw.kovidgoyal.net/kitty/graphics-protocol/#control-data-reference - */ +// Kitty graphics protocol format types. +// See: https://sw.kovidgoyal.net/kitty/graphics-protocol/#control-data-reference export const enum KittyFormat { RGB = 24, RGBA = 32, PNG = 100 } -/* - * Kitty graphics protocol compression types. - * See: https://sw.kovidgoyal.net/kitty/graphics-protocol/#control-data-reference under key 'o'. - */ +// Kitty graphics protocol compression types. +// See: https://sw.kovidgoyal.net/kitty/graphics-protocol/#control-data-reference under key 'o'. export const enum KittyCompression { NONE = '', ZLIB = 'z' } -/* - * Kitty graphics protocol control data keys. - * See: https://sw.kovidgoyal.net/kitty/graphics-protocol/#control-data-reference - */ +// Kitty graphics protocol control data keys. +// See: https://sw.kovidgoyal.net/kitty/graphics-protocol/#control-data-reference export const enum KittyKey { // Action to perform (t=transmit, T=transmit+display, q=query, p=placement, d=delete) ACTION = 'a', @@ -123,10 +115,8 @@ export interface IKittyCommand { payload?: string; } -/* - * Pending chunked transmission state. - * Stores metadata from the first chunk while accumulating decoded payload data. - */ +// Pending chunked transmission state. +// Stores metadata from the first chunk while accumulating decoded payload data. export interface IPendingTransmission { // The parsed command from the first chunk (contains action, format, dimensions, etc.) cmd: IKittyCommand; diff --git a/addons/addon-image/src/kitty/KittyImageStorage.ts b/addons/addon-image/src/kitty/KittyImageStorage.ts index 766806532..95ea594c7 100644 --- a/addons/addon-image/src/kitty/KittyImageStorage.ts +++ b/addons/addon-image/src/kitty/KittyImageStorage.ts @@ -8,31 +8,27 @@ import { ImageStorage } from '../ImageStorage'; import { ImageLayer } from '../Types'; import { IKittyImageData } from './KittyGraphicsTypes'; -/* - * Kitty-specific image storage controller. - * - * Wraps shared ImageStorage with kitty protocol semantics: - * - tracks transmitted image payloads by kitty image id - * - tracks kitty image id -> shared ImageStorage id mapping for displayed images - * - mirrors shared-storage evictions into kitty maps - * - applies protocol-level undisplayed-image eviction policy - */ +// Kitty-specific image storage controller. +// +// Wraps shared ImageStorage with kitty protocol semantics: +// - tracks transmitted image payloads by kitty image id +// - tracks kitty image id -> shared ImageStorage id mapping for displayed images +// - mirrors shared-storage evictions into kitty maps +// - applies protocol-level undisplayed-image eviction policy export class KittyImageStorage implements IDisposable { private static readonly _maxStoredImages = 256; private _nextImageId = 1; private readonly _images: Map = new Map(); - /* - * TODO: Support multiple placements per image. The kitty spec identifies - * placements by an (image id, placement id) pair — same i + different p - * values should coexist, and same i + same p should replace the prior - * placement. Currently we track only one storage entry per kitty image id, - * so multiple placements of the same image overwrite each other. Fixing - * this requires changing these maps to Map> - * (kittyId → placementId → storageId) and updating addImage/deleteById - * accordingly. The underlying shared ImageStorage would also need to - * support multiple entries per logical image. - */ + // TODO: Support multiple placements per image. The kitty spec identifies + // placements by an (image id, placement id) pair — same i + different p + // values should coexist, and same i + same p should replace the prior + // placement. Currently we track only one storage entry per kitty image id, + // so multiple placements of the same image overwrite each other. Fixing + // this requires changing these maps to Map> + // (kittyId → placementId → storageId) and updating addImage/deleteById + // accordingly. The underlying shared ImageStorage would also need to + // support multiple entries per logical image. private readonly _kittyIdToStorageId: Map = new Map(); private readonly _storageIdToKittyId: Map = new Map(); @@ -94,12 +90,10 @@ export class KittyImageStorage implements IDisposable { } public addImage(kittyId: number, image: HTMLCanvasElement | ImageBitmap, scrolling: boolean, layer: ImageLayer, zIndex: number): void { - /* - * Clean up stale reverse-mapping from a previous placement of the same - * kitty image. The old shared-storage entry is kept (it may still be - * visible on screen) but its reverse mapping is removed so that eviction - * of the old entry won't incorrectly delete the kitty image data. - */ + // Clean up stale reverse-mapping from a previous placement of the same + // kitty image. The old shared-storage entry is kept (it may still be + // visible on screen) but its reverse mapping is removed so that eviction + // of the old entry won't incorrectly delete the kitty image data. const oldStorageId = this._kittyIdToStorageId.get(kittyId); if (oldStorageId !== undefined) { this._storageIdToKittyId.delete(oldStorageId); diff --git a/addons/addon-image/test/ImageAddon.test.ts b/addons/addon-image/test/ImageAddon.test.ts index 9db26cd7f..dc729ec63 100644 --- a/addons/addon-image/test/ImageAddon.test.ts +++ b/addons/addon-image/test/ImageAddon.test.ts @@ -64,10 +64,8 @@ const TESTDATA: ITestData = (() => { }; })(); const SIXEL_SEQ_0 = introducer(0) + TESTDATA.sixel + FINALIZER; -/* - * const SIXEL_SEQ_1 = introducer(1) + TESTDATA.sixel + FINALIZER; - * const SIXEL_SEQ_2 = introducer(2) + TESTDATA.sixel + FINALIZER; - */ +// const SIXEL_SEQ_1 = introducer(1) + TESTDATA.sixel + FINALIZER; +// const SIXEL_SEQ_2 = introducer(2) + TESTDATA.sixel + FINALIZER; // NOTE: the data is loaded as string for easier transport through playwright const TESTDATA_IIP: [string, [number, number]][] = [ diff --git a/addons/addon-image/test/KittyGraphics.test.ts b/addons/addon-image/test/KittyGraphics.test.ts index 2822cce6b..a9b5d6845 100644 --- a/addons/addon-image/test/KittyGraphics.test.ts +++ b/addons/addon-image/test/KittyGraphics.test.ts @@ -101,14 +101,12 @@ test.beforeAll(async ({ browser }) => { test.afterAll(async () => await ctx.page.close()); test.describe('Kitty Graphics Protocol', () => { - /* - * TODO: Add tests for larger images with various dimensions - * TODO: Add tests for virtual placement (U=1) - * TODO: Add tests for animation frames - * TODO: Add performance tests for streaming large images - * TODO: Implement cursor movement per Kitty spec - cursor should move by cols/rows after placement (unless C=1) - * TODO: Distinguish lowercase delete selectors (placement only) from uppercase (placement + free data) - */ + // TODO: Add tests for larger images with various dimensions + // TODO: Add tests for virtual placement (U=1) + // TODO: Add tests for animation frames + // TODO: Add performance tests for streaming large images + // TODO: Implement cursor movement per Kitty spec - cursor should move by cols/rows after placement (unless C=1) + // TODO: Distinguish lowercase delete selectors (placement only) from uppercase (placement + free data) test.beforeEach(async ({}, testInfo) => { await ctx.page.evaluate(` @@ -211,10 +209,8 @@ test.describe('Kitty Graphics Protocol', () => { }); test('enforces size limit across chunked transmissions', async () => { - /* - * Create a custom addon with very small size limit (100 bytes) - * The 1x1 PNG is ~164 bytes base64, so 2 chunks should exceed 100 - */ + // Create a custom addon with very small size limit (100 bytes) + // The 1x1 PNG is ~164 bytes base64, so 2 chunks should exceed 100 await ctx.page.evaluate(() => { (window as any).smallLimitAddon = new ImageAddon({ kittySupport: true, @@ -1283,10 +1279,8 @@ test.describe('Kitty Graphics Protocol', () => { }); test('only c specified computes r from aspect ratio', async () => { - /* - * 200x100 image (2:1 aspect) with c=10. - * Per spec: r = ceil((h/w) * c * cw / ch) = ceil(0.5 * 10 * cw / ch) - */ + // 200x100 image (2:1 aspect) with c=10. + // Per spec: r = ceil((h/w) * c * cw / ch) = ceil(0.5 * 10 * cw / ch) await ctx.proxy.write(`\x1b_Ga=t,f=100,i=225;${KITTY_MULTICOLOR_200X100_BASE64}\x1b\\`); await timeout(100); @@ -1303,10 +1297,8 @@ test.describe('Kitty Graphics Protocol', () => { }); test('only r specified computes c from aspect ratio', async () => { - /* - * 200x100 image (2:1 aspect) with r=5. - * Per spec: c = ceil((w/h) * r * ch / cw) = ceil(2 * 5 * ch / cw) - */ + // 200x100 image (2:1 aspect) with r=5. + // Per spec: c = ceil((w/h) * r * ch / cw) = ceil(2 * 5 * ch / cw) await ctx.proxy.write(`\x1b_Ga=t,f=100,i=226;${KITTY_MULTICOLOR_200X100_BASE64}\x1b\\`); await timeout(100); @@ -1324,11 +1316,9 @@ test.describe('Kitty Graphics Protocol', () => { }); test.describe('Cursor positioning', () => { - /* - * NOTE: Current tests document ACTUAL behavior (MVP - cursor doesn't move) - * Per Kitty spec: cursor placed at first column after last image column, - * on the last row of the image. C=1 means don't move cursor. - */ + // NOTE: Current tests document ACTUAL behavior (MVP - cursor doesn't move) + // Per Kitty spec: cursor placed at first column after last image column, + // on the last row of the image. C=1 means don't move cursor. test('cursor advances past 1x1 image', async () => { const cursorBefore = await getCursor(); @@ -1386,10 +1376,8 @@ test.describe('Kitty Graphics Protocol', () => { }); test('cursor should move right by cols when c specified', async () => { - /* - * c=5, r computed from 1x1 aspect ratio: r = ceil((1/1) * (5*cw) / ch) - * With 1:1 aspect, image height in pixels = 5*cw, so r = ceil(5*cw/ch) - */ + // c=5, r computed from 1x1 aspect ratio: r = ceil((1/1) * (5*cw) / ch) + // With 1:1 aspect, image height in pixels = 5*cw, so r = ceil(5*cw/ch) await ctx.proxy.write(`\x1b_Ga=T,f=100,c=5;${KITTY_BLACK_1X1_BASE64}\x1b\\`); await timeout(100); @@ -1646,12 +1634,10 @@ test.describe('Kitty Graphics Protocol', () => { }); test.describe('Pixel verification', () => { - /* - * The 200x100 image has 20 colored rectangles in a 10x2 grid. - * Each rectangle is 20px wide x 50px tall. - * Top row (y=0..49): Red, Orange, Yellow, Lime, Green, Cyan, SkyBlue, Blue, Purple, Magenta - * Bottom row (y=50..99): Pink, Brown, Maroon, Olive, Teal, Navy, Gray, DarkGray, LightGray, White - */ + // The 200x100 image has 20 colored rectangles in a 10x2 grid. + // Each rectangle is 20px wide x 50px tall. + // Top row (y=0..49): Red, Orange, Yellow, Lime, Green, Cyan, SkyBlue, Blue, Purple, Magenta + // Bottom row (y=50..99): Pink, Brown, Maroon, Olive, Teal, Navy, Gray, DarkGray, LightGray, White test('renders red rectangle at top-left origin (0,0)', async () => { await ctx.proxy.write(`\x1b_Ga=T,f=100;${KITTY_MULTICOLOR_200X100_BASE64}\x1b\\`); @@ -1664,11 +1650,9 @@ test.describe('Kitty Graphics Protocol', () => { await ctx.proxy.write(`\x1b_Ga=T,f=100;${KITTY_MULTICOLOR_200X100_BASE64}\x1b\\`); await timeout(200); - /* - * Sample center of each top-row rectangle (y=25, x=10,30,50,...,190) - * All within the first cell row, so we read from the canvas at cell (0,0) - * Red at x=10 - */ + // Sample center of each top-row rectangle (y=25, x=10,30,50,...,190) + // All within the first cell row, so we read from the canvas at cell (0,0) + // Red at x=10 deepStrictEqual(await getPixel(0, 0, 10, 25), [255, 0, 0, 255]); // Orange at x=30 deepStrictEqual(await getPixel(0, 0, 30, 25), [255, 128, 0, 255]); @@ -1684,10 +1668,8 @@ test.describe('Kitty Graphics Protocol', () => { await ctx.proxy.write(`\x1b_Ga=T,f=100;${KITTY_MULTICOLOR_200X100_BASE64}\x1b\\`); await timeout(200); - /* - * Bottom row starts at y=50. Center at y=75. - * Pink at x=10 - */ + // Bottom row starts at y=50. Center at y=75. + // Pink at x=10 deepStrictEqual(await getPixel(0, 0, 10, 75), [255, 192, 203, 255]); // Brown at x=30 deepStrictEqual(await getPixel(0, 0, 30, 75), [165, 42, 42, 255]); @@ -1743,10 +1725,8 @@ test.describe('Kitty Graphics Protocol', () => { }); test('scales cropped source region to c/r placement rectangle', async () => { - /* - * Firefox's createImageBitmap uses different resize sampling, producing - * slightly off pixel values compared to Chromium, so skip on Firefox. - */ + // Firefox's createImageBitmap uses different resize sampling, producing + // slightly off pixel values compared to Chromium, so skip on Firefox. if (ctx.browser.browserType().name() === 'firefox') { test.skip(); } @@ -2157,10 +2137,8 @@ test.describe('Kitty Graphics Protocol', () => { }); test('memory limit eviction cleans Kitty handler maps', async () => { - /* - * Resize terminal to fit 7 non-overlapping 200x100 images without scrolling. - * Each image ≈ 29 cols × 8 rows at default cell size. - */ + // Resize terminal to fit 7 non-overlapping 200x100 images without scrolling. + // Each image ≈ 29 cols × 8 rows at default cell size. await ctx.page.evaluate(` window.term.reset(); window.imageAddon?.dispose(); @@ -2169,11 +2147,9 @@ test.describe('Kitty Graphics Protocol', () => { window.term.loadAddon(window.imageAddon); `); - /* - * storageLimit 0.5 MB = 125,000 pixels. Each 200x100 image = 20,000 pixels. - * 6 images = 120K pixels (under limit). 7th triggers eviction (140K > 125K). - * Place non-overlapping so tile-count eviction doesn't interfere. - */ + // storageLimit 0.5 MB = 125,000 pixels. Each 200x100 image = 20,000 pixels. + // 6 images = 120K pixels (under limit). 7th triggers eviction (140K > 125K). + // Place non-overlapping so tile-count eviction doesn't interfere. const positions = [[1, 1], [30, 1], [1, 9], [30, 9], [1, 17], [30, 17]]; for (let n = 0; n < 6; n++) { const [c, r] = positions[n]; diff --git a/addons/addon-ligatures/src/font.ts b/addons/addon-ligatures/src/font.ts index 8e5f93dd5..60ab3d746 100644 --- a/addons/addon-ligatures/src/font.ts +++ b/addons/addon-ligatures/src/font.ts @@ -43,11 +43,9 @@ export default async function load(fontFamily: string, cacheSize: number): Promi throw new Error('Permission to access local fonts not granted.'); } } catch (err: unknown) { - /* - * A `TypeError` indicates the 'local-fonts' - * permission is not yet implemented, so - * only `throw` if this is _not_ the problem. - */ + // A `TypeError` indicates the 'local-fonts' + // permission is not yet implemented, so + // only `throw` if this is _not_ the problem. if (err instanceof Error && err.name !== 'TypeError') { throw err; } @@ -91,11 +89,9 @@ export default async function load(fontFamily: string, cacheSize: number): Promi const fonts = await fontsPromise; for (const family of parse(fontFamily)) { - /* - * If we reach one of the generic font families, the font resolution - * will end for the browser and we can't determine the specific font - * used. Throw. - */ + // If we reach one of the generic font families, the font resolution + // will end for the browser and we can't determine the specific font + // used. Throw. if (genericFontFamilies.includes(family)) { return undefined; } diff --git a/addons/addon-ligatures/src/fontLigatures/index.ts b/addons/addon-ligatures/src/fontLigatures/index.ts index 794b39ecc..d65e73bc4 100644 --- a/addons/addon-ligatures/src/fontLigatures/index.ts +++ b/addons/addon-ligatures/src/fontLigatures/index.ts @@ -88,11 +88,9 @@ class FontImpl implements IFont { glyphIds.push(this._font.charToGlyphIndex(char)); } - /* - * If there are no lookup groups, there's no point looking for - * replacements. This gives us a minor performance boost for fonts with - * no ligatures - */ + // If there are no lookup groups, there's no point looking for + // replacements. This gives us a minor performance boost for fonts with + // no ligatures if (this._lookupTrees.length === 0) { return { inputGlyphs: glyphIds, @@ -115,10 +113,8 @@ class FontImpl implements IFont { } public findLigatureRanges(text: string): [number, number][] { - /* - * Short circuit the process if there are no possible ligatures in the - * font - */ + // Short circuit the process if there are no possible ligatures in the + // font if (this._lookupTrees.length === 0) { return []; } @@ -174,11 +170,9 @@ class FontImpl implements IFont { } } } else { - /* - * We don't need to do the lastGlyphIndex tracking here because - * reverse processing isn't allowed to replace more than one - * character at a time. - */ + // We don't need to do the lastGlyphIndex tracking here because + // reverse processing isn't allowed to replace more than one + // character at a time. for (let i = nextLookup.last - 1; i >= nextLookup.first; i--) { const result = walkTree(lookup.tree, sequence, i, i); if (result) { @@ -230,10 +224,8 @@ class FontImpl implements IFont { for (let j = 0; j < lookups.length; j++) { const lookupIndex = lookups[j]; if (lookupIndex >= start) { - /* - * Update the lookup information if it's the one we're - * storing or earlier than it. - */ + // Update the lookup information if it's the one we're + // storing or earlier than it. if (result.index === null || lookupIndex <= result.index) { result.index = lookupIndex; diff --git a/addons/addon-ligatures/src/fontLigatures/merge.ts b/addons/addon-ligatures/src/fontLigatures/merge.ts index a16f748a9..262ea85ae 100644 --- a/addons/addon-ligatures/src/fontLigatures/merge.ts +++ b/addons/addon-ligatures/src/fontLigatures/merge.ts @@ -32,11 +32,9 @@ export default function mergeTrees(trees: ILookupTree[]): ILookupTree { function mergeSubtree(mainTree: ILookupTree, mergeTree: ILookupTree, mergedEntries: WeakMap>): void { // Need to fix this recursively (and handle lookups) for (const [glyphId, value] of Object.entries(mergeTree.individual)) { - /* - * The main tree is guaranteed to have no overlaps between the - * individual and range values, so if we match an invididual, there - * must not be a range - */ + // The main tree is guaranteed to have no overlaps between the + // individual and range values, so if we match an invididual, there + // must not be a range if (mainTree.individual[glyphId]) { mergeTreeEntry(mainTree.individual[glyphId], value, mergedEntries); } else { @@ -51,17 +49,13 @@ function mergeSubtree(mainTree: ILookupTree, mergeTree: ILookupTree, mergedEntri matched = true; - /* - * If they overlap, we have to split the range and then - * merge the overlap - */ + // If they overlap, we have to split the range and then + // merge the overlap mainTree.individual[glyphId] = value; mergeTreeEntry(mainTree.individual[glyphId], cloneEntry(entry), mergedEntries); - /* - * When there's an overlap, we also have to fix up the range - * that we had already processed - */ + // When there's an overlap, we also have to fix up the range + // that we had already processed mainTree.range.splice(index, 1); for (const glyph of overlap.second) { if (Array.isArray(glyph)) { @@ -82,11 +76,9 @@ function mergeSubtree(mainTree: ILookupTree, mergeTree: ILookupTree, mergedEntri } for (const { range, entry } of mergeTree.range) { - /* - * Ranges are more complicated, because they can overlap with - * multiple things, individual and range alike. We start by - * eliminating ranges that are already present in another range - */ + // Ranges are more complicated, because they can overlap with + // multiple things, individual and range alike. We start by + // eliminating ranges that are already present in another range let remainingRanges: (number | [number, number])[] = [range]; for (let index = 0; index < mainTree.range.length; index++) { @@ -131,17 +123,13 @@ function mergeSubtree(mainTree: ILookupTree, mergeTree: ILookupTree, mergedEntri continue; } - /* - * If they overlap, we have to split the range and then - * merge the overlap - */ + // If they overlap, we have to split the range and then + // merge the overlap mainTree.individual[remainingRange] = cloneEntry(entry); mergeTreeEntry(mainTree.individual[remainingRange], cloneEntry(resultEntry), mergedEntries); - /* - * When there's an overlap, we also have to fix up the range - * that we had already processed - */ + // When there's an overlap, we also have to fix up the range + // that we had already processed mainTree.range.splice(index, 1); index--; diff --git a/addons/addon-ligatures/src/fontLigatures/mergeRange.ts b/addons/addon-ligatures/src/fontLigatures/mergeRange.ts index 8058e077f..ec5305083 100644 --- a/addons/addon-ligatures/src/fontLigatures/mergeRange.ts +++ b/addons/addon-ligatures/src/fontLigatures/mergeRange.ts @@ -18,18 +18,14 @@ export default function mergeRange(ranges: [number, number][], newRangeStart: nu return ranges; } if (newRangeEnd <= range[1]) { - /* - * Case 2: New range is either wholly contained within the - * search range or overlaps with the front of it - */ + // Case 2: New range is either wholly contained within the + // search range or overlaps with the front of it range[0] = Math.min(newRangeStart, range[0]); return ranges; } if (newRangeStart < range[1]) { - /* - * Case 3: New range either wholly contains the search range - * or overlaps with the end of it - */ + // Case 3: New range either wholly contains the search range + // or overlaps with the end of it range[0] = Math.min(newRangeStart, range[0]); inRange = true; } else { @@ -38,27 +34,21 @@ export default function mergeRange(ranges: [number, number][], newRangeStart: nu } } else { if (newRangeEnd <= range[0]) { - /* - * Case 5: New range extends from previous range but doesn't - * reach the current one - */ + // Case 5: New range extends from previous range but doesn't + // reach the current one ranges[i - 1][1] = newRangeEnd; return ranges; } if (newRangeEnd <= range[1]) { - /* - * Case 6: New range extends from prvious range into the - * current range - */ + // Case 6: New range extends from prvious range into the + // current range ranges[i - 1][1] = Math.max(newRangeEnd, range[1]); ranges.splice(i, 1); inRange = false; return ranges; } - /* - * Case 7: New range extends from previous range past the - * end of the current range - */ + // Case 7: New range extends from previous range past the + // end of the current range ranges.splice(i, 1); i--; } diff --git a/addons/addon-ligatures/src/fontLigatures/processors/6-2.ts b/addons/addon-ligatures/src/fontLigatures/processors/6-2.ts index 890188508..f39682423 100644 --- a/addons/addon-ligatures/src/fontLigatures/processors/6-2.ts +++ b/addons/addon-ligatures/src/fontLigatures/processors/6-2.ts @@ -72,10 +72,8 @@ export default function buildTree(table: ChainingContextualSubstitutionTable.IFo ); } - /* - * When we get to the end, all of the entries we've accumulated - * should have a lookup defined - */ + // When we get to the end, all of the entries we've accumulated + // should have a lookup defined for (const { entry, substitutions } of currentEntries) { entry.lookup = { substitutions, diff --git a/addons/addon-ligatures/src/fontLigatures/processors/6-3.ts b/addons/addon-ligatures/src/fontLigatures/processors/6-3.ts index 2f8290824..334e6a566 100644 --- a/addons/addon-ligatures/src/fontLigatures/processors/6-3.ts +++ b/addons/addon-ligatures/src/fontLigatures/processors/6-3.ts @@ -53,10 +53,8 @@ export default function buildTree(table: ChainingContextualSubstitutionTable.IFo ); } - /* - * When we get to the end, all of the entries we've accumulated - * should have a lookup defined - */ + // When we get to the end, all of the entries we've accumulated + // should have a lookup defined for (const { entry, substitutions } of currentEntries) { entry.lookup = { substitutions, diff --git a/addons/addon-ligatures/src/fontLigatures/processors/helper.ts b/addons/addon-ligatures/src/fontLigatures/processors/helper.ts index 6bb6060d1..e6259b6d4 100644 --- a/addons/addon-ligatures/src/fontLigatures/processors/helper.ts +++ b/addons/addon-ligatures/src/fontLigatures/processors/helper.ts @@ -57,10 +57,8 @@ export function processLookaheadPosition( range: [] }; - /* - * All glyphs at this position share ONE entry - lookahead just needs to match, - * all paths lead to the same result - */ + // All glyphs at this position share ONE entry - lookahead just needs to match, + // all paths lead to the same result const sharedEntry: ILookupTreeEntry = {}; for (const glyph of glyphs) { @@ -102,10 +100,8 @@ export function processBacktrackPosition( range: [] }; - /* - * All glyphs at this position share ONE entry - backtrack just needs to match, - * all paths lead to the same result - */ + // All glyphs at this position share ONE entry - backtrack just needs to match, + // all paths lead to the same result const sharedEntry: ILookupTreeEntry = {}; for (const glyph of glyphs) { diff --git a/addons/addon-ligatures/src/fontLigatures/processors/substitution.ts b/addons/addon-ligatures/src/fontLigatures/processors/substitution.ts index 7e461b87c..3a41e936c 100644 --- a/addons/addon-ligatures/src/fontLigatures/processors/substitution.ts +++ b/addons/addon-ligatures/src/fontLigatures/processors/substitution.ts @@ -49,10 +49,8 @@ export function getIndividualSubstitutionGlyph(table: SubstitutionTable, glyphId switch (table.substFormat) { // https://docs.microsoft.com/en-us/typography/opentype/spec/gsub#11-single-substitution-format-1 case 1: - /* - * TODO: determine if there's a rhyme or reason to the 16-bit - * wraparound and if it can ever be a different number - */ + // TODO: determine if there's a rhyme or reason to the 16-bit + // wraparound and if it can ever be a different number return (glyphId + table.deltaGlyphId) % (2 ** 16); // https://docs.microsoft.com/en-us/typography/opentype/spec/gsub#12-single-substitution-format-2 case 2: diff --git a/addons/addon-ligatures/src/index.ts b/addons/addon-ligatures/src/index.ts index 33990b773..fb8e85e73 100644 --- a/addons/addon-ligatures/src/index.ts +++ b/addons/addon-ligatures/src/index.ts @@ -15,10 +15,8 @@ const enum LoadingState { FAILED } -/* - * Caches 100K characters worth of ligatures. In practice this works out to - * about 650 KB worth of cache, when a moderate number of ligatures are present. - */ +// Caches 100K characters worth of ligatures. In practice this works out to +// about 650 KB worth of cache, when a moderate number of ligatures are present. const CACHE_SIZE = 100000; /** @@ -48,10 +46,8 @@ export function enableLigatures(term: Terminal, fallbackLigatures: string[] = [] load(currentCallFontName, CACHE_SIZE) .then(f => { - /* - * Another request may have come in while we were waiting, so make - * sure our font is still vaild. - */ + // Another request may have come in while we were waiting, so make + // sure our font is still vaild. if (currentCallFontName === term.options.fontFamily) { loadingState = LoadingState.LOADED; font = f; @@ -63,10 +59,8 @@ export function enableLigatures(term: Terminal, fallbackLigatures: string[] = [] } }) .catch(e => { - /* - * Another request may have come in while we were waiting, so make - * sure our font is still vaild. - */ + // Another request may have come in while we were waiting, so make + // sure our font is still vaild. if (currentCallFontName === term.options.fontFamily) { loadingState = LoadingState.FAILED; if (term.options.logLevel === 'debug') { @@ -79,10 +73,8 @@ export function enableLigatures(term: Terminal, fallbackLigatures: string[] = [] } if (font && loadingState === LoadingState.LOADED) { - /* - * We clone the entries to avoid the internal cache of the ligature finder - * getting messed up. - */ + // We clone the entries to avoid the internal cache of the ligature finder + // getting messed up. return font.findLigatureRanges(text).map<[number, number]>( range => [range[0], range[1]] ); diff --git a/addons/addon-ligatures/src/parse.ts b/addons/addon-ligatures/src/parse.ts index 7cf7ad2f8..6289e4681 100644 --- a/addons/addon-ligatures/src/parse.ts +++ b/addons/addon-ligatures/src/parse.ts @@ -27,10 +27,8 @@ export default function parse(family: string): string[] { const families = []; let currentFamily = ''; - /* - * Work through the input character by character until there are none left. - * This lexing and parsing in one pass. - */ + // Work through the input character by character until there are none left. + // This lexing and parsing in one pass. while (context.offset < context.input.length) { const char = context.input[context.offset++]; switch (char) { @@ -154,17 +152,13 @@ function parseUnicode(context: IParseContext): string { while (context.offset < context.input.length) { const char = context.input[context.offset++]; if (/\s/.test(char)) { - /* - * The first whitespace character after a unicode escape indicates the end - * of the escape and is swallowed. - */ + // The first whitespace character after a unicode escape indicates the end + // of the escape and is swallowed. return unicodeToString(str); } if (str.length >= 6 || !/[\dA-Fa-f]/.test(char)) { - /* - * If the next character is not a valid hex digit or we have reached the - * maximum of 6 digits in the escape, terminate the escape. - */ + // If the next character is not a valid hex digit or we have reached the + // maximum of 6 digits in the escape, terminate the escape. context.offset--; return unicodeToString(str); } diff --git a/addons/addon-search/src/SearchEngine.ts b/addons/addon-search/src/SearchEngine.ts index cbff3eb8e..b9991974d 100644 --- a/addons/addon-search/src/SearchEngine.ts +++ b/addons/addon-search/src/SearchEngine.ts @@ -261,10 +261,8 @@ export class SearchEngine { return; } - /* - * This will iterate until we find the line start. - * When we find it, we will search using the calculated start column. - */ + // This will iterate until we find the line start. + // When we find it, we will search using the calculated start column. searchPosition.startRow--; searchPosition.startCol += this._terminal.cols; return this._findInLine(term, searchPosition, searchOptions); @@ -317,10 +315,8 @@ export class SearchEngine { return; } - /* - * Adjust the row number and search index if needed since a "line" of text can span multiple - * rows - */ + // Adjust the row number and search index if needed since a "line" of text can span multiple + // rows let startRowOffset = 0; while (startRowOffset < offsets.length - 1 && resultIndex >= offsets[startRowOffset + 1]) { startRowOffset++; @@ -359,10 +355,8 @@ export class SearchEngine { if (char.length > 1) { offset -= char.length - 1; } - /* - * Adjust the searchIndex for empty characters following wide unicode - * chars (eg. CJK) - */ + // Adjust the searchIndex for empty characters following wide unicode + // chars (eg. CJK) const nextCell = line.getCell(i + 1); if (nextCell && nextCell.getWidth() === 0) { offset++; diff --git a/addons/addon-search/src/SearchLineCache.ts b/addons/addon-search/src/SearchLineCache.ts index d62f47cd8..526f4bfcc 100644 --- a/addons/addon-search/src/SearchLineCache.ts +++ b/addons/addon-search/src/SearchLineCache.ts @@ -38,10 +38,8 @@ export class SearchLineCache extends Disposable { private _linesCache: LineCacheEntry[] | undefined; private _linesCacheTimeout = this._register(new MutableDisposable()); private _linesCacheDisposables = this._register(new MutableDisposable()); - /* - * Track access to avoid recreating a timeout on every init call which occurs once per search - * result (findNext/findPrevious -> _highlightAllMatches -> find loop). - */ + // Track access to avoid recreating a timeout on every init call which occurs once per search + // result (findNext/findPrevious -> _highlightAllMatches -> find loop). private _lastAccessTimestamp = 0; constructor(private readonly _terminal: Terminal) { diff --git a/addons/addon-serialize/src/SerializeAddon.ts b/addons/addon-serialize/src/SerializeAddon.ts index 1d46fd918..4980c8eda 100644 --- a/addons/addon-serialize/src/SerializeAddon.ts +++ b/addons/addon-serialize/src/SerializeAddon.ts @@ -84,10 +84,8 @@ function equalBg(cell1: IBufferCell | IAttributeData, cell2: IBufferCell): boole } function equalUnderline(cell1: IBufferCell | IAttributeData, cell2: IBufferCell): boolean { - /* - * If neither cell has underline, consider them equal regardless of internal underline color - * values - */ + // If neither cell has underline, consider them equal regardless of internal underline color + // values if (!cell1.isUnderline() && !cell2.isUnderline()) { return true; } @@ -136,18 +134,14 @@ class StringSerializeHandler extends BaseSerializeHandler { private _currentRow: string = ''; private _nullCellCount: number = 0; - /* - * we can see a full colored cell and a null cell that only have background the same style - * but the information isn't preserved by null cell itself - * so wee need to record it when required. - */ + // we can see a full colored cell and a null cell that only have background the same style + // but the information isn't preserved by null cell itself + // so wee need to record it when required. private _cursorStyle: IBufferCell = this._buffer.getNullCell(); - /* - * where exact the cursor styles comes from - * because we can't copy the cell directly - * so we remember where the content comes from instead - */ + // where exact the cursor styles comes from + // because we can't copy the cell directly + // so we remember where the content comes from instead private _cursorStyleRow: number = 0; private _cursorStyleCol: number = 0; @@ -178,10 +172,8 @@ class StringSerializeHandler extends BaseSerializeHandler { private _thisRowLastSecondChar: IBufferCell = this._buffer.getNullCell(); private _nextRowFirstChar: IBufferCell = this._buffer.getNullCell(); protected _rowEnd(row: number, isLastRow: boolean): void { - /* - * if there is colorful empty cell at line end, whe must pad it back, or the the color block - * will missing - */ + // if there is colorful empty cell at line end, whe must pad it back, or the the color block + // will missing if (this._nullCellCount > 0 && !equalBg(this._cursorStyle, this._backgroundCell)) { // use clear right to set background. this._currentRow += `\u001b[${this._nullCellCount}X`; @@ -214,10 +206,8 @@ class StringSerializeHandler extends BaseSerializeHandler { const nextRowFirstChar = nextLine.getCell(0, this._nextRowFirstChar)!; const isNextRowFirstCharDoubleWidth = nextRowFirstChar.getWidth() > 1; - /* - * validate whether this line wrap is ever possible - * which mean whether cursor can placed at a overflow position (x === row) naturally - */ + // validate whether this line wrap is ever possible + // which mean whether cursor can placed at a overflow position (x === row) naturally let isValid = false; if ( @@ -226,31 +216,23 @@ class StringSerializeHandler extends BaseSerializeHandler { isNextRowFirstCharDoubleWidth ? this._nullCellCount <= 1 : this._nullCellCount <= 0 ) { if ( - /* - * the last character can't be null, - * you can't use control sequence to move cursor to (x === row) - */ + // the last character can't be null, + // you can't use control sequence to move cursor to (x === row) (thisRowLastChar.getChars() || thisRowLastChar.getWidth() === 0) && - /* - * change background of the first wrapped cell also affects BCE - * so we mark it as invalid to simply the process to determine line separator - */ + // change background of the first wrapped cell also affects BCE + // so we mark it as invalid to simply the process to determine line separator equalBg(thisRowLastChar, nextRowFirstChar) ) { isValid = true; } if ( - /* - * the second to last character can't be null if the next line starts with CJK, - * you can't use control sequence to move cursor to (x === row) - */ + // the second to last character can't be null if the next line starts with CJK, + // you can't use control sequence to move cursor to (x === row) isNextRowFirstCharDoubleWidth && (thisRowLastSecondChar.getChars() || thisRowLastSecondChar.getWidth() === 0) && - /* - * change background of the first wrapped cell also affects BCE - * so we mark it as invalid to simply the process to determine line separator - */ + // change background of the first wrapped cell also affects BCE + // so we mark it as invalid to simply the process to determine line separator equalBg(thisRowLastChar, nextRowFirstChar) && equalBg(thisRowLastSecondChar, nextRowFirstChar) ) { @@ -259,10 +241,8 @@ class StringSerializeHandler extends BaseSerializeHandler { } if (!isValid) { - /* - * force the wrap with magic - * insert enough character to force the wrap - */ + // force the wrap with magic + // insert enough character to force the wrap rowSeparator = '-'.repeat(this._nullCellCount + 1); // move back and erase next line head rowSeparator += '\u001b[1D\u001b[1X'; @@ -276,10 +256,8 @@ class StringSerializeHandler extends BaseSerializeHandler { rowSeparator += '\u001b[B'; } - /* - * This is content and need the be serialized even it is invisible. - * without this, wrap will be missing from outputs. - */ + // This is content and need the be serialized even it is invisible. + // without this, wrap will be missing from outputs. this._lastContentCursorRow = row + 1; this._lastContentCursorCol = 0; @@ -380,10 +358,8 @@ class StringSerializeHandler extends BaseSerializeHandler { const sgrSeq = this._diffStyle(cell, this._cursorStyle); - /* - * the empty cell style is only assumed to be changed when background changed, because - * foreground is always 0. - */ + // the empty cell style is only assumed to be changed when background changed, because + // foreground is always 0. const styleChanged = isEmptyCell ? !equalBg(this._cursorStyle, cell) : sgrSeq.length > 0; /** @@ -422,11 +398,9 @@ class StringSerializeHandler extends BaseSerializeHandler { this._nullCellCount += cell.getWidth(); } else { if (this._nullCellCount > 0) { - /* - * we can just assume we have same style with previous one here - * because style change is handled by previous stage - * use move right when background is empty, use clear right when there is background. - */ + // we can just assume we have same style with previous one here + // because style change is handled by previous stage + // use move right when background is empty, use clear right when there is background. if (equalBg(this._cursorStyle, this._backgroundCell)) { this._currentRow += `\u001b[${this._nullCellCount}C`; } else { @@ -447,10 +421,8 @@ class StringSerializeHandler extends BaseSerializeHandler { protected _serializeString(excludeFinalCursorPosition: boolean): string { let rowEnd = this._allRows.length; - /* - * the fixup is only required for data without scrollback - * because it will always be placed at last line otherwise - */ + // the fixup is only required for data without scrollback + // because it will always be placed at last line otherwise if (this._buffer.length - this._firstRow <= this._terminal.rows) { rowEnd = this._lastContentCursorRow + 1 - this._firstRow; this._lastCursorCol = this._lastContentCursorCol; @@ -494,11 +466,9 @@ class StringSerializeHandler extends BaseSerializeHandler { } } - /* - * Restore the cursor's current style, see https://github.com/xtermjs/xterm.js/issues/3677 - * HACK: Internal API access since it's awkward to expose this in the API and serialize will - * likely be the only consumer - */ + // Restore the cursor's current style, see https://github.com/xtermjs/xterm.js/issues/3677 + // HACK: Internal API access since it's awkward to expose this in the API and serialize will + // likely be the only consumer const curAttrData: IAttributeData = (this._terminal as any)._core._inputHandler._curAttrData; const sgrSeq = this._diffStyle(curAttrData, this._cursorStyle); if (sgrSeq.length > 0) { @@ -611,10 +581,8 @@ export class SerializeAddon implements ITerminalAddon, ISerializeApi { } } - /* - * Cursor visibility (DECTCEM) - * Default: visible - */ + // Cursor visibility (DECTCEM) + // Default: visible if (!modes.showCursor) { content += '\x1b[?25l'; } diff --git a/addons/addon-serialize/test/SerializeAddon.test.ts b/addons/addon-serialize/test/SerializeAddon.test.ts index 02188146d..fb42d807d 100644 --- a/addons/addon-serialize/test/SerializeAddon.test.ts +++ b/addons/addon-serialize/test/SerializeAddon.test.ts @@ -416,13 +416,11 @@ test.describe('SerializeAddon', () => { '中文中文', '12中文', '中文12', - /* - * This line is going to be wrapped at last character - * because it has line length of 11 (1+2*5). - * We concat it back without the null cell currently. - * But this may be incorrect. - * see also #3097 - */ + // This line is going to be wrapped at last character + // because it has line length of 11 (1+2*5). + // We concat it back without the null cell currently. + // But this may be incorrect. + // see also #3097 '1中文中文中' ]; await ctx.proxy.write(lines.join('\r\n')); @@ -505,11 +503,9 @@ test.describe('SerializeAddon', () => { const MOVE_DOWN = (l: number): string => `\u001b[${l}B`; const MOVE_LEFT = (l: number): string => `\u001b[${l}D`; - /* - * A line wrap happened after current line. - * But there is no content. - * so wrap shouldn't even be able to happen. - */ + // A line wrap happened after current line. + // But there is no content. + // so wrap shouldn't even be able to happen. const segments = [ `123456789012345`, MOVE_UP(1), @@ -535,11 +531,9 @@ test.describe('SerializeAddon', () => { 10 ); - /* - * A line wrap happened after current line. - * But there is no content. - * so wrap shouldn't even be able to happen. - */ + // A line wrap happened after current line. + // But there is no content. + // so wrap shouldn't even be able to happen. const lines = [ padLines.join('\r\n'), '\r\n', diff --git a/addons/addon-unicode-graphemes/src/UnicodeGraphemeProvider.ts b/addons/addon-unicode-graphemes/src/UnicodeGraphemeProvider.ts index e68185f76..3d5717a0f 100644 --- a/addons/addon-unicode-graphemes/src/UnicodeGraphemeProvider.ts +++ b/addons/addon-unicode-graphemes/src/UnicodeGraphemeProvider.ts @@ -22,11 +22,9 @@ export class UnicodeGraphemeProvider implements IUnicodeVersionProvider { = UnicodeService.createPropertyValue(UC.GRAPHEME_BREAK_Other, 1, false); public charProperties(codepoint: number, preceding: UnicodeCharProperties): UnicodeCharProperties { - /* - * Optimize the simple ASCII case, under the condition that - * UnicodeService.extractCharKind(preceding) === GRAPHEME_BREAK_Other - * (which also covers the case that preceding === 0). - */ + // Optimize the simple ASCII case, under the condition that + // UnicodeService.extractCharKind(preceding) === GRAPHEME_BREAK_Other + // (which also covers the case that preceding === 0). if ((codepoint >= 32 && codepoint < 127) && (preceding >> 3) === 0) { return UnicodeGraphemeProvider._plainNarrowProperties; } diff --git a/addons/addon-web-fonts/test/WebFontsAddon.test.ts b/addons/addon-web-fonts/test/WebFontsAddon.test.ts index 100e34f55..c94bff84d 100644 --- a/addons/addon-web-fonts/test/WebFontsAddon.test.ts +++ b/addons/addon-web-fonts/test/WebFontsAddon.test.ts @@ -21,10 +21,8 @@ test.describe('WebFontsAddon', () => { deepStrictEqual(empty, []); }); test.afterEach(async () => { - /* - * for font loading tests to work, we have to remove added rules and fonts - * to work around the quite aggressive font caching done by the browsers - */ + // for font loading tests to work, we have to remove added rules and fonts + // to work around the quite aggressive font caching done by the browsers await ctx.page.evaluate(` document.styleSheets[0].deleteRule(1); document.styleSheets[0].deleteRule(0); @@ -75,10 +73,8 @@ test.describe('WebFontsAddon', () => { }); test('autoload & relayout from ctor', async ({ browser }) => { - /* - * to make this test work, we exclude the default measurement char W (x57) by restricting unicode-range - * now the browser will postpone font loading until codepoint is hit --> wrong glyph metrics on first usage - */ + // to make this test work, we exclude the default measurement char W (x57) by restricting unicode-range + // now the browser will postpone font loading until codepoint is hit --> wrong glyph metrics on first usage const data = await ctx.page.evaluate(` document.styleSheets[0].insertRule("@font-face {font-family: Kongtext; src: url(/fonts/kongtext.regular.ttf) format('truetype'); unicode-range: U+00A0-00FF}", 0); `); @@ -101,10 +97,8 @@ test.describe('WebFontsAddon', () => { deepStrictEqual(await getDocumentFonts(), [{ family: 'Kongtext', status: 'unloaded' }]); } - /* - * good case: addon fixes layout for webfont in ctor - * the relayout happens async, so wait a bit with a promise - */ + // good case: addon fixes layout for webfont in ctor + // the relayout happens async, so wait a bit with a promise await ctx.page.evaluate(` window.helperTerm.dispose(); window.helperTerm = new Terminal({fontFamily: '"Kongtext", ' + term.options.fontFamily}); diff --git a/addons/addon-web-links/src/WebLinkProvider.ts b/addons/addon-web-links/src/WebLinkProvider.ts index 3f4a894dd..efaead5c4 100644 --- a/addons/addon-web-links/src/WebLinkProvider.ts +++ b/addons/addon-web-links/src/WebLinkProvider.ts @@ -73,10 +73,8 @@ export class LinkComputer { continue; } - /* - * map string positions back to buffer positions - * values are 0-based right side excluding - */ + // map string positions back to buffer positions + // values are 0-based right side excluding const [startY, startX] = LinkComputer._mapStrIdx(terminal, startLineIndex, 0, match.index); const [endY, endX] = LinkComputer._mapStrIdx(terminal, startY, startX, text.length); @@ -174,13 +172,11 @@ export class LinkComputer { if (width) { stringIndex -= chars.length || 1; - /* - * correct stringIndex for early wrapped wide chars: - * - currently only happens at last cell - * - cells to the right are reset with chars='' and width=1 in InputHandler.print - * - follow-up line must be wrapped and contain wide char at first cell - * --> if all these conditions are met, correct stringIndex by +1 - */ + // correct stringIndex for early wrapped wide chars: + // - currently only happens at last cell + // - cells to the right are reset with chars='' and width=1 in InputHandler.print + // - follow-up line must be wrapped and contain wide char at first cell + // --> if all these conditions are met, correct stringIndex by +1 if (i === line.length - 1 && chars === '') { const line = buf.getLine(lineIndex + 1); if (line && line.isWrapped) { diff --git a/addons/addon-web-links/src/WebLinksAddon.ts b/addons/addon-web-links/src/WebLinksAddon.ts index 0a7171987..3328a3c3f 100644 --- a/addons/addon-web-links/src/WebLinksAddon.ts +++ b/addons/addon-web-links/src/WebLinksAddon.ts @@ -7,19 +7,17 @@ import type { Terminal, ITerminalAddon, IDisposable } from '@xterm/xterm'; import type { WebLinksAddon as IWebLinksApi } from '@xterm/addon-web-links'; import { ILinkProviderOptions, WebLinkProvider } from './WebLinkProvider'; -/* - * consider everthing starting with http:// or https:// - * up to first whitespace, `"` or `'` as url - * NOTE: The repeated end clause is needed to not match a dangling `:` - * resembling the old (...)*([^:"\'\\s]) final path clause - * additionally exclude early + final: - * - unsafe from rfc3986: !*'() - * - unsafe chars from rfc1738: {}|\^~[]` (minus [] as we need them for ipv6 adresses, also allow ~) - * also exclude as finals: - * - final interpunction like ,.!? - * - any sort of brackets <>()[]{} (not spec conform, but often used to enclose urls) - * - unsafe chars from rfc1738: {}|\^~[]` - */ +// consider everthing starting with http:// or https:// +// up to first whitespace, `"` or `'` as url +// NOTE: The repeated end clause is needed to not match a dangling `:` +// resembling the old (...)*([^:"\'\\s]) final path clause +// additionally exclude early + final: +// - unsafe from rfc3986: !*'() +// - unsafe chars from rfc1738: {}|\^~[]` (minus [] as we need them for ipv6 adresses, also allow ~) +// also exclude as finals: +// - final interpunction like ,.!? +// - any sort of brackets <>()[]{} (not spec conform, but often used to enclose urls) +// - unsafe chars from rfc1738: {}|\^~[]` const strictUrlRegex = /(https?|HTTPS?):[/]{2}[^\s"'!*(){}|\\\^<>`]*[^\s"':,.!?{}|\\\^~\[\]`()<>]/; diff --git a/addons/addon-webgl/src/CellColorResolver.ts b/addons/addon-webgl/src/CellColorResolver.ts index fe8baa35a..3a4661cc3 100644 --- a/addons/addon-webgl/src/CellColorResolver.ts +++ b/addons/addon-webgl/src/CellColorResolver.ts @@ -47,10 +47,8 @@ export class CellColorResolver { this.result.bg = cell.bg; this.result.fg = cell.fg; this.result.ext = cell.bg & BgFlags.HAS_EXTENDED ? cell.extended.ext : 0; - /* - * Get any foreground/background overrides, this happens on the model to avoid spreading - * override logic throughout the different sub-renderers - */ + // Get any foreground/background overrides, this happens on the model to avoid spreading + // override logic throughout the different sub-renderers // Reset overrides work variables $bg = 0; @@ -188,10 +186,8 @@ export class CellColorResolver { } }); - /* - * Convert any overrides from rgba to the fg/bg packed format. This resolves the inverse flag - * ahead of time in order to use the correct cache key - */ + // Convert any overrides from rgba to the fg/bg packed format. This resolves the inverse flag + // ahead of time in order to use the correct cache key if ($hasBg) { if ($isSelected) { // Non-RGB attributes from model + force non-dim + override + force RGB color mode @@ -206,10 +202,8 @@ export class CellColorResolver { $fg = (cell.fg & ~Attributes.RGB_MASK & ~FgFlags.INVERSE) | $fg | Attributes.CM_RGB; } - /* - * Handle case where inverse was specified by only one of bg override or fg override was set, - * resolving the other inverse color and setting the inverse flag if needed. - */ + // Handle case where inverse was specified by only one of bg override or fg override was set, + // resolving the other inverse color and setting the inverse flag if needed. if (this.result.fg & FgFlags.INVERSE) { if ($hasBg && !$hasFg) { // Resolve bg color type (default color has a different meaning in fg vs bg) diff --git a/addons/addon-webgl/src/CharAtlasCache.ts b/addons/addon-webgl/src/CharAtlasCache.ts index 5581ea22d..823a74cc6 100644 --- a/addons/addon-webgl/src/CharAtlasCache.ts +++ b/addons/addon-webgl/src/CharAtlasCache.ts @@ -13,10 +13,8 @@ import type { ILogService } from 'common/services/Services'; interface ITextureAtlasCacheEntry { atlas: ITextureAtlas; config: ICharAtlasConfig; - /* - * N.B. This implementation potentially holds onto copies of the terminal forever, so - * this may cause memory leaks. - */ + // N.B. This implementation potentially holds onto copies of the terminal forever, so + // this may cause memory leaks. ownedBy: Terminal[]; } diff --git a/addons/addon-webgl/src/CharAtlasUtils.ts b/addons/addon-webgl/src/CharAtlasUtils.ts index 15e796256..4ce103e98 100644 --- a/addons/addon-webgl/src/CharAtlasUtils.ts +++ b/addons/addon-webgl/src/CharAtlasUtils.ts @@ -25,10 +25,8 @@ export function generateConfig(deviceCellWidth: number, deviceCellHeight: number scrollbarSliderBackground: NULL_COLOR, scrollbarSliderHoverBackground: NULL_COLOR, scrollbarSliderActiveBackground: NULL_COLOR, - /* - * For the static char atlas, we only use the first 16 colors, but we need all 256 for the - * dynamic character atlas. - */ + // For the static char atlas, we only use the first 16 colors, but we need all 256 for the + // dynamic character atlas. ansi: colors.ansi.slice(), contrastCache: colors.contrastCache, halfContrastCache: colors.halfContrastCache diff --git a/addons/addon-webgl/src/Constants.ts b/addons/addon-webgl/src/Constants.ts index af08fe8bb..a00434d61 100644 --- a/addons/addon-webgl/src/Constants.ts +++ b/addons/addon-webgl/src/Constants.ts @@ -6,9 +6,7 @@ import { isFirefox, isLegacyEdge } from 'common/Platform'; export const DIM_OPACITY = 0.5; -/* - * The text baseline is set conditionally by browser. Using 'ideographic' for Firefox or Legacy Edge - * would result in truncated text (Issue 3353). Using 'bottom' for Chrome would result in slightly - * unaligned Powerline fonts (PR 3356#issuecomment-850928179). - */ +// The text baseline is set conditionally by browser. Using 'ideographic' for Firefox or Legacy Edge +// would result in truncated text (Issue 3353). Using 'bottom' for Chrome would result in slightly +// unaligned Powerline fonts (PR 3356#issuecomment-850928179). export const TEXT_BASELINE: CanvasTextBaseline = isFirefox || isLegacyEdge ? 'bottom' : 'ideographic'; diff --git a/addons/addon-webgl/src/CursorBlinkStateManager.ts b/addons/addon-webgl/src/CursorBlinkStateManager.ts index 1e9695c94..688aee7b7 100644 --- a/addons/addon-webgl/src/CursorBlinkStateManager.ts +++ b/addons/addon-webgl/src/CursorBlinkStateManager.ts @@ -87,17 +87,13 @@ export class CursorBlinkStateManager { this._blinkInterval = undefined; } - /* - * Setup the initial timeout which will hide the cursor, this is done before - * the regular interval is setup in order to support restarting the blink - * animation in a lightweight way (without thrashing clearInterval and - * setInterval). - */ + // Setup the initial timeout which will hide the cursor, this is done before + // the regular interval is setup in order to support restarting the blink + // animation in a lightweight way (without thrashing clearInterval and + // setInterval). this._blinkStartTimeout = this._coreBrowserService.window.setTimeout(() => { - /* - * Check if another animation restart was requested while this was being - * started - */ + // Check if another animation restart was requested while this was being + // started if (this._animationTimeRestarted) { const time = Constants.BLINK_INTERVAL - (Date.now() - this._animationTimeRestarted); this._animationTimeRestarted = undefined; @@ -118,10 +114,8 @@ export class CursorBlinkStateManager { this._blinkInterval = this._coreBrowserService.window.setInterval(() => { // Adjust the animation time if it was restarted if (this._animationTimeRestarted) { - /* - * calc time diff - * Make restart interval do a setTimeout initially? - */ + // calc time diff + // Make restart interval do a setTimeout initially? const time = Constants.BLINK_INTERVAL - (Date.now() - this._animationTimeRestarted); this._animationTimeRestarted = undefined; this._restartInterval(time); diff --git a/addons/addon-webgl/src/DevicePixelObserver.ts b/addons/addon-webgl/src/DevicePixelObserver.ts index c7af6a1bb..dcd8de0a6 100644 --- a/addons/addon-webgl/src/DevicePixelObserver.ts +++ b/addons/addon-webgl/src/DevicePixelObserver.ts @@ -6,12 +6,10 @@ import { toDisposable, IDisposable } from 'common/Lifecycle'; export function observeDevicePixelDimensions(element: HTMLElement, parentWindow: Window & typeof globalThis, callback: (deviceWidth: number, deviceHeight: number) => void): IDisposable { - /* - * Observe any resizes to the element and extract the actual pixel size of the element if the - * devicePixelContentBoxSize API is supported. This allows correcting rounding errors when - * converting between CSS pixels and device pixels which causes blurry rendering when device - * pixel ratio is not a round number. - */ + // Observe any resizes to the element and extract the actual pixel size of the element if the + // devicePixelContentBoxSize API is supported. This allows correcting rounding errors when + // converting between CSS pixels and device pixels which causes blurry rendering when device + // pixel ratio is not a round number. let observer: ResizeObserver | undefined = new parentWindow.ResizeObserver((entries) => { const entry = entries.find((entry) => entry.target === element); if (!entry) { diff --git a/addons/addon-webgl/src/GlyphRenderer.ts b/addons/addon-webgl/src/GlyphRenderer.ts index 82ca01b63..46b8efeb9 100644 --- a/addons/addon-webgl/src/GlyphRenderer.ts +++ b/addons/addon-webgl/src/GlyphRenderer.ts @@ -148,11 +148,9 @@ export class GlyphRenderer extends Disposable { gl.enableVertexAttribArray(VertexAttribLocations.UNIT_QUAD); gl.vertexAttribPointer(VertexAttribLocations.UNIT_QUAD, 2, this._gl.FLOAT, false, 0, 0); - /* - * Setup the unit quad element array buffer, this points to indices in - * unitQuadVertices to allow is to draw 2 triangles from the vertices via a - * triangle strip - */ + // Setup the unit quad element array buffer, this points to indices in + // unitQuadVertices to allow is to draw 2 triangles from the vertices via a + // triangle strip const unitQuadElementIndices = new Uint8Array([0, 1, 2, 3]); const elementIndicesBuffer = gl.createBuffer(); this._register(toDisposable(() => gl.deleteBuffer(elementIndicesBuffer))); @@ -191,10 +189,8 @@ export class GlyphRenderer extends Disposable { gl.uniform1iv(this._textureLocation, textureUnits); gl.uniformMatrix4fv(this._projectionLocation, false, PROJECTION_MATRIX); - /* - * Setup 1x1 red pixel textures for all potential atlas pages, if one of these invalid textures - * is ever drawn it will show characters as red rectangles. - */ + // Setup 1x1 red pixel textures for all potential atlas pages, if one of these invalid textures + // is ever drawn it will show characters as red rectangles. this._atlasTextures = []; for (let i = 0; i < TextureAtlas.maxAtlasPages; i++) { const glTexture = new GLTexture(throwIfFalsy(gl.createTexture())); @@ -220,22 +216,18 @@ export class GlyphRenderer extends Disposable { } public updateCell(x: number, y: number, code: number, bg: number, fg: number, ext: number, chars: string, width: number, lastBg: number): void { - /* - * Since this function is called for every cell (`rows*cols`), it must be very optimized. It - * should not instantiate any variables unless a new glyph is drawn to the cache where the - * slight slowdown is acceptable for the developer ergonomics provided as it's a once of for - * each glyph. - */ + // Since this function is called for every cell (`rows*cols`), it must be very optimized. It + // should not instantiate any variables unless a new glyph is drawn to the cache where the + // slight slowdown is acceptable for the developer ergonomics provided as it's a once of for + // each glyph. this._updateCell(this._vertices.attributes, x, y, code, bg, fg, ext, chars, width, lastBg); } private _updateCell(array: Float32Array, x: number, y: number, code: number | undefined, bg: number, fg: number, ext: number, chars: string, width: number, lastBg: number): void { $i = (y * this._terminal.cols + x) * Constants.INDICES_PER_CELL; - /* - * Exit early if this is a null character, allow space character to continue as it may have - * underline/strikethrough styles - */ + // Exit early if this is a null character, allow space character to continue as it may have + // underline/strikethrough styles if (code === NULL_CELL_CODE || code === undefined/* This is used for the right side of wide chars */) { array.fill(0, $i, $i + Constants.INDICES_PER_CELL - 1 - Constants.CELL_POSITION_INDICES); return; @@ -287,10 +279,8 @@ export class GlyphRenderer extends Disposable { } // a_cellpos only changes on resize - /* - * Reduce scale horizontally for wide glyphs printed in cells that would overlap with the - * following cell (ie. the width is not 2). - */ + // Reduce scale horizontally for wide glyphs printed in cells that would overlap with the + // following cell (ie. the width is not 2). if (this._optionsService.rawOptions.rescaleOverlappingGlyphs) { if (allowRescaling(code, width, $glyph.size.x, this._dimensions.device.cell.width)) { array[$i + 2] = (this._dimensions.device.cell.width - 1) / this._dimensions.device.canvas.width; // - 1 to improve readability @@ -349,15 +339,13 @@ export class GlyphRenderer extends Disposable { this._activeBuffer = (this._activeBuffer + 1) % 2; const activeBuffer = this._vertices.attributesBuffers[this._activeBuffer]; - /* - * Copy data for each cell of each line up to its line length (the last non-whitespace cell) - * from the attributes buffer into activeBuffer, which is the one that gets bound to the GPU. - * The reasons for this are as follows: - * - So the active buffer can be alternated so we don't get blocked on rendering finishing - * - To copy either the normal attributes buffer or the selection attributes buffer when there - * is a selection - * - So we don't send vertices for all the line-ending whitespace to the GPU - */ + // Copy data for each cell of each line up to its line length (the last non-whitespace cell) + // from the attributes buffer into activeBuffer, which is the one that gets bound to the GPU. + // The reasons for this are as follows: + // - So the active buffer can be alternated so we don't get blocked on rendering finishing + // - To copy either the normal attributes buffer or the selection attributes buffer when there + // is a selection + // - So we don't send vertices for all the line-ending whitespace to the GPU let bufferLength = 0; for (let y = 0; y < renderModel.lineLengths.length; y++) { const si = y * this._terminal.cols * Constants.INDICES_PER_CELL; @@ -370,11 +358,9 @@ export class GlyphRenderer extends Disposable { gl.bindBuffer(gl.ARRAY_BUFFER, this._attributesBuffer); gl.bufferData(gl.ARRAY_BUFFER, activeBuffer.subarray(0, bufferLength), gl.STREAM_DRAW); - /* - * Bind the atlas page texture if they have changed. AtlasPage.version is globally - * monotonic, so a page object swap at the same index (which happens after a page merge) - * is detected by the same comparison. - */ + // Bind the atlas page texture if they have changed. AtlasPage.version is globally + // monotonic, so a page object swap at the same index (which happens after a page merge) + // is detected by the same comparison. for (let i = 0; i < this._atlas.pages.length; i++) { if (this._atlas.pages[i].version !== this._atlasTextures[i].version) { this._bindAtlasPageTexture(gl, this._atlas, i); diff --git a/addons/addon-webgl/src/RectangleRenderer.ts b/addons/addon-webgl/src/RectangleRenderer.ts index e87bc690c..81e129e5c 100644 --- a/addons/addon-webgl/src/RectangleRenderer.ts +++ b/addons/addon-webgl/src/RectangleRenderer.ts @@ -114,11 +114,9 @@ export class RectangleRenderer extends Disposable { gl.enableVertexAttribArray(VertexAttribLocations.UNIT_QUAD); gl.vertexAttribPointer(VertexAttribLocations.UNIT_QUAD, 2, this._gl.FLOAT, false, 0, 0); - /* - * Setup the unit quad element array buffer, this points to indices in - * unitQuadVertices to allow is to draw 2 triangles from the vertices via a - * triangle strip - */ + // Setup the unit quad element array buffer, this points to indices in + // unitQuadVertices to allow is to draw 2 triangles from the vertices via a + // triangle strip const unitQuadElementIndices = new Uint8Array([0, 1, 2, 3]); const elementIndicesBuffer = gl.createBuffer(); this._register(toDisposable(() => gl.deleteBuffer(elementIndicesBuffer))); diff --git a/addons/addon-webgl/src/TextureAtlas.ts b/addons/addon-webgl/src/TextureAtlas.ts index 09293d4fa..34188235c 100644 --- a/addons/addon-webgl/src/TextureAtlas.ts +++ b/addons/addon-webgl/src/TextureAtlas.ts @@ -151,17 +151,13 @@ export class TextureAtlas implements ITextureAtlas { } private _createNewPage(): AtlasPage { - /* - * Try merge the set of the 4 most used pages of the largest size. This is is deferred to a - * microtask to ensure it does not interrupt textures that will be rendered in the current - * animation frame which would result in blank rendered areas. This is actually not that - * expensive relative to drawing the glyphs, so there is no need to wait for an idle callback. - */ + // Try merge the set of the 4 most used pages of the largest size. This is is deferred to a + // microtask to ensure it does not interrupt textures that will be rendered in the current + // animation frame which would result in blank rendered areas. This is actually not that + // expensive relative to drawing the glyphs, so there is no need to wait for an idle callback. if (TextureAtlas.maxAtlasPages && this._pages.length >= Math.max(4, TextureAtlas.maxAtlasPages)) { - /* - * Find the set of the largest 4 images, below the maximum size, with the highest - * percentages used - */ + // Find the set of the largest 4 images, below the maximum size, with the highest + // percentages used const pagesBySize = this._pages.filter(e => { return e.canvas.width * 2 <= (TextureAtlas.maxTextureSize || Constants.FORCED_MAX_TEXTURE_SIZE); }).sort((a, b) => { @@ -184,10 +180,8 @@ export class TextureAtlas implements ITextureAtlas { // Gather details of the merge const mergingPages = pagesBySize.slice(sameSizeI, sameSizeI + 4); - /* - * Only proceed with merge if we have exactly 4 same-sized pages. If not, we cannot - * effectively reduce page count and merging would cause issues. - */ + // Only proceed with merge if we have exactly 4 same-sized pages. If not, we cannot + // effectively reduce page count and merging would cause issues. if (mergingPages.length < 4 || mergingPages.some(p => p.canvas.width !== mergingPages[0].canvas.width)) { const newPage = new AtlasPage(this._document, this._textureSize); this._pages.push(newPage); @@ -300,11 +294,9 @@ export class TextureAtlas implements ITextureAtlas { private _getBackgroundColor(bgColorMode: number, bgColor: number, inverse: boolean, dim: boolean): IColor { if (this._config.allowTransparency) { - /* - * The background color might have some transparency, so we need to render it as fully - * transparent in the atlas. Otherwise we'd end up drawing the transparent background twice - * around the anti-aliased edges of the glyph, and it would look too dark. - */ + // The background color might have some transparency, so we need to render it as fully + // transparent in the atlas. Otherwise we'd end up drawing the transparent background twice + // around the anti-aliased edges of the glyph, and it would look too dark. return NULL_COLOR; } @@ -426,10 +418,8 @@ export class TextureAtlas implements ITextureAtlas { const bgRgba = this._resolveBackgroundRgba(bgColorMode, bgColor, inverse); const fgRgba = this._resolveForegroundRgba(fgColorMode, fgColor, inverse, bold); - /* - * Dim cells only require half the contrast, otherwise they wouldn't be distinguishable from - * non-dim cells - */ + // Dim cells only require half the contrast, otherwise they wouldn't be distinguishable from + // non-dim cells const result = rgba.ensureContrastRatio(bgRgba, fgRgba, this._config.minimumContrastRatio / (dim ? 2 : 1)); if (!result) { @@ -457,26 +447,20 @@ export class TextureAtlas implements ITextureAtlas { private _drawToCache(codeOrChars: number | string, bg: number, fg: number, ext: number, restrictToCellHeight: boolean, domContainer: HTMLElement | undefined): IRasterizedGlyph { const chars = typeof codeOrChars === 'number' ? String.fromCharCode(codeOrChars) : codeOrChars; - /* - * Uncomment for debugging - * console.log(`draw to cache "${chars}"`, bg, fg, ext); - */ + // Uncomment for debugging + // console.log(`draw to cache "${chars}"`, bg, fg, ext); - /* - * Attach the canvas to the DOM in order to inherit font-feature-settings - * from the parent elements. This is necessary for ligatures and variants to - * work. - */ + // Attach the canvas to the DOM in order to inherit font-feature-settings + // from the parent elements. This is necessary for ligatures and variants to + // work. if (domContainer && this._tmpCanvas.parentElement !== domContainer) { this._tmpCanvas.style.display = 'none'; domContainer.append(this._tmpCanvas); } - /* - * Allow 1 cell width per character, with a minimum of 2 (CJK), plus some padding. This is used - * to draw the glyph to the canvas as well as to restrict the bounding box search to ensure - * giant ligatures (eg. =====>) don't impact overall performance. - */ + // Allow 1 cell width per character, with a minimum of 2 (CJK), plus some padding. This is used + // to draw the glyph to the canvas as well as to restrict the bounding box search to ensure + // giant ligatures (eg. =====>) don't impact overall performance. const allowedWidth = Math.min(this._config.deviceCellWidth * Math.max(chars.length, 2) + TMP_CANVAS_GLYPH_PADDING * 2, this._config.deviceMaxTextureSize); if (this._tmpCanvas.width < allowedWidth) { this._tmpCanvas.width = allowedWidth; @@ -519,10 +503,8 @@ export class TextureAtlas implements ITextureAtlas { // draw the background const backgroundColor = this._getBackgroundColor(bgColorMode, bgColor, inverse, dim); - /* - * Use a 'copy' composite operation to clear any existing glyph out of _tmpCtxWithAlpha, - * regardless of transparency in backgroundColor - */ + // Use a 'copy' composite operation to clear any existing glyph out of _tmpCtxWithAlpha, + // regardless of transparency in backgroundColor this._tmpCtx.globalCompositeOperation = 'copy'; this._tmpCtx.fillStyle = backgroundColor.css; this._tmpCtx.fillRect(0, 0, this._tmpCanvas.width, this._tmpCanvas.height); @@ -550,11 +532,9 @@ export class TextureAtlas implements ITextureAtlas { customGlyph = tryDrawCustomGlyph(this._tmpCtx, chars, padding, padding, this._config.deviceCellWidth, this._config.deviceCellHeight, this._config.deviceCharWidth, this._config.deviceCharHeight, this._config.fontSize, this._config.devicePixelRatio, backgroundColor.css, variantOffset); } - /* - * Whether to clear pixels based on a threshold difference between the glyph color and the - * background color. This should be disabled when the glyph contains multiple colors such as - * underline colors to prevent important colors could get cleared. - */ + // Whether to clear pixels based on a threshold difference between the glyph color and the + // background color. This should be disabled when the glyph contains multiple colors such as + // underline colors to prevent important colors could get cleared. let enableClearThresholdCheck = !powerlineGlyph; let chWidth: number; @@ -619,10 +599,8 @@ export class TextureAtlas implements ITextureAtlas { clipRegion.rect(xChLeft, yTop, this._config.deviceCellWidth, yBot - yTop); this._tmpCtx.clip(clipRegion); - /* - * Draw a zigzag pattern, this is derived from the SVG used in monaco for the same - * style. The viewbox is 6x3 so scale it using that. - */ + // Draw a zigzag pattern, this is derived from the SVG used in monaco for the same + // style. The viewbox is 6x3 so scale it using that. const cellW = this._config.deviceCellWidth; const curlyH = (yBot - yTop); const scaleX = cellW / 6; @@ -693,21 +671,15 @@ export class TextureAtlas implements ITextureAtlas { } this._tmpCtx.restore(); - /* - * Draw stroke in the background color for non custom characters in order to give an outline - * between the text and the underline. Only do this when font size is >= 12 as the underline - * looks odd when the font size is too small - */ + // Draw stroke in the background color for non custom characters in order to give an outline + // between the text and the underline. Only do this when font size is >= 12 as the underline + // looks odd when the font size is too small if (!customGlyph && this._config.fontSize >= 12) { - /* - * This only works when transparency is disabled because it's not clear how to clear stroked - * text - */ + // This only works when transparency is disabled because it's not clear how to clear stroked + // text if (!this._config.allowTransparency && chars !== ' ') { - /* - * Measure the text, only draw the stroke if there is a descent beyond an alphabetic text - * baseline - */ + // Measure the text, only draw the stroke if there is a descent beyond an alphabetic text + // baseline this._tmpCtx.save(); this._tmpCtx.textBaseline = 'alphabetic'; const metrics = this._tmpCtx.measureText(chars); @@ -715,11 +687,9 @@ export class TextureAtlas implements ITextureAtlas { if ('actualBoundingBoxDescent' in metrics && metrics.actualBoundingBoxDescent > 0) { // This translates to 1/2 the line width in either direction this._tmpCtx.save(); - /* - * Clip the region to only draw in valid pixels near the underline to avoid a slight - * outline around the whole glyph, as well as additional pixels in the glyph at the top - * which would increase GPU memory demands - */ + // Clip the region to only draw in valid pixels near the underline to avoid a slight + // outline around the whole glyph, as well as additional pixels in the glyph at the top + // which would increase GPU memory demands const clipRegion = new Path2D(); clipRegion.rect(xLeft, yTop - Math.ceil(lineWidth / 2), this._config.deviceCellWidth * chWidth, yBot - yTop + Math.ceil(lineWidth / 2)); this._tmpCtx.clip(clipRegion); @@ -749,10 +719,8 @@ export class TextureAtlas implements ITextureAtlas { this._tmpCtx.fillText(chars, padding, padding + this._config.deviceCharHeight); } - /* - * If this character is underscore and beyond the cell bounds, shift it up until it is visible - * even on the bottom row, try for a maximum of 5 pixels. - */ + // If this character is underscore and beyond the cell bounds, shift it up until it is visible + // even on the bottom row, try for a maximum of 5 pixels. if (chars === '_' && !this._config.allowTransparency) { let isBeyondCellBounds = clearColor(this._tmpCtx.getImageData(padding, padding, this._config.deviceCellWidth, this._config.deviceCellHeight), backgroundColor, foregroundColor, enableClearThresholdCheck); if (isBeyondCellBounds) { @@ -784,10 +752,8 @@ export class TextureAtlas implements ITextureAtlas { this._tmpCtx.restore(); - /* - * clear the background from the character to avoid issues with drawing over the previous - * character if it extends past it's bounds - */ + // clear the background from the character to avoid issues with drawing over the previous + // character if it extends past it's bounds const imageData = this._tmpCtx.getImageData( 0, 0, this._tmpCanvas.width, this._tmpCanvas.height ); @@ -830,16 +796,12 @@ export class TextureAtlas implements ITextureAtlas { } } - /* - * TODO: This algorithm could be simplified: - * - Search for the page with ROW_PIXEL_THRESHOLD in mind - * - Keep track of current/fixed rows in a Map - */ + // TODO: This algorithm could be simplified: + // - Search for the page with ROW_PIXEL_THRESHOLD in mind + // - Keep track of current/fixed rows in a Map - /* - * Replace the best current row with a fixed row if there is one at least as good as the - * current row. Search in reverse to prioritize filling in older pages. - */ + // Replace the best current row with a fixed row if there is one at least as good as the + // current row. Search in reverse to prioritize filling in older pages. for (let i = this._activePages.length - 1; i >= 0; i--) { for (const row of this._activePages[i].fixedRows) { if (row.height <= activeRow.height && rasterizedGlyph.size.y <= row.height) { @@ -870,16 +832,12 @@ export class TextureAtlas implements ITextureAtlas { break; } - /* - * Create a new page if too much vertical space would be wasted or there is not enough room - * left in the page. The previous active row will become fixed in the process as it now has a - * fixed height - */ + // Create a new page if too much vertical space would be wasted or there is not enough room + // left in the page. The previous active row will become fixed in the process as it now has a + // fixed height if (activeRow.y + rasterizedGlyph.size.y >= activePage.canvas.height || activeRow.height > rasterizedGlyph.size.y + Constants.ROW_PIXEL_THRESHOLD) { - /* - * Create the new fixed height row, creating a new page if there isn't enough room on the - * current page - */ + // Create the new fixed height row, creating a new page if there isn't enough room on the + // current page let wasPageAndRowFound = false; if (activePage.currentRow.y + activePage.currentRow.height + rasterizedGlyph.size.y >= activePage.canvas.height) { // Find the first page with room to create the new row on @@ -894,12 +852,10 @@ export class TextureAtlas implements ITextureAtlas { if (candidatePage) { activePage = candidatePage; } else { - /* - * Before creating a new atlas page that would trigger a page merge, check if the - * current active row is sufficient when ignoring the ROW_PIXEL_THRESHOLD. This will - * improve texture utilization by using the available space before the page is merged - * and becomes static. - */ + // Before creating a new atlas page that would trigger a page merge, check if the + // current active row is sufficient when ignoring the ROW_PIXEL_THRESHOLD. This will + // improve texture utilization by using the available space before the page is merged + // and becomes static. if ( TextureAtlas.maxAtlasPages && this._pages.length >= TextureAtlas.maxAtlasPages && @@ -967,10 +923,8 @@ export class TextureAtlas implements ITextureAtlas { rasterizedGlyph.sizeClipSpace.x /= activePage.canvas.width; rasterizedGlyph.sizeClipSpace.y /= activePage.canvas.height; - /* - * Update atlas current row, for fixed rows the glyph height will never be larger than the row - * height - */ + // Update atlas current row, for fixed rows the glyph height will never be larger than the row + // height activeRow.height = Math.max(activeRow.height, rasterizedGlyph.size.y); activeRow.x += rasterizedGlyph.size.x; @@ -1103,18 +1057,16 @@ class AtlasPage { public static nextVersion: number = 0; public version = ++AtlasPage.nextVersion; - /* - * Texture atlas current positioning data. The texture packing strategy used is to fill from - * left-to-right and top-to-bottom. When the glyph being written is less than half of the current - * row's height, the following happens: - * - * - The current row becomes the fixed height row A - * - A new fixed height row B the exact size of the glyph is created below the current row - * - A new dynamic height current row is created below B - * - * This strategy does a good job preventing space being wasted for very short glyphs such as - * underscores, hyphens etc. or those with underlines rendered. - */ + // Texture atlas current positioning data. The texture packing strategy used is to fill from + // left-to-right and top-to-bottom. When the glyph being written is less than half of the current + // row's height, the following happens: + // + // - The current row becomes the fixed height row A + // - A new fixed height row B the exact size of the glyph is created below the current row + // - A new dynamic height current row is created below B + // + // This strategy does a good job preventing space being wasted for very short glyphs such as + // underscores, hyphens etc. or those with underlines rendered. public currentRow: ICharAtlasActiveRow = { x: 0, y: 0, @@ -1134,11 +1086,9 @@ class AtlasPage { } } this.canvas = createCanvas(document, size, size); - /* - * 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 - * set. - */ + // 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 + // set. this.ctx = throwIfFalsy(this.canvas.getContext('2d', { alpha: true })); } @@ -1166,14 +1116,12 @@ function clearColor(imageData: ImageData, bg: IColor, fg: IColor, enableThreshol const fgG = fg.rgba >>> 16 & 0xFF; const fgB = fg.rgba >>> 8 & 0xFF; - /* - * Calculate a threshold that when below a color will be treated as transpart when the sum of - * channel value differs. This helps improve rendering when glyphs overlap with others. This - * threshold is calculated relative to the difference between the background and foreground to - * ensure important details of the glyph are always shown, even when the contrast ratio is low. - * The number 12 is largely arbitrary to ensure the pixels that escape the cell in the test case - * were covered (fg=#8ae234, bg=#c4a000). - */ + // Calculate a threshold that when below a color will be treated as transpart when the sum of + // channel value differs. This helps improve rendering when glyphs overlap with others. This + // threshold is calculated relative to the difference between the background and foreground to + // ensure important details of the glyph are always shown, even when the contrast ratio is low. + // The number 12 is largely arbitrary to ensure the pixels that escape the cell in the test case + // were covered (fg=#8ae234, bg=#c4a000). const threshold = Math.floor((Math.abs(r - fgR) + Math.abs(g - fgG) + Math.abs(b - fgB)) / 12); // Set alpha channel of relevent pixels to 0 diff --git a/addons/addon-webgl/src/WebglRenderer.ts b/addons/addon-webgl/src/WebglRenderer.ts index 2997129f2..500308bd1 100644 --- a/addons/addon-webgl/src/WebglRenderer.ts +++ b/addons/addon-webgl/src/WebglRenderer.ts @@ -84,11 +84,9 @@ export class WebglRenderer extends Disposable implements IRenderer { ) { super(); - /* - * IMPORTANT: Canvas initialization and fetching of the context must be first in order to - * prevent possible listeners leaking and continuing to operate after the WebglRenderer has been - * discarded. - */ + // IMPORTANT: Canvas initialization and fetching of the context must be first in order to + // prevent possible listeners leaking and continuing to operate after the WebglRenderer has been + // discarded. this._canvas = this._coreBrowserService.mainDocument.createElement('canvas'); const contextAttributes = { antialias: false, @@ -127,10 +125,8 @@ export class WebglRenderer extends Disposable implements IRenderer { console.log('webglcontextlost event received'); // Prevent the default behavior in order to enable WebGL context restoration. e.preventDefault(); - /* - * Wait a few seconds to see if the 'webglcontextrestored' event is fired. - * If not, dispatch the onContextLoss notification to observers. - */ + // Wait a few seconds to see if the 'webglcontextrestored' event is fired. + // If not, dispatch the onContextLoss notification to observers. this._contextRestorationTimeout = setTimeout(() => { this._contextRestorationTimeout = undefined; console.warn('webgl context not restored; firing onContextLoss'); @@ -141,10 +137,8 @@ export class WebglRenderer extends Disposable implements IRenderer { console.warn('webglcontextrestored event received'); clearTimeout(this._contextRestorationTimeout); this._contextRestorationTimeout = undefined; - /* - * The texture atlas and glyph renderer must be fully reinitialized - * because their contents have been lost. - */ + // The texture atlas and glyph renderer must be fully reinitialized + // because their contents have been lost. removeTerminalFromCache(this._terminal); this._initializeWebGLState(); this._requestRedrawViewport(); @@ -184,10 +178,8 @@ export class WebglRenderer extends Disposable implements IRenderer { } public handleDevicePixelRatioChange(): void { - /* - * If the device pixel ratio changed, the char atlas needs to be regenerated - * and the terminal needs to refreshed - */ + // If the device pixel ratio changed, the char atlas needs to be regenerated + // and the terminal needs to refreshed if (this._devicePixelRatio !== this._coreBrowserService.dpr) { this._devicePixelRatio = this._coreBrowserService.dpr; this.handleResize(this._terminal.cols, this._terminal.rows); @@ -223,10 +215,8 @@ export class WebglRenderer extends Disposable implements IRenderer { this._refreshCharAtlas(); - /* - * Force a full refresh. Resizing `_glyphRenderer` should clear it already, - * so there is no need to clear it again here. - */ + // Force a full refresh. Resizing `_glyphRenderer` should clear it already, + // so there is no need to clear it again here. this._clearModel(false); // Render synchronously to avoid flicker when the canvas is cleared @@ -378,11 +368,9 @@ export class WebglRenderer extends Disposable implements IRenderer { return; } - /* - * Tell renderer the frame is beginning - * upon a model clear also refresh the full viewport model - * (also triggered by an atlas page merge, part of #4480) - */ + // Tell renderer the frame is beginning + // upon a model clear also refresh the full viewport model + // (also triggered by an atlas page merge, part of #4480) if (this._glyphRenderer.value.beginFrame()) { this._clearModel(true); this._updateModel(0, this._terminal.rows - 1); @@ -391,10 +379,8 @@ export class WebglRenderer extends Disposable implements IRenderer { this._updateModel(start, end); } - /* - * A mid-update atlas page merge invalidates vertex data and may not bump the host - * page's version, so re-run the update and force a full texture rebind. - */ + // A mid-update atlas page merge invalidates vertex data and may not bump the host + // page's version, so re-run the update and force a full texture rebind. let merged = false; let mergeRetries = 0; while (this._charAtlas && this._glyphRenderer.value.beginFrame() && mergeRetries++ < Constants.MERGE_RETRY_LIMIT) { @@ -422,10 +408,8 @@ export class WebglRenderer extends Disposable implements IRenderer { } else { this._cursorBlinkStateManager.clear(); } - /* - * Request a refresh from the terminal as management of rendering is being - * moved back to the terminal - */ + // Request a refresh from the terminal as management of rendering is being + // moved back to the terminal this._requestRedrawCursor(); } @@ -486,26 +470,20 @@ export class WebglRenderer extends Disposable implements IRenderer { // If true, indicates that the current character(s) to draw were joined. isJoined = false; - /* - * Indicates whether this cell is part of a joined range that should be ignored as it cannot - * be rendered entirely, like the selection state differs across the range. - */ + // Indicates whether this cell is part of a joined range that should be ignored as it cannot + // be rendered entirely, like the selection state differs across the range. isValidJoinRange = (x >= skipJoinedCheckUntilX); lastCharX = x; - /* - * Process any joined character ranges as needed. Because of how the - * ranges are produced, we know that they are valid for the characters - * and attributes of our input. - */ + // Process any joined character ranges as needed. Because of how the + // ranges are produced, we know that they are valid for the characters + // and attributes of our input. if (joinedRanges.length > 0 && x === joinedRanges[0][0] && isValidJoinRange) { range = joinedRanges.shift()!; - /* - * If the ligature's selection state is not consistent, don't join it. This helps the - * selection render correctly regardless whether they should be joined. - */ + // If the ligature's selection state is not consistent, don't join it. This helps the + // selection render correctly regardless whether they should be joined. const firstSelectionState = this._model.selection.isCellSelected(this._terminal, range[0], row); for (i = range[0] + 1; i < range[1]; i++) { isValidJoinRange &&= (firstSelectionState === this._model.selection.isCellSelected(this._terminal, i, row)); @@ -517,10 +495,8 @@ export class WebglRenderer extends Disposable implements IRenderer { } else { isJoined = true; - /* - * We already know the exact start and end column of the joined range, - * so we get the string and width representing it directly. - */ + // We already know the exact start and end column of the joined range, + // so we get the string and width representing it directly. cell = new JoinedCellData( cell, line!.translateToString(true, range[0], range[1]), @@ -610,10 +586,8 @@ export class WebglRenderer extends Disposable implements IRenderer { 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 - * supported - */ + // Don't re-resolve the cell color since multi-colored ligature backgrounds are not + // supported this._model.cells[j + RenderModelConstants.BG_OFFSET] = this._cellColorResolver.result.bg; this._model.cells[j + RenderModelConstants.FG_OFFSET] = this._cellColorResolver.result.fg; this._model.cells[j + RenderModelConstants.EXT_OFFSET] = this._cellColorResolver.result.ext; @@ -657,64 +631,48 @@ export class WebglRenderer extends Disposable implements IRenderer { return; } - /* - * Calculate the device character width. Width is floored as it must be drawn to an integer grid - * in order for the char atlas glyphs to not be blurry. - */ + // Calculate the device character width. Width is floored as it must be drawn to an integer grid + // in order for the char atlas glyphs to not be blurry. this.dimensions.device.char.width = Math.floor(this._charSizeService.width * this._devicePixelRatio); - /* - * Calculate the device character height. Height is ceiled in case devicePixelRatio is a - * floating point number in order to ensure there is enough space to draw the character to the - * cell. - */ + // Calculate the device character height. Height is ceiled in case devicePixelRatio is a + // floating point number in order to ensure there is enough space to draw the character to the + // cell. this.dimensions.device.char.height = Math.ceil(this._charSizeService.height * this._devicePixelRatio); - /* - * Calculate the device cell height, if lineHeight is _not_ 1, the resulting value will be - * floored since lineHeight can never be lower then 1, this guarentees the device cell height - * will always be larger than device char height. - */ + // Calculate the device cell height, if lineHeight is _not_ 1, the resulting value will be + // floored since lineHeight can never be lower then 1, this guarentees the device cell height + // will always be larger than device char height. this.dimensions.device.cell.height = Math.floor(this.dimensions.device.char.height * this._optionsService.rawOptions.lineHeight); - /* - * Calculate the y offset within a cell that glyph should draw at in order for it to be centered - * correctly within the cell. - */ + // Calculate the y offset within a cell that glyph should draw at in order for it to be centered + // correctly within the cell. this.dimensions.device.char.top = this._optionsService.rawOptions.lineHeight === 1 ? 0 : Math.round((this.dimensions.device.cell.height - this.dimensions.device.char.height) / 2); // Calculate the device cell width, taking the letterSpacing into account. this.dimensions.device.cell.width = this.dimensions.device.char.width + Math.round(this._optionsService.rawOptions.letterSpacing); - /* - * Calculate the x offset with a cell that text should draw from in order for it to be centered - * correctly within the cell. - */ + // Calculate the x offset with a cell that text should draw from in order for it to be centered + // correctly within the cell. this.dimensions.device.char.left = Math.floor(this._optionsService.rawOptions.letterSpacing / 2); - /* - * Recalculate the canvas dimensions, the device dimensions define the actual number of pixel in - * the canvas - */ + // Recalculate the canvas dimensions, the device dimensions define the actual number of pixel in + // the canvas this.dimensions.device.canvas.height = this._terminal.rows * this.dimensions.device.cell.height; this.dimensions.device.canvas.width = this._terminal.cols * this.dimensions.device.cell.width; - /* - * The the size of the canvas on the page. It's important that this rounds to nearest integer - * and not ceils as browsers often have floating point precision issues where - * `window.devicePixelRatio` ends up being something like `1.100000023841858` for example, when - * it's actually 1.1. Ceiling may causes blurriness as the backing canvas image is 1 pixel too - * large for the canvas element size. - */ + // The the size of the canvas on the page. It's important that this rounds to nearest integer + // and not ceils as browsers often have floating point precision issues where + // `window.devicePixelRatio` ends up being something like `1.100000023841858` for example, when + // it's actually 1.1. Ceiling may causes blurriness as the backing canvas image is 1 pixel too + // large for the canvas element size. this.dimensions.css.canvas.height = Math.round(this.dimensions.device.canvas.height / this._devicePixelRatio); this.dimensions.css.canvas.width = Math.round(this.dimensions.device.canvas.width / this._devicePixelRatio); - /* - * Get the CSS dimensions of an individual cell. This needs to be derived from the calculated - * device pixel canvas value above. CharMeasure.width/height by itself is insufficient when the - * page is not at 100% zoom level as CharMeasure is measured in CSS pixels, but the actual char - * size on the canvas can differ. - */ + // Get the CSS dimensions of an individual cell. This needs to be derived from the calculated + // device pixel canvas value above. CharMeasure.width/height by itself is insufficient when the + // page is not at 100% zoom level as CharMeasure is measured in CSS pixels, but the actual char + // size on the canvas can differ. this.dimensions.css.cell.height = this.dimensions.device.cell.height / this._devicePixelRatio; this.dimensions.css.cell.width = this.dimensions.device.cell.width / this._devicePixelRatio; } @@ -723,10 +681,8 @@ export class WebglRenderer extends Disposable implements IRenderer { if (this._canvas.width === width && this._canvas.height === height) { return; } - /* - * While the actual canvas size has changed, keep device canvas dimensions as the value before - * the change as it's an exact multiple of the cell sizes. - */ + // While the actual canvas size has changed, keep device canvas dimensions as the value before + // the change as it's an exact multiple of the cell sizes. this._canvas.width = width; this._canvas.height = height; // Render synchronously to avoid flicker when the canvas is cleared @@ -746,10 +702,8 @@ export class WebglRenderer extends Disposable implements IRenderer { // TODO: Share impl with core export class JoinedCellData extends AttributeData implements ICellData { private _width: number; - /* - * .content carries no meaning for joined CellData, simply nullify it - * thus we have to overload all other .content accessors - */ + // .content carries no meaning for joined CellData, simply nullify it + // thus we have to overload all other .content accessors public content: number = 0; public fg: number; public bg: number; @@ -777,10 +731,8 @@ export class JoinedCellData extends AttributeData implements ICellData { } public getCode(): number { - /* - * code always gets the highest possible fake codepoint (read as -1) - * this is needed as code is used by caches as identifier - */ + // code always gets the highest possible fake codepoint (read as -1) + // this is needed as code is used by caches as identifier return 0x1FFFFF; } diff --git a/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts b/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts index d2c95723a..c35299809 100644 --- a/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts +++ b/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts @@ -274,17 +274,15 @@ export const customGlyphDefinitions: { [index: string]: CustomGlyphCharacterDefi // #region Powerline Symbols (E0A0-E0BF) - /* - * This contains the definitions of the primarily used box drawing characters as vector shapes. - * The reason these characters are defined specially is to avoid common problems if a user's font - * has not been patched with powerline characters and also to get pixel perfect rendering as - * rendering issues can occur around AA/SPAA. - * - * The line variants draw beyond the cell and get clipped to ensure the end of the line is not - * visible. - * - * Original symbols defined in https://github.com/powerline/fontpatcher - */ + // This contains the definitions of the primarily used box drawing characters as vector shapes. + // The reason these characters are defined specially is to avoid common problems if a user's font + // has not been patched with powerline characters and also to get pixel perfect rendering as + // rendering issues can occur around AA/SPAA. + // + // The line variants draw beyond the cell and get clipped to ensure the end of the line is not + // visible. + // + // Original symbols defined in https://github.com/powerline/fontpatcher // Git branch '\u{E0A0}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M.3,1 L.03,1 L.03,.88 C.03,.82,.06,.78,.11,.73 C.15,.7,.2,.68,.28,.65 L.43,.6 C.49,.58,.53,.56,.56,.53 C.59,.5,.6,.47,.6,.43 L.6,.27 L.4,.27 L.69,.1 L.98,.27 L.78,.27 L.78,.46 C.78,.52,.76,.56,.72,.61 C.68,.66,.63,.67,.56,.7 L.48,.72 C.42,.74,.38,.76,.35,.78 C.32,.8,.31,.84,.31,.88 L.31,1 M.3,.5 L.03,.59 L.03,.09 L.3,.09 L.3,.655', type: CustomGlyphVectorType.FILL } }, @@ -357,10 +355,8 @@ export const customGlyphDefinitions: { [index: string]: CustomGlyphCharacterDefi // #region Progress Indicators (EE00-EE0B) - /* - * initially added in Fira Code and later Nerd Fonts - * https://github.com/ryanoasis/nerd-fonts/pull/1733 - */ + // initially added in Fira Code and later Nerd Fonts + // https://github.com/ryanoasis/nerd-fonts/pull/1733 // Progress bars (EE00-EE05) '\u{EE00}': { type: CustomGlyphDefinitionType.PATH, data: 'M0,0 L1,0 L1,.05 L.1,.05 L.1,.95 L1,.95 L1,1 L0,1 Z' }, // PROGRESS BAR EMPTY START @@ -382,10 +378,8 @@ export const customGlyphDefinitions: { [index: string]: CustomGlyphCharacterDefi // #region Git Branch Symbols (F5D0-F60D) - /* - * Initially added in Kitty (https://github.com/kovidgoyal/kitty/pull/7681) - * then in Wezterm (https://github.com/wezterm/wezterm/issues/6328). - */ + // Initially added in Kitty (https://github.com/kovidgoyal/kitty/pull/7681) + // then in Wezterm (https://github.com/wezterm/wezterm/issues/6328). // Straight lines (F5D0-F5D9) '\u{F5D0}': GitBranchSymbolsParts.LINE_H, // Same as 2500 @@ -439,10 +433,8 @@ export const customGlyphDefinitions: { [index: string]: CustomGlyphCharacterDefi '\u{F5FA}': [GitBranchSymbolsParts.NODE_FILL, GitBranchSymbolsParts.NODE_STROKE, GitBranchSymbolsParts.NODE_LINE_DOWN, GitBranchSymbolsParts.NODE_LINE_UP], '\u{F5FB}': [GitBranchSymbolsParts.NODE_STROKE, GitBranchSymbolsParts.NODE_LINE_DOWN, GitBranchSymbolsParts.NODE_LINE_UP], - /* - * Extended Nodes (F5FC-F60D) - * These were added a little later https://github.com/kovidgoyal/kitty/pull/7805 - */ + // Extended Nodes (F5FC-F60D) + // These were added a little later https://github.com/kovidgoyal/kitty/pull/7805 '\u{F5FC}': [GitBranchSymbolsParts.NODE_FILL, GitBranchSymbolsParts.NODE_STROKE, GitBranchSymbolsParts.NODE_LINE_DOWN, GitBranchSymbolsParts.NODE_LINE_RIGHT], '\u{F5FD}': [GitBranchSymbolsParts.NODE_STROKE, GitBranchSymbolsParts.NODE_LINE_DOWN, GitBranchSymbolsParts.NODE_LINE_RIGHT], '\u{F5FE}': [GitBranchSymbolsParts.NODE_FILL, GitBranchSymbolsParts.NODE_STROKE, GitBranchSymbolsParts.NODE_LINE_DOWN, GitBranchSymbolsParts.NODE_LINE_LEFT], @@ -468,10 +460,8 @@ export const customGlyphDefinitions: { [index: string]: CustomGlyphCharacterDefi // https://www.unicode.org/charts/PDF/U1FB00.pdf - /* - * Block mosaic terminal graphic characters (1FB00-1FB3B) - * The term "sextant" refers to block mosaics divided into six parts. - */ + // Block mosaic terminal graphic characters (1FB00-1FB3B) + // The term "sextant" refers to block mosaics divided into six parts. '\u{1FB00}': sextant(0b000001), // BLOCK SEXTANT-1 '\u{1FB01}': sextant(0b000010), // BLOCK SEXTANT-2 '\u{1FB02}': sextant(0b000011), // BLOCK SEXTANT-12 (upper one third block) @@ -734,10 +724,8 @@ export const customGlyphDefinitions: { [index: string]: CustomGlyphCharacterDefi '\u{1FBB2}': { type: CustomGlyphDefinitionType.PATH, data: 'M.29,.27 L0.13,.56 L.22,.59 L.35,0.35 L.67,.35 L.57,.57 L.71,.76 L.22,.76 L.42,1 L.53,.98 L.43,.86 L.9,.86 L.71,.6 L1,.6 L1,.52 L.83,.52 L.92,.36 L1,.36 L1,.27Z M.99,.13 A.12,.12,0,1,1,.75,.13 A.12,.12,0,1,1,.99,.13' }, // LEFT HALF RUNNING MAN '\u{1FBB3}': { type: CustomGlyphDefinitionType.PATH, data: 'M0,.27 L.3,.27 L.55,.12 L.63,.18 L.33,.36 L0,.36 M0,.52 L.33,.52 L.59,.89 L.73,.89 L.73,.98 L.53,.98 L.28,.6 L0,.6' }, // RIGHT HALF RUNNING MAN - /* - * Arrows (1FBB4-1FBB8) - * TODO: Improve all arrows, use hybrid approach - */ + // Arrows (1FBB4-1FBB8) + // TODO: Improve all arrows, use hybrid approach '\u{1FBB4}': { type: CustomGlyphDefinitionType.PATH_NEGATIVE, data: { d: 'M.15,.55 L.5,.4 L.5,.45 L.65,.45 L.65,.35 L.85,.35 L.85,.625 L.5,.625 L.5,.7 Z', type: CustomGlyphVectorType.FILL }, scaleType: CustomGlyphScaleType.CHAR }, // INVERSE DOWNWARDS ARROW WITH TIP LEFTWARDS '\u{1FBB5}': [ // LEFTWARDS ARROW AND UPPER AND LOWER ONE EIGHTH BLOCK { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M0,0 L1,0 L1,.0625 L0,.0625 Z M0,.9375 L1,.9375 L1,1 L0,1 Z', type: CustomGlyphVectorType.FILL } }, @@ -892,11 +880,9 @@ function sextant(pattern: number): { type: CustomGlyphDefinitionType.PATH_FUNCTI return { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: () => { - /* - * Sextant grid: 2 columns, 3 rows - * Row heights in 8ths: top=3, middle=2, bottom=3 - * Column widths: left=4, right=4 - */ + // Sextant grid: 2 columns, 3 rows + // Row heights in 8ths: top=3, middle=2, bottom=3 + // Column widths: left=4, right=4 const rects: string[] = []; const colW = 0.5; // Each column is half width const rowH = [3 / 8, 2 / 8, 3 / 8]; // Row heights as fractions @@ -946,12 +932,10 @@ function sextant(pattern: number): { type: CustomGlyphDefinitionType.PATH_FUNCTI function segmentedDigit(pattern: number): string { const paths: string[] = []; - /* - * Each segment should have approximately the same stroke width, this is somewhat difficult to be - * precise since coordinates are 0-1 of the whole cell (percentage-based). To handle this, the - * fact that terminal cells are typically sized at ~2:1 (height:width) is leveraged. - * for horizontal vs vertical to make segments appear the same thickness - */ + // Each segment should have approximately the same stroke width, this is somewhat difficult to be + // precise since coordinates are 0-1 of the whole cell (percentage-based). To handle this, the + // fact that terminal cells are typically sized at ~2:1 (height:width) is leveraged. + // for horizontal vs vertical to make segments appear the same thickness const segW = 0.15; // Width of vertical segments (fraction of cell width) const segH = 0.075; // Height of horizontal segments (fraction of cell height, ~half of segW for 2:1 cells) const padX = 0.05; // Horizontal padding from edge diff --git a/addons/addon-webgl/src/customGlyphs/CustomGlyphRasterizer.ts b/addons/addon-webgl/src/customGlyphs/CustomGlyphRasterizer.ts index ad36378cd..ba8925727 100644 --- a/addons/addon-webgl/src/customGlyphs/CustomGlyphRasterizer.ts +++ b/addons/addon-webgl/src/customGlyphs/CustomGlyphRasterizer.ts @@ -489,10 +489,8 @@ function drawPatternChar( pattern = throwIfFalsy(ctx.createPattern(tmpCanvas, null)); patternSet.set(fillStyle, pattern); } - /* - * Apply pattern offset to ensure seamless tiling across cells when cell dimensions are odd. - * variantOffset encodes: bit 1 = x pixel shift, bit 0 = y pixel shift. - */ + // Apply pattern offset to ensure seamless tiling across cells when cell dimensions are odd. + // variantOffset encodes: bit 1 = x pixel shift, bit 0 = y pixel shift. const dx = (variantOffset >> 1) & 1; const dy = variantOffset & 1; if (dx !== 0 || dy !== 0) { @@ -715,10 +713,8 @@ function translateArgs(args: string[], cellWidth: number, cellHeight: number, xO for (let x = 0; x < result.length; x += 2) { // Translate from 0-1 to 0-cellWidth result[x] *= cellWidth - (leftPadding * devicePixelRatio) - (rightPadding * devicePixelRatio); - /* - * Round to the nearest 0.5 to ensure a crisp line at 100% devicePixelRatio, and optionally - * clamp to the cell bounds. - */ + // Round to the nearest 0.5 to ensure a crisp line at 100% devicePixelRatio, and optionally + // clamp to the cell bounds. if (doClamp && result[x] !== 0) { const rounded = Math.round(result[x] + 0.5) - 0.5; result[x] = clampToCell ? clamp(rounded, cellWidth, 0) : rounded; @@ -730,10 +726,8 @@ function translateArgs(args: string[], cellWidth: number, cellHeight: number, xO for (let y = 1; y < result.length; y += 2) { // Translate from 0-1 to 0-cellHeight result[y] *= cellHeight; - /* - * Round to the nearest 0.5 to ensure a crisp line at 100% devicePixelRatio, and optionally - * clamp to the cell bounds. - */ + // Round to the nearest 0.5 to ensure a crisp line at 100% devicePixelRatio, and optionally + // clamp to the cell bounds. if (doClamp && result[y] !== 0) { const rounded = Math.round(result[y] + 0.5) - 0.5; result[y] = clampToCell ? clamp(rounded, cellHeight, 0) : rounded; diff --git a/addons/addon-webgl/test/WebglRenderer.test.ts b/addons/addon-webgl/test/WebglRenderer.test.ts index b39521180..c425fa000 100644 --- a/addons/addon-webgl/test/WebglRenderer.test.ts +++ b/addons/addon-webgl/test/WebglRenderer.test.ts @@ -24,11 +24,9 @@ test.beforeAll(async ({ browser }) => { test.afterAll(async () => await ctx.page.close()); test.describe('WebGL Renderer Integration Tests', async () => { - /* - * HACK: webgl2 is often not supported in headless firefox on Linux - * https://github.com/microsoft/playwright/issues/11566 - * also disable safari due to #5852 - */ + // HACK: webgl2 is often not supported in headless firefox on Linux + // https://github.com/microsoft/playwright/issues/11566 + // also disable safari due to #5852 if (platform() === 'linux') { test.skip(({ browserName }) => browserName === 'firefox' || browserName === 'webkit'); } diff --git a/demo/client/client.ts b/demo/client/client.ts index b1718f64f..aab723b7e 100644 --- a/demo/client/client.ts +++ b/demo/client/client.ts @@ -173,11 +173,9 @@ const createNewWindowButtonHandler: () => void = () => { terminalContainer.id = 'terminal-container'; win.document.body.appendChild(terminalContainer); - /* - * Stylesheets are needed to get the terminal in the popout window to render - * correctly. We also need to wait for them to load before creating the - * terminal, otherwise we will not compute the correct metrics when rendering. - */ + // Stylesheets are needed to get the terminal in the popout window to render + // correctly. We also need to wait for them to load before creating the + // terminal, otherwise we will not compute the correct metrics when rendering. let pendingStylesheets = 0; for (const linkNode of document.querySelectorAll('head link[rel=stylesheet]')) { const newLink = document.createElement('link'); @@ -361,10 +359,8 @@ function createTerminal(): Terminal { const resizeObserver = new ResizeObserver(entries => { if (optionsWindow.autoResize) { - /* - * In general this should be debounced to avoid excessive work on the main - * thread by firing the expensive resize action repeatedly - */ + // In general this should be debounced to avoid excessive work on the main + // thread by firing the expensive resize action repeatedly addons.fit.instance!.fit(); } }); @@ -551,10 +547,8 @@ function initAddons(term: Terminal): void { addon.instance = undefined; } if (name === 'ligatures') { - /* - * Recreate webgl when ligatures are toggled so texture atlas picks up any font feature - * settings changes - */ + // Recreate webgl when ligatures are toggled so texture atlas picks up any font feature + // settings changes if (addons.webgl.instance) { preDisposeWebgl(); addons.webgl.instance.dispose(); diff --git a/demo/client/components/window/addonImageWindow.ts b/demo/client/components/window/addonImageWindow.ts index c089cf5f7..392b7177b 100644 --- a/demo/client/components/window/addonImageWindow.ts +++ b/demo/client/components/window/addonImageWindow.ts @@ -165,18 +165,16 @@ export class AddonImageWindow extends BaseWindow implements IControlWindow { this._terminal.element!.addEventListener('click', (ev: MouseEvent) => { if (!ev.ctrlKey || !imageAddon) return; - /* - * TODO... - * if (ev.altKey) { - * const sel = term.getSelectionPosition(); - * if (sel) { - * addons.image.instance - * .extractCanvasAtBufferRange(term.getSelectionPosition()) - * ?.toBlob(data => window.open(URL.createObjectURL(data), '_blank')); - * return; - * } - * } - */ + // TODO... + // if (ev.altKey) { + // const sel = term.getSelectionPosition(); + // if (sel) { + // addons.image.instance + // .extractCanvasAtBufferRange(term.getSelectionPosition()) + // ?.toBlob(data => window.open(URL.createObjectURL(data), '_blank')); + // return; + // } + // } const pos = (this._terminal as any)._core._mouseService!.getCoords(ev, (this._terminal as any)._core.screenElement!, this._terminal.cols, this._terminal.rows); const x = pos[0] - 1; diff --git a/demo/client/components/window/addonProgressWindow.ts b/demo/client/components/window/addonProgressWindow.ts index fe4e51ca7..167c9ca28 100644 --- a/demo/client/components/window/addonProgressWindow.ts +++ b/demo/client/components/window/addonProgressWindow.ts @@ -66,13 +66,11 @@ export class AddonProgressWindow extends BaseWindow implements IControlWindow { const colors = { 0: '', 1: 'green', 2: 'red', 3: '', 4: 'yellow' }; function progressHandler({ state, value }: IProgressState): void { - /* - * Simulate windows taskbar hack by windows terminal: - * Since the taskbar has no means to indicate error/pause state other than by coloring - * the current progress, we move 0 to 10% and distribute higher values in the remaining 90 % - * NOTE: This is most likely not what you want to do for other progress indicators, - * that have a proper visual state for error/paused. - */ + // Simulate windows taskbar hack by windows terminal: + // Since the taskbar has no means to indicate error/pause state other than by coloring + // the current progress, we move 0 to 10% and distribute higher values in the remaining 90 % + // NOTE: This is most likely not what you want to do for other progress indicators, + // that have a proper visual state for error/paused. value = Math.min(10 + value * 0.9, 100); document.getElementById('progress-percent')!.style.width = `${value}%`; document.getElementById('progress-percent')!.style.backgroundColor = colors[state]; diff --git a/demo/client/components/window/testWindow.ts b/demo/client/components/window/testWindow.ts index e66795ab1..1ea6b3e67 100644 --- a/demo/client/components/window/testWindow.ts +++ b/demo/client/components/window/testWindow.ts @@ -435,11 +435,9 @@ function customGlyphAlignmentHandler(term: Terminal): void { } function customGlyphRangesHandler(term: Terminal): void { - /* - * Box Drawing - * 2500-257F - * https://www.unicode.org/charts/PDF/U2500.pdf - */ + // Box Drawing + // 2500-257F + // https://www.unicode.org/charts/PDF/U2500.pdf writeUnicodeTable(term, 'Box Drawing', 0x2500, 0x257F, [ ['Light and heavy solid lines', 0x2500, 0x2503], ['Light and heavy dashed lines', 0x2504, 0x250B], @@ -452,49 +450,39 @@ function customGlyphRangesHandler(term: Terminal): void { ['Light and heavy half lines', 0x2574, 0x257B], ['Mixed light and heavy lines', 0x257C, 0x257F], ]); - /* - * Box Elements - * 2580-259F - * https://www.unicode.org/charts/PDF/U2580.pdf - */ + // Box Elements + // 2580-259F + // https://www.unicode.org/charts/PDF/U2580.pdf writeUnicodeTable(term, 'Box Elements', 0x2580, 0x259F, [ ['Block elements', 0x2580, 0x2590], ['Shade characters', 0x2591, 0x2593], ['Block elements', 0x2594, 0x2595], ['Terminal graphic characters', 0x2596, 0x259F], ]); - /* - * Braille Patterns - * 2800-28FF - * https://www.unicode.org/charts/PDF/U2800.pdf - */ + // Braille Patterns + // 2800-28FF + // https://www.unicode.org/charts/PDF/U2800.pdf writeUnicodeTable(term, 'Braille patterns', 0x2800, 0x28FF, [ ['Braille patterns', 0x2800, 0x28FF], ]); - /* - * Powerline Symbols - * Range: E0A0–E0D4 - * https://github.com/ryanoasis/nerd-fonts - */ + // Powerline Symbols + // Range: E0A0–E0D4 + // https://github.com/ryanoasis/nerd-fonts writeUnicodeTable(term, 'Powerline Symbols', 0xE0A0, 0xE0D4, [ ['Powerline symbols', 0xE0A0, 0xE0B3, [0xE0A4, 0xE0A5, 0xE0A6, 0xE0A7, 0xE0A8, 0xE0A9, 0xE0AA, 0xE0AB, 0xE0AC, 0xE0AD, 0xE0AE, 0xE0AF]], ['Powerline extra symbols', 0xE0B4, 0xE0D4, [0xE0C9, 0xE0CB, 0xE0D3]], ]); - /* - * Progress Indicators - * Range: EE00-EE0B - * https://github.com/tonsky/FiraCode - */ + // Progress Indicators + // Range: EE00-EE0B + // https://github.com/tonsky/FiraCode writeUnicodeTable(term, 'Progress Indicators', 0xEE00, 0xEE0B, [ ['Progress bars', 0xEE00, 0xEE05], ['Progress spinners', 0xEE06, 0xEE0B], ]); - /* - * https://github.com/ryanoasis/nerd-fonts/pull/1733 - * Git Branch Symbols - * F5D0-F60D - * https://github.com/xtermjs/xterm.js/issues/5477 - */ + // https://github.com/ryanoasis/nerd-fonts/pull/1733 + // Git Branch Symbols + // F5D0-F60D + // https://github.com/xtermjs/xterm.js/issues/5477 writeUnicodeTable(term, 'Git Branch Symbols', 0xF5D0, 0xF5FB, [ ['Straight lines', 0xF5D0, 0xF5D5], ['Curved lines', 0xF5D6, 0xF5D9], @@ -502,11 +490,9 @@ function customGlyphRangesHandler(term: Terminal): void { ['Nodes', 0xF5EE, 0xF5FB], ['Extended nodes', 0xF5FC, 0xF60D], ]); - /* - * Symbols for Legacy Computing - * Range: 1FB00–1FBFF - * https://www.unicode.org/charts/PDF/U1FB00.pdf - */ + // Symbols for Legacy Computing + // Range: 1FB00–1FBFF + // https://www.unicode.org/charts/PDF/U1FB00.pdf writeUnicodeTable(term, 'Symbols for Legacy Computing', 0x1FB00, 0x1FBFF, [ ['Block mosaic terminal graphic characters (Sextants)', 0x1FB00, 0x1FB3B], ['Smooth mosaic terminal graphic characters', 0x1FB3C, 0x1FB6F], @@ -704,10 +690,8 @@ function loadTest(term: Terminal, addons: AddonCollection): void { for (let i = 1; i < count + 1; i++) { data[i] = 0x61 + Math.floor(Math.random() * (0x7A - 0x61)); } - /* - * End each line with \r so the cursor remains constant, this is what ls/tree do and improves - * performance significantly due to the cursor DOM element not needing to change - */ + // End each line with \r so the cursor remains constant, this is what ls/tree do and improves + // performance significantly due to the cursor DOM element not needing to change data[data.length - 1] = 0x0D; // \r testData.push(data); } @@ -739,10 +723,8 @@ async function loadTestLongLines(term: Terminal, addons: AddonCollection): Promi for (let i = 1; i < count + 1; i++) { data[i] = 0x61 + Math.floor(Math.random() * (0x7A - 0x61)); } - /* - * End each line with \r so the cursor remains constant, this is what ls/tree do and improves - * performance significantly due to the cursor DOM element not needing to change - */ + // End each line with \r so the cursor remains constant, this is what ls/tree do and improves + // performance significantly due to the cursor DOM element not needing to change data[data.length - 1] = 0x0D; // \r testData.push(data); } @@ -752,10 +734,8 @@ async function loadTestLongLines(term: Terminal, addons: AddonCollection): Promi try { term.write(d); } catch { - /* - * Flush events when cap is hit and try again(workaround for not having flow control in - * demo) - */ + // Flush events when cap is hit and try again(workaround for not having flow control in + // demo) await new Promise(r => term.write('', () => r())); term.write(d); } diff --git a/demo/client/unicodeTable.ts b/demo/client/unicodeTable.ts index 9755b911a..61bba36f2 100644 --- a/demo/client/unicodeTable.ts +++ b/demo/client/unicodeTable.ts @@ -220,10 +220,8 @@ export function writeUnicodeTable(term: Terminal, name: string, start: number, e term.write('\n\r'); - /* - * Render reserved labels that appear after the first label (below the row) - * Show one label per non-contiguous reserved range - */ + // Render reserved labels that appear after the first label (below the row) + // Show one label per non-contiguous reserved range const lateReserved = rowLabels.length > 0 ? rowReserved.filter(r => r.col >= rowLabels[0].col) : rowReserved; diff --git a/demo/server/server.ts b/demo/server/server.ts index 5bd594dcb..ef98eb84c 100644 --- a/demo/server/server.ts +++ b/demo/server/server.ts @@ -180,11 +180,9 @@ function startServer(): void { } const send = (USE_BINARY ? bufferUtf8 : buffer)(ws, 3, 262144); - /* - * WARNING: This is a naive implementation that will not throttle the flow of data. This means - * it could flood the communication channel and make the terminal unresponsive. Learn more about - * the problem and how to implement flow control at https://xtermjs.org/docs/guides/flowcontrol/ - */ + // WARNING: This is a naive implementation that will not throttle the flow of data. This means + // it could flood the communication channel and make the terminal unresponsive. Learn more about + // the problem and how to implement flow control at https://xtermjs.org/docs/guides/flowcontrol/ term.onData(function(data) { try { send(data as string & Buffer); @@ -211,10 +209,8 @@ function startServer(): void { app.listen(port, host, 0); } -/* - * HACK: There is an EPIPE error thrown when reloading a page. This only seems to happen on Windows - * and it's unclear why it happens. Suppressing the error here since this is just the demo server. - */ +// HACK: There is an EPIPE error thrown when reloading a page. This only seems to happen on Windows +// and it's unclear why it happens. Suppressing the error here since this is just the demo server. process.on('uncaughtException', (error) => { if (process.platform === 'win32' && error.message === 'read EPIPE') { return; diff --git a/eslint.config.mjs b/eslint.config.mjs index 18a4dfb59..e3d8a4dba 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -86,9 +86,9 @@ export default tseslint.config( 'eqeqeq': ['warn', 'always'], 'jsdoc/check-alignment': 'warn', 'jsdoc/check-param-names': 'warn', + 'jsdoc/multiline-blocks': 'warn', 'jsdoc/no-multi-asterisks': 'warn', 'keyword-spacing': 'warn', - 'multiline-comment-style': ['warn', 'starred-block'], 'max-len': ['warn', { code: 1000, comments: 100, diff --git a/src/browser/AccessibilityManager.ts b/src/browser/AccessibilityManager.ts index 5960c8d4f..6fc421337 100644 --- a/src/browser/AccessibilityManager.ts +++ b/src/browser/AccessibilityManager.ts @@ -21,10 +21,8 @@ const enum BoundaryPosition { BOTTOM } -/* - * Turn this on to unhide the accessibility tree and display it under - * (instead of overlapping with) the terminal. - */ +// Turn this on to unhide the accessibility tree and display it under +// (instead of overlapping with) the terminal. const DEBUG = false; export class AccessibilityManager extends Disposable { @@ -225,10 +223,8 @@ export class AccessibilityManager extends Disposable { return; } - /* - * Don't scroll when the last focused item was not the second row (focus is going the other - * direction) - */ + // Don't scroll when the last focused item was not the second row (focus is going the other + // direction) if (e.relatedTarget !== beforeBoundaryElement) { return; } @@ -287,11 +283,9 @@ export class AccessibilityManager extends Disposable { } if (selection.isCollapsed) { - /* - * Only do something when the anchorNode is inside the row container. This - * behavior mirrors what we do with mouse --- if the mouse clicks - * somewhere outside of the terminal, we don't clear the selection. - */ + // Only do something when the anchorNode is inside the row container. This + // behavior mirrors what we do with mouse --- if the mouse clicks + // somewhere outside of the terminal, we don't clear the selection. if (this._rowContainer.contains(selection.anchorNode)) { this._terminal.clearSelection(); } diff --git a/src/browser/Clipboard.ts b/src/browser/Clipboard.ts index 513dfa60c..aaf8a48e4 100644 --- a/src/browser/Clipboard.ts +++ b/src/browser/Clipboard.ts @@ -22,10 +22,8 @@ export function bracketTextForPaste(text: string, bracketedPasteMode: boolean): if (!bracketedPasteMode) { return text; } - /* - * Sanitize pasted text to prevent injected escape sequences (e.g. exiting bracketed paste) - * by replacing ESC (\x1b) with its visible representation U+241B (␛). - */ + // Sanitize pasted text to prevent injected escape sequences (e.g. exiting bracketed paste) + // by replacing ESC (\x1b) with its visible representation U+241B (␛). const sanitizedText = text.replace(/\x1b/g, '\u241b'); return `\x1b[200~${sanitizedText}\x1b[201~`; } diff --git a/src/browser/CoreBrowserTerminal.ts b/src/browser/CoreBrowserTerminal.ts index 519d8b1dc..52eb00025 100644 --- a/src/browser/CoreBrowserTerminal.ts +++ b/src/browser/CoreBrowserTerminal.ts @@ -323,10 +323,8 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal { * Binds the desired blur behavior on a given terminal object. */ private _handleTextAreaBlur(): void { - /* - * Text can safely be removed on blur. Doing it earlier could interfere with - * screen readers reading it out. - */ + // Text can safely be removed on blur. Doing it earlier could interfere with + // screen readers reading it out. this.textarea!.value = ''; this.refresh(this.buffer.y, this.buffer.y); if (this.coreService.decPrivateModes.sendFocus) { @@ -352,10 +350,8 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal { const cursorTop = this.buffer.y * this._renderService.dimensions.css.cell.height; const cursorLeft = cursorX * this._renderService.dimensions.css.cell.width; - /* - * Sync the textarea to the exact position of the composition view so the IME knows where the - * text is. - */ + // Sync the textarea to the exact position of the composition view so the IME knows where the + // text is. this.textarea.style.left = cursorLeft + 'px'; this.textarea.style.top = cursorTop + 'px'; this.textarea.style.width = cellWidth + 'px'; @@ -372,10 +368,8 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal { // Bind clipboard functionality this._register(addDisposableListener(this.element!, 'copy', (event: ClipboardEvent) => { - /* - * If mouse events are active it means the selection manager is disabled and - * copy should be handled by the host program. - */ + // If mouse events are active it means the selection manager is disabled and + // copy should be handled by the host program. if (!this.hasSelection()) { return; } @@ -399,16 +393,12 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal { })); } - /* - * Move the textarea under the cursor when middle clicking on Linux to ensure - * middle click to paste selection works. This only appears to work in Chrome - * at the time is writing. - */ + // Move the textarea under the cursor when middle clicking on Linux to ensure + // middle click to paste selection works. This only appears to work in Chrome + // at the time is writing. if (Browser.isLinux) { - /* - * Use auxclick event over mousedown the latter doesn't seem to work. Note - * that the regular click event doesn't fire for the middle mouse button. - */ + // Use auxclick event over mousedown the latter doesn't seem to work. Note + // that the regular click event doesn't fire for the middle mouse button. this._register(addDisposableListener(this.element!, 'auxclick', (event: MouseEvent) => { if (event.button === 1) { moveTextAreaUnderMouseCursor(event, this.textarea!, this.screenElement!); @@ -425,13 +415,11 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal { this._register(addDisposableListener(this.textarea!, 'keydown', (ev: KeyboardEvent) => this._keyDown(ev), true)); this._register(addDisposableListener(this.textarea!, 'keypress', (ev: KeyboardEvent) => this._keyPress(ev), true)); this._register(addDisposableListener(this.textarea!, 'compositionstart', () => { - /* - * Ensure the textarea is synced to the latest cursor location before composition begins. This - * is to workaround a problem where highly dynamic TUIs like agentic CLIs reprint agressively - * would cause the IME to appear in the wrong position. The theory is that when the IME is - * triggered during a partial render the textarea position becomes locked and will not move - * until it is hidden and a custom move occurs. - */ + // Ensure the textarea is synced to the latest cursor location before composition begins. This + // is to workaround a problem where highly dynamic TUIs like agentic CLIs reprint agressively + // would cause the IME to appear in the wrong position. The theory is that when the IME is + // triggered during a partial render the textarea position becomes locked and will not move + // until it is hidden and a custom move occurs. this._syncTextArea(); this._compositionHelper!.compositionstart(); this._compositionHelper!.updateCompositionElements(); @@ -479,10 +467,8 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal { this._register(this.optionsService.onSpecificOptionChange('allowTransparency', value => this.element!.classList.toggle('allow-transparency', value))); parent.appendChild(this.element); - /* - * Performance: Use a document fragment to build the terminal - * viewport and helper elements detached from the DOM - */ + // Performance: Use a document fragment to build the terminal + // viewport and helper elements detached from the DOM const fragment = this._document.createDocumentFragment(); this._viewportElement = this._document.createElement('div'); this._viewportElement.classList.add('xterm-viewport'); @@ -491,10 +477,8 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal { this.screenElement = this._document.createElement('div'); this.screenElement.classList.add('xterm-screen'); this._register(addDisposableListener(this.screenElement, 'mousemove', (ev: MouseEvent) => this.updateCursorStyle(ev))); - /* - * Create the container that will hold helpers like the textarea for - * capturing DOM Events. Then produce the helpers. - */ + // Create the container that will hold helpers like the textarea for + // capturing DOM Events. Then produce the helpers. this._helperContainer = this._document.createElement('div'); this._helperContainer.classList.add('xterm-helpers'); this.screenElement.appendChild(this._helperContainer); @@ -504,10 +488,8 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal { this.textarea.classList.add('xterm-helper-textarea'); this.textarea.setAttribute('aria-label', Strings.promptLabel.get()); if (!Browser.isChromeOS) { - /* - * ChromeVox on ChromeOS does not like this. See - * https://issuetracker.google.com/issues/260170397 - */ + // ChromeVox on ChromeOS does not like this. See + // https://issuetracker.google.com/issues/260170397 this.textarea.setAttribute('aria-multiline', 'false'); } this.textarea.setAttribute('autocorrect', 'off'); @@ -517,10 +499,8 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal { this._register(this.optionsService.onSpecificOptionChange('disableStdin', () => textarea.readOnly = this.optionsService.rawOptions.disableStdin)); this.textarea.readOnly = this.optionsService.rawOptions.disableStdin; - /* - * Register the core browser service before the generic textarea handlers are registered so it - * handles them first. Otherwise the renderers may use the wrong focus state. - */ + // Register the core browser service before the generic textarea handlers are registered so it + // handles them first. Otherwise the renderers may use the wrong focus state. this._coreBrowserService = this._register(this._instantiationService.createInstance(CoreBrowserService, this.textarea, parent.ownerDocument.defaultView ?? window, @@ -618,11 +598,9 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal { this._register(this._selectionService.onSelectionChange(() => this._onSelectionChange.fire())); this._register(this._selectionService.onRequestRedraw(e => this._renderService!.handleSelectionChanged(e.start, e.end, e.columnSelectMode))); this._register(this._selectionService.onLinuxMouseSelection(text => { - /* - * If there's a new selection, put it into the textarea, focus and select it - * in order to register it as a selection on the OS. This event is fired - * only on Linux to enable middle click to paste selection. - */ + // If there's a new selection, put it into the textarea, focus and select it + // in order to register it as a selection on the OS. This event is fired + // only on Linux to enable middle click to paste selection. this.textarea!.value = text; this.textarea!.focus(); this.textarea!.select(); @@ -647,10 +625,8 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal { } if (this.options.screenReaderMode) { - /* - * Note that this must be done *after* the renderer is created in order to - * ensure the correct order of the dprchange event - */ + // Note that this must be done *after* the renderer is created in order to + // ensure the correct order of the dprchange event this._accessibilityManager.value = this._instantiationService.createInstance(AccessibilityManager, this); } this._register(this.optionsService.onSpecificOptionChange('screenReaderMode', e => this._handleScreenReaderModeOptionChange(e))); @@ -675,10 +651,8 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal { // Initialize global actions that need to be taken on the document. this._initGlobal(); - /* - * Listen for mouse events and translate - * them into terminal mouse protocols. - */ + // Listen for mouse events and translate + // them into terminal mouse protocols. this._mouseService.bindMouse({ element: this.element!, screenElement: this.screenElement!, @@ -918,11 +892,9 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal { return true; } - /* - * HACK: Process A-Z in the keypress event to fix an issue with macOS IMEs where lower case - * letters cannot be input while caps lock is on. Skip this hack when using kitty protocol - * or Win32 input mode as they need to send proper sequences for all key events. - */ + // HACK: Process A-Z in the keypress event to fix an issue with macOS IMEs where lower case + // letters cannot be input while caps lock is on. Skip this hack when using kitty protocol + // or Win32 input mode as they need to send proper sequences for all key events. if (!this._keyboardService.useKitty && !this._keyboardService.useWin32InputMode && event.key && !event.ctrlKey && !event.altKey && !event.metaKey && event.key.length === 1) { if (event.key.charCodeAt(0) >= 65 && event.key.charCodeAt(0) <= 90) { return true; @@ -934,11 +906,9 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal { return true; } - /* - * If ctrl+c or enter is being sent, clear out the textarea. This is done so that screen readers - * will announce deleted characters. This will not work 100% of the time but it should cover - * most scenarios. - */ + // If ctrl+c or enter is being sent, clear out the textarea. This is done so that screen readers + // will announce deleted characters. This will not work 100% of the time but it should cover + // most scenarios. if (result.key === C0.ETX || result.key === C0.CR) { this.textarea!.value = ''; } @@ -948,12 +918,10 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal { this._showCursor(); this.coreService.triggerDataEvent(result.key, !wasModifierOnly); - /* - * Cancel events when not in screen reader mode so events don't get bubbled up and handled by - * other listeners. When screen reader mode is enabled, we don't cancel them (unless ctrl or alt - * is also depressed) so that the cursor textarea can be updated, which triggers the screen - * reader to read it. - */ + // Cancel events when not in screen reader mode so events don't get bubbled up and handled by + // other listeners. When screen reader mode is enabled, we don't cancel them (unless ctrl or alt + // is also depressed) so that the cursor textarea can be updated, which triggers the screen + // reader to read it. if (!this.optionsService.rawOptions.screenReaderMode || event.altKey || event.ctrlKey) { event.preventDefault(); event.stopPropagation(); @@ -1042,10 +1010,8 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal { this._keyPressHandled = true; - /* - * The key was handled so clear the dead key state, otherwise certain keystrokes like arrow - * keys could be ignored - */ + // The key was handled so clear the dead key state, otherwise certain keystrokes like arrow + // keys could be ignored this._unprocessedDeadKey = false; return true; @@ -1058,20 +1024,16 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal { * @param ev The input event to be handled. */ protected _inputEvent(ev: InputEvent): boolean { - /* - * Only support emoji IMEs when screen reader mode is disabled as the event must bubble up to - * support reading out character input which can doubling up input characters - * Based on these event traces: https://github.com/xtermjs/xterm.js/issues/3679 - */ + // Only support emoji IMEs when screen reader mode is disabled as the event must bubble up to + // support reading out character input which can doubling up input characters + // Based on these event traces: https://github.com/xtermjs/xterm.js/issues/3679 if (ev.data && ev.inputType === 'insertText' && (!ev.composed || !this._keyDownSeen) && !this.optionsService.rawOptions.screenReaderMode) { if (this._keyPressHandled) { return false; } - /* - * The key was handled so clear the dead key state, otherwise certain keystrokes like arrow - * keys could be ignored - */ + // The key was handled so clear the dead key state, otherwise certain keystrokes like arrow + // keys could be ignored this._unprocessedDeadKey = false; const text = ev.data; @@ -1121,10 +1083,8 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal { for (let i = 1; i < this.rows; i++) { this.buffer.lines.push(this.buffer.getBlankLine(DEFAULT_ATTR_DATA)); } - /* - * IMPORTANT: Fire scroll event before viewport is reset. This ensures embedders get the clear - * scroll event and that the viewport's state will be valid for immediate writes. - */ + // IMPORTANT: Fire scroll event before viewport is reset. This ensures embedders get the clear + // scroll event and that the viewport's state will be valid for immediate writes. this._onScroll.fire({ position: this.buffer.ydisp }); this.refresh(0, this.rows - 1); } diff --git a/src/browser/Linkifier.ts b/src/browser/Linkifier.ts index e99c41dfa..31c91daee 100644 --- a/src/browser/Linkifier.ts +++ b/src/browser/Linkifier.ts @@ -87,11 +87,9 @@ export class Linkifier extends Disposable implements ILinkifier2 { } private _handleHover(position: IBufferCellPosition): void { - /* - * TODO: This currently does not cache link provider results across wrapped lines, activeLine - * should be something like `activeRange: {startY, endY}` - * Check if we need to clear the link - */ + // TODO: This currently does not cache link provider results across wrapped lines, activeLine + // should be something like `activeRange: {startY, endY}` + // Check if we need to clear the link if (this._activeLine !== position.y || this._wasResized) { this._clearCurrentLink(); this._askForLink(position, false); @@ -127,11 +125,9 @@ export class Linkifier extends Disposable implements ILinkifier2 { const existingReply = this._activeProviderReplies?.get(i); // If there isn't a reply, the provider hasn't responded yet. - /* - * TODO: If there isn't a reply yet it means that the provider is still resolving. Ensuring - * provideLinks isn't triggered again saves ILink.hover firing twice though. This probably - * needs promises to get fixed - */ + // TODO: If there isn't a reply yet it means that the provider is still resolving. Ensuring + // provideLinks isn't triggered again saves ILink.hover firing twice though. This probably + // needs promises to get fixed if (existingReply) { linkProvided = this._checkLinkProviderResult(i, position, linkProvided); } @@ -144,10 +140,8 @@ export class Linkifier extends Disposable implements ILinkifier2 { this._activeProviderReplies?.set(i, linksWithState); linkProvided = this._checkLinkProviderResult(i, position, linkProvided); - /* - * If all providers have responded, remove lower priority links that intersect ranges of - * higher priority links - */ + // If all providers have responded, remove lower priority links that intersect ranges of + // higher priority links if (this._activeProviderReplies?.size === this._linkProviderService.linkProviders.length) { this._removeIntersectingLinks(position.y, this._activeProviderReplies); } @@ -193,10 +187,8 @@ export class Linkifier extends Disposable implements ILinkifier2 { } } - /* - * If all providers with higher priority came back undefined, then this provider's link for - * the position should be used - */ + // If all providers with higher priority came back undefined, then this provider's link for + // the position should be used if (!hasLinkBefore && links) { const linkAtPosition = links.find(link => this._linkAtPosition(link.link, position)); if (linkAtPosition) { @@ -304,19 +296,15 @@ export class Linkifier extends Disposable implements ILinkifier2 { } }); - /* - * Listen to viewport changes to re-render the link under the cursor (only when the line the - * link is on changes) - */ + // Listen to viewport changes to re-render the link under the cursor (only when the line the + // link is on changes) this._linkCacheDisposables.push(this._renderService.onRenderedViewportChange(e => { // Sanity check, this shouldn't happen in practice as this listener would be disposed if (!this._currentLink) { return; } - /* - * When start is 0 a scroll most likely occurred, make sure links above the fold also get - * cleared. - */ + // When start is 0 a scroll most likely occurred, make sure links above the fold also get + // cleared. const start = e.start === 0 ? 0 : e.start + 1 + this._bufferService.buffer.ydisp; const end = this._bufferService.buffer.ydisp + 1 + e.end; // Only clear the link if the viewport change happened on this line diff --git a/src/browser/OscLinkProvider.ts b/src/browser/OscLinkProvider.ts index 8ed823203..b6a3cce0a 100644 --- a/src/browser/OscLinkProvider.ts +++ b/src/browser/OscLinkProvider.ts @@ -33,10 +33,8 @@ export class OscLinkProvider implements ILinkProvider { let currentStart = -1; let finishLink = false; for (let x = 0; x < lineLength; x++) { - /* - * Minor optimization, only check for content if there isn't a link in case the link ends with - * a null cell - */ + // Minor optimization, only check for content if there isn't a link in case the link ends with + // a null cell if (currentStart === -1 && !line.hasContent(x)) { continue; } @@ -109,10 +107,8 @@ export class OscLinkProvider implements ILinkProvider { } } - /* - * TODO: Handle fetching and returning other link ranges to underline other links with the same - * id - */ + // TODO: Handle fetching and returning other link ranges to underline other links with the same + // id callback(result); } } diff --git a/src/browser/Terminal.test.ts b/src/browser/Terminal.test.ts index 425955982..d359fa28c 100644 --- a/src/browser/Terminal.test.ts +++ b/src/browser/Terminal.test.ts @@ -576,16 +576,14 @@ describe('Terminal', () => { beforeEach(() => { term.clearSelection = () => { }; - /* - * term.compositionHelper = { - * isComposing: false, - * keydown: { - * bind: () => { - * return () => { return true; }; - * } - * } - * }; - */ + // term.compositionHelper = { + // isComposing: false, + // keydown: { + // bind: () => { + // return () => { return true; }; + // } + // } + // }; evKeyDown = { preventDefault: () => { }, stopPropagation: () => { }, @@ -1150,10 +1148,8 @@ describe('Terminal', () => { // FIXME: move to common/CoreTerminal.test once the trimming is moved over describe('marker lifecycle', () => { - /* - * create a 10x5 terminal with markers on every line - * to test marker lifecycle under various terminal actions - */ + // create a 10x5 terminal with markers on every line + // to test marker lifecycle under various terminal actions let markers: IMarker[]; let disposeStack: IMarker[]; let term: TestTerminal; diff --git a/src/browser/TimeBasedDebouncer.ts b/src/browser/TimeBasedDebouncer.ts index 8d609cb23..997312901 100644 --- a/src/browser/TimeBasedDebouncer.ts +++ b/src/browser/TimeBasedDebouncer.ts @@ -43,10 +43,8 @@ export class TimeBasedDebouncer implements IRenderDebouncer { this._rowStart = this._rowStart !== undefined ? Math.min(this._rowStart, rowStart) : rowStart; this._rowEnd = this._rowEnd !== undefined ? Math.max(this._rowEnd, rowEnd) : rowEnd; - /* - * Only refresh if the time since last refresh is above a threshold, otherwise wait for - * enough time to pass before refreshing again. - */ + // Only refresh if the time since last refresh is above a threshold, otherwise wait for + // enough time to pass before refreshing again. const refreshRequestTime: number = performance.now(); if (refreshRequestTime - this._lastRefreshMs >= this._debounceThresholdMS) { // Enough time has lapsed since the last refresh; refresh immediately diff --git a/src/browser/Types.ts b/src/browser/Types.ts index 9c76fb07d..fa08de2e3 100644 --- a/src/browser/Types.ts +++ b/src/browser/Types.ts @@ -201,10 +201,8 @@ export const DEFAULT_ANSI_COLORS = Object.freeze((() => { css.toColor('#eeeeec') ]; - /* - * Fill in the remaining 240 ANSI colors. - * Generate colors (16-231) - */ + // Fill in the remaining 240 ANSI colors. + // Generate colors (16-231) const v = [0x00, 0x5f, 0x87, 0xaf, 0xd7, 0xff]; for (let i = 0; i < 216; i++) { const r = v[(i / 36) % 6 | 0]; diff --git a/src/browser/Viewport.ts b/src/browser/Viewport.ts index c629bc7a3..3b6ae8301 100644 --- a/src/browser/Viewport.ts +++ b/src/browser/Viewport.ts @@ -99,20 +99,16 @@ export class Viewport extends Disposable { this._register(this._bufferService.onResize(() => this.queueSync())); this._register(this._bufferService.buffers.onBufferActivate(() => { - /* - * Reset _latestYDisp when switching buffers to prevent stale scroll position - * from alt buffer contaminating normal buffer scroll position - */ + // Reset _latestYDisp when switching buffers to prevent stale scroll position + // from alt buffer contaminating normal buffer scroll position this._latestYDisp = undefined; this.queueSync(); })); this._register(this._bufferService.onScroll(() => this._sync())); - /* - * Flush deferred viewport sync after a render completes (e.g. after ESU ends - * synchronized output mode). This ensures DOM scroll position updates atomically - * with the canvas render. - */ + // Flush deferred viewport sync after a render completes (e.g. after ESU ends + // synchronized output mode). This ensures DOM scroll position updates atomically + // with the canvas render. this._register(this._renderService.onRender(() => { if (this._needsSyncOnRender) { this._needsSyncOnRender = false; @@ -177,20 +173,16 @@ export class Viewport extends Disposable { if (!this._renderService || this._isSyncing) { return; } - /* - * Defer DOM scroll updates during synchronized output to prevent visible - * scroll position flickering while the canvas content is frozen. - */ + // Defer DOM scroll updates during synchronized output to prevent visible + // scroll position flickering while the canvas content is frozen. if (this._coreService.decPrivateModes.synchronizedOutput) { this._needsSyncOnRender = true; return; } this._isSyncing = true; - /* - * Ignore any onScroll event that happens as a result of dimensions changing as this should - * never cause a scrollLines call, only setScrollPosition can do that. - */ + // Ignore any onScroll event that happens as a result of dimensions changing as this should + // never cause a scrollLines call, only setScrollPosition can do that. this._suppressOnScrollHandler = true; this._scrollableElement.setScrollDimensions({ height: this._renderService.dimensions.css.canvas.height, @@ -198,10 +190,8 @@ export class Viewport extends Disposable { }); this._suppressOnScrollHandler = false; - /* - * If ydisp has been changed by some other component (input/buffer), then stop animating smooth - * scroll and scroll there immediately. - */ + // If ydisp has been changed by some other component (input/buffer), then stop animating smooth + // scroll and scroll there immediately. if (ydisp !== this._latestYDisp) { this._scrollableElement.setScrollPosition({ scrollTop: ydisp * this._renderService.dimensions.css.cell.height diff --git a/src/browser/decorations/BufferDecorationRenderer.ts b/src/browser/decorations/BufferDecorationRenderer.ts index fb4e047a7..e374af260 100644 --- a/src/browser/decorations/BufferDecorationRenderer.ts +++ b/src/browser/decorations/BufferDecorationRenderer.ts @@ -1,9 +1,7 @@ -/* - *--------------------------------------------------------------------------------------------- +/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. - *-------------------------------------------------------------------------------------------- - */ + *--------------------------------------------------------------------------------------------*/ import { ICoreBrowserService, IRenderService } from 'browser/services/Services'; import { Disposable, toDisposable } from 'common/Lifecycle'; diff --git a/src/browser/decorations/ColorZoneStore.ts b/src/browser/decorations/ColorZoneStore.ts index d1795ab8e..d066bedb8 100644 --- a/src/browser/decorations/ColorZoneStore.ts +++ b/src/browser/decorations/ColorZoneStore.ts @@ -1,9 +1,7 @@ -/* - *--------------------------------------------------------------------------------------------- +/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. - *-------------------------------------------------------------------------------------------- - */ + *--------------------------------------------------------------------------------------------*/ import { IInternalDecoration } from 'common/services/Services'; @@ -34,11 +32,9 @@ interface IMinimalDecorationForColorZone { export class ColorZoneStore implements IColorZoneStore { private _zones: IColorZone[] = []; - /* - * The zone pool is used to keep zone objects from being freed between clearing the color zone - * store and fetching the zones. This helps reduce GC pressure since the color zones are - * accumulated on potentially every scroll event. - */ + // The zone pool is used to keep zone objects from being freed between clearing the color zone + // store and fetching the zones. This helps reduce GC pressure since the color zones are + // accumulated on potentially every scroll event. private _zonePool: IColorZone[] = []; private _zonePoolIndex = 0; diff --git a/src/browser/decorations/OverviewRulerRenderer.ts b/src/browser/decorations/OverviewRulerRenderer.ts index 8ea3000b3..b28b25965 100644 --- a/src/browser/decorations/OverviewRulerRenderer.ts +++ b/src/browser/decorations/OverviewRulerRenderer.ts @@ -1,9 +1,7 @@ -/* - *--------------------------------------------------------------------------------------------- +/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. - *-------------------------------------------------------------------------------------------- - */ + *--------------------------------------------------------------------------------------------*/ import { ColorZoneStore, IColorZone, IColorZoneStore } from 'browser/decorations/ColorZoneStore'; import { ICoreBrowserService, IRenderService, IThemeService } from 'browser/services/Services'; @@ -14,10 +12,8 @@ const enum Constants { OVERVIEW_RULER_BORDER_WIDTH = 1 } -/* - * Helper objects to avoid excessive calculation and garbage collection during rendering. These are - * static values for each render and can be accessed using the decoration position as the key. - */ +// Helper objects to avoid excessive calculation and garbage collection during rendering. These are +// static values for each render and can be accessed using the decoration position as the key. const drawHeight = { full: 0, left: 0, diff --git a/src/browser/input/CompositionHelper.test.ts b/src/browser/input/CompositionHelper.test.ts index 512d5a773..94b5841af 100644 --- a/src/browser/input/CompositionHelper.test.ts +++ b/src/browser/input/CompositionHelper.test.ts @@ -236,11 +236,9 @@ describe('CompositionHelper', () => { it('Should insert middle composition and subsequent input without appending existing trailing text', (done) => { textarea.value = '一二'; - /* - * screenReaderMode keeps textarea content/selection for assistive technologies (eg. screen - * readers), so the caret can be moved within the textarea (eg. via arrow keys) before - * starting composition. - */ + // screenReaderMode keeps textarea content/selection for assistive technologies (eg. screen + // readers), so the caret can be moved within the textarea (eg. via arrow keys) before + // starting composition. textarea.selectionStart = 1; textarea.selectionEnd = 1; diff --git a/src/browser/input/CompositionHelper.ts b/src/browser/input/CompositionHelper.ts index f211e2553..283874139 100644 --- a/src/browser/input/CompositionHelper.ts +++ b/src/browser/input/CompositionHelper.ts @@ -72,10 +72,8 @@ export class CompositionHelper { */ public compositionstart(): void { this._isComposing = true; - /* - * It's important to use the selection here instead of textarea length to avoid conflicts with - * screen reader mode - */ + // It's important to use the selection here instead of textarea length to avoid conflicts with + // screen reader mode const start = this._textarea.selectionStart ?? this._textarea.value.length; const end = this._textarea.selectionEnd ?? start; this._compositionPosition.start = Math.min(start, end); @@ -91,10 +89,8 @@ export class CompositionHelper { * @param ev The event. */ public compositionupdate(ev: Pick): void { - /* - * Mark text as LTR, direction=rtl is used in CSS so the end of the text is followed for long - * compositions - */ + // Mark text as LTR, direction=rtl is used in CSS so the end of the text is followed for long + // compositions this._compositionView.textContent = `\u200E${ev.data}\u200E`; this.updateCompositionElements(); setTimeout(() => { @@ -119,28 +115,22 @@ export class CompositionHelper { public keydown(ev: KeyboardEvent): boolean { if (this._isComposing || this._isSendingComposition) { if (ev.keyCode === 20 || ev.keyCode === 229) { - /* - * 20 is CapsLock, 229 is Enter - * Continue composing if the keyCode is the "composition character" - */ + // 20 is CapsLock, 229 is Enter + // Continue composing if the keyCode is the "composition character" return false; } if (ev.keyCode === 16 || ev.keyCode === 17 || ev.keyCode === 18) { // Continue composing if the keyCode is a modifier key return false; } - /* - * Finish composition immediately. This is mainly here for the case where enter is - * pressed and the handler needs to be triggered before the command is executed. - */ + // Finish composition immediately. This is mainly here for the case where enter is + // pressed and the handler needs to be triggered before the command is executed. this._finalizeComposition(false); } if (ev.keyCode === 229) { - /* - * If the "composition character" is used but gets to this point it means a non-composition - * character (eg. numbers and punctuation) was pressed when the IME was active. - */ + // If the "composition character" is used but gets to this point it means a non-composition + // character (eg. numbers and punctuation) was pressed when the IME was active. this._handleAnyTextareaChanges(); return false; } @@ -166,49 +156,39 @@ export class CompositionHelper { const input = this._textarea.value.substring(this._compositionPosition.start, this._compositionPosition.end); this._coreService.triggerDataEvent(input, true); } else { - /* - * Make a deep copy of the composition position here as a new compositionstart event may - * fire before the setTimeout executes. - */ + // Make a deep copy of the composition position here as a new compositionstart event may + // fire before the setTimeout executes. const currentCompositionPosition = { start: this._compositionPosition.start, end: this._compositionPosition.end }; const currentCompositionSuffix = this._compositionSuffix; - /* - * Since composition* events happen before the changes take place in the textarea on most - * browsers, use a setTimeout with 0ms time to allow the native compositionend event to - * complete. This ensures the correct character is retrieved. - * This solution was used because: - * - The compositionend event's data property is unreliable, at least on Chromium - * - The last compositionupdate event's data property does not always accurately describe - * the character, a counter example being Korean where an ending consonsant can move to - * the following character if the following input is a vowel. - */ + // Since composition* events happen before the changes take place in the textarea on most + // browsers, use a setTimeout with 0ms time to allow the native compositionend event to + // complete. This ensures the correct character is retrieved. + // This solution was used because: + // - The compositionend event's data property is unreliable, at least on Chromium + // - The last compositionupdate event's data property does not always accurately describe + // the character, a counter example being Korean where an ending consonsant can move to + // the following character if the following input is a vowel. this._isSendingComposition = true; setTimeout(() => { // Ensure that the input has not already been sent if (this._isSendingComposition) { this._isSendingComposition = false; let input; - /* - * Add length of data already sent due to keydown event, - * otherwise input characters can be duplicated. (Issue #3191) - */ + // Add length of data already sent due to keydown event, + // otherwise input characters can be duplicated. (Issue #3191) currentCompositionPosition.start += this._dataAlreadySent.length; if (this._isComposing) { - /* - * Use the start position of the new composition to get the string - * if a new composition has started. - */ + // Use the start position of the new composition to get the string + // if a new composition has started. input = this._textarea.value.substring(currentCompositionPosition.start, this._compositionPosition.start); } else { - /* - * Keep support for non-composition characters typed immediately after composition end - * while avoiding re-sending the trailing text that was already present - * before composition started. - */ + // Keep support for non-composition characters typed immediately after composition end + // while avoiding re-sending the trailing text that was already present + // before composition started. const value = this._textarea.value; const valueEnd = currentCompositionSuffix.length > 0 && value.endsWith(currentCompositionSuffix) ? value.length - currentCompositionSuffix.length @@ -280,18 +260,14 @@ export class CompositionHelper { this._compositionView.style.lineHeight = cellHeight + 'px'; this._compositionView.style.fontFamily = this._optionsService.rawOptions.fontFamily; this._compositionView.style.fontSize = this._optionsService.rawOptions.fontSize + 'px'; - /* - * Limit the composition view width to the space between the cursor and - * the terminal's right edge, preventing it from overflowing the terminal. - */ + // Limit the composition view width to the space between the cursor and + // the terminal's right edge, preventing it from overflowing the terminal. const maxWidth = this._bufferService.cols * this._renderService.dimensions.css.cell.width - cursorLeft; this._compositionView.style.maxWidth = maxWidth + 'px'; this._compositionView.style.overflow = 'hidden'; this._compositionView.style.direction = 'rtl'; - /* - * Sync the textarea to the exact position of the composition view so the IME knows where the - * text is. - */ + // Sync the textarea to the exact position of the composition view so the IME knows where the + // text is. const compositionViewBounds = this._compositionView.getBoundingClientRect(); this._textarea.style.left = cursorLeft + 'px'; this._textarea.style.top = cursorTop + 'px'; diff --git a/src/browser/input/Mouse.ts b/src/browser/input/Mouse.ts index 1094cdcf4..c40a7cc78 100644 --- a/src/browser/input/Mouse.ts +++ b/src/browser/input/Mouse.ts @@ -44,11 +44,9 @@ export function getCoords(window: Pick, event: Pick< coords[0] = Math.ceil((coords[0] + (isSelection ? cssCellWidth / 2 : 0)) / cssCellWidth); coords[1] = Math.ceil(coords[1] / cssCellHeight); - /* - * Ensure coordinates are within the terminal viewport. Note that selections - * need an addition point of precision to cover the end point (as characters - * cover half of one char and half of the next). - */ + // Ensure coordinates are within the terminal viewport. Note that selections + // need an addition point of precision to cover the end point (as characters + // cover half of one char and half of the next). coords[0] = Math.min(Math.max(coords[0], 1), colCount + (isSelection ? 1 : 0)); coords[1] = Math.min(Math.max(coords[1], 1), rowCount); diff --git a/src/browser/public/Terminal.ts b/src/browser/public/Terminal.ts index 9255f67f2..75aada048 100644 --- a/src/browser/public/Terminal.ts +++ b/src/browser/public/Terminal.ts @@ -54,11 +54,9 @@ export class Terminal extends Disposable implements ITerminalApi { } private _checkReadonlyOptions(propName: string): void { - /* - * Throw an error if any constructor only option is modified - * from terminal.options - * Modifications from anywhere else are allowed - */ + // Throw an error if any constructor only option is modified + // from terminal.options + // Modifications from anywhere else are allowed if (CONSTRUCTOR_ONLY_OPTIONS.includes(propName)) { throw new Error(`Option "${propName}" can only be set in the constructor`); } diff --git a/src/browser/renderer/dom/DomRenderer.ts b/src/browser/renderer/dom/DomRenderer.ts index 943906524..6f873af7b 100644 --- a/src/browser/renderer/dom/DomRenderer.ts +++ b/src/browser/renderer/dom/DomRenderer.ts @@ -114,10 +114,8 @@ export class DomRenderer extends Disposable implements IRenderer { this._register(toDisposable(() => { this._element.classList.remove(Constants.TERMINAL_CLASS_PREFIX + this._terminalClass); - /* - * Outside influences such as React unmounts may manipulate the DOM before our disposal. - * https://github.com/xtermjs/xterm.js/issues/2960 - */ + // Outside influences such as React unmounts may manipulate the DOM before our disposal. + // https://github.com/xtermjs/xterm.js/issues/2960 this._rowContainer.remove(); this._selectionContainer.remove(); this._widthCache.dispose(); @@ -186,11 +184,9 @@ export class DomRenderer extends Disposable implements IRenderer { // Base CSS let styles = `${this._terminalSelector} .${Constants.ROW_CONTAINER_CLASS} {` + - /* - * Disabling pointer events circumvents a browser behavior that prevents `click` events from - * being delivered if the target element is replaced during the click. This happened due to - * refresh() being called during the mousedown handler to start a selection. - */ + // Disabling pointer events circumvents a browser behavior that prevents `click` events from + // being delivered if the target element is replaced during the click. This happened due to + // refresh() being called during the mousedown handler to start a selection. ` pointer-events: none;` + ` color: ${colors.foreground.css};` + `}`; @@ -261,11 +257,9 @@ export class DomRenderer extends Disposable implements IRenderer { `${this._terminalSelector} .${Constants.ROW_CONTAINER_CLASS}.${Constants.CURSOR_BLINK_IDLE_CLASS} .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_BLINK_CLASS} {` + ` animation: none !important;` + `}` + - /* - * !important helps fix an issue where the cursor will not render on top of the selection, - * however it's very hard to fix this issue and retain the blink animation without the use of - * !important. So this edge case fails when cursor blink is on. - */ + // !important helps fix an issue where the cursor will not render on top of the selection, + // however it's very hard to fix this issue and retain the blink animation without the use of + // !important. So this edge case fails when cursor blink is on. `${this._terminalSelector} .${Constants.ROW_CONTAINER_CLASS} .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_STYLE_BLOCK_CLASS} {` + ` background-color: ${colors.cursor.css};` + ` color: ${colors.cursorAccent.css};` + diff --git a/src/browser/renderer/dom/DomRendererRowFactory.test.ts b/src/browser/renderer/dom/DomRendererRowFactory.test.ts index 4bd5b8e7b..6ca84569a 100644 --- a/src/browser/renderer/dom/DomRendererRowFactory.test.ts +++ b/src/browser/renderer/dom/DomRendererRowFactory.test.ts @@ -359,10 +359,8 @@ describe('DomRendererRowFactory', () => { }); describe('createRow with merged spans', () => { - /* - * for test purpose assume all in codepoints 0..255 are merging - * const ALL_MERGING = new Uint8Array(FontMetrics.MAX); - */ + // for test purpose assume all in codepoints 0..255 are merging + // const ALL_MERGING = new Uint8Array(FontMetrics.MAX); beforeEach(() => { lineData = createEmptyLineData(10); diff --git a/src/browser/renderer/dom/DomRendererRowFactory.ts b/src/browser/renderer/dom/DomRendererRowFactory.ts index fc03e27b3..0cff9c25f 100644 --- a/src/browser/renderer/dom/DomRendererRowFactory.ts +++ b/src/browser/renderer/dom/DomRendererRowFactory.ts @@ -115,26 +115,20 @@ export class DomRendererRowFactory { // If true, indicates that the current character(s) to draw were joined. let isJoined = false; - /* - * Indicates whether this cell is part of a joined range that should be ignored as it cannot - * be rendered entirely, like the selection state differs across the range. - */ + // Indicates whether this cell is part of a joined range that should be ignored as it cannot + // be rendered entirely, like the selection state differs across the range. let isValidJoinRange = (x >= skipJoinedCheckUntilX); let lastCharX = x; - /* - * Process any joined character ranges as needed. Because of how the - * ranges are produced, we know that they are valid for the characters - * and attributes of our input. - */ + // Process any joined character ranges as needed. Because of how the + // ranges are produced, we know that they are valid for the characters + // and attributes of our input. let cell: ICellData = this._workCell; if (joinedRanges.length > 0 && x === joinedRanges[0][0] && isValidJoinRange) { const range = joinedRanges.shift()!; - /* - * If the ligature's selection state is not consistent, don't join it. This helps the - * selection render correctly regardless whether they should be joined. - */ + // If the ligature's selection state is not consistent, don't join it. This helps the + // selection render correctly regardless whether they should be joined. const firstSelectionState = this._isCellInSelection(range[0], row); for (i = range[0] + 1; i < range[1]; i++) { isValidJoinRange &&= (firstSelectionState === this._isCellInSelection(i, row)); @@ -146,10 +140,8 @@ export class DomRendererRowFactory { } else { isJoined = true; - /* - * We already know the exact start and end column of the joined range, - * so we get the string and width representing it directly - */ + // We already know the exact start and end column of the joined range, + // so we get the string and width representing it directly cell = new JoinedCellData( this._workCell, lineData.translateToString(true, range[0], range[1]), @@ -251,11 +243,9 @@ export class DomRendererRowFactory { oldIsInSelection = isInSelection; if (isJoined) { - /* - * The DOM renderer colors the background of the cursor but for ligatures all cells are - * joined. The workaround here is to show a cursor around the whole ligature so it shows up, - * the cursor looks the same when on any character of the ligature though - */ + // The DOM renderer colors the background of the cursor but for ligatures all cells are + // joined. The workaround here is to show a cursor around the whole ligature so it shows up, + // the cursor looks the same when on any character of the ligature though if (cursorX >= x && cursorX <= lastCharX) { cursorX = x; } @@ -343,10 +333,8 @@ export class DomRendererRowFactory { classes.push(RowCss.STRIKETHROUGH_CLASS); } - /* - * apply link hover underline late, effectively overrides any previous text-decoration - * settings - */ + // apply link hover underline late, effectively overrides any previous text-decoration + // settings if (isLinkHover) { charElement.style.textDecoration = 'underline'; } @@ -365,10 +353,8 @@ export class DomRendererRowFactory { bgColorMode = temp2; } - /* - * Apply any decoration foreground/background overrides, this must happen after inverse has - * been applied - */ + // Apply any decoration foreground/background overrides, this must happen after inverse has + // been applied let bgOverride: IColor | undefined; let fgOverride: IColor | undefined; let isTop = false; @@ -391,19 +377,15 @@ export class DomRendererRowFactory { // Apply selection if (!isTop && isInSelection) { - /* - * If in the selection, force the element to be above the selection to improve contrast and - * support opaque selections. The applies background is not actually needed here as - * selection is drawn in a seperate container, the main purpose of this to ensuring minimum - * contrast ratio - */ + // If in the selection, force the element to be above the selection to improve contrast and + // support opaque selections. The applies background is not actually needed here as + // selection is drawn in a seperate container, the main purpose of this to ensuring minimum + // contrast ratio bgOverride = this._coreBrowserService.isFocused ? colors.selectionBackgroundOpaque : colors.selectionInactiveBackgroundOpaque; bg = bgOverride.rgba >> 8 & 0xFFFFFF; bgColorMode = Attributes.CM_RGB; - /* - * Since an opaque selection is being rendered, the selection pretends to be a decoration to - * ensure text is drawn above the selection. - */ + // Since an opaque selection is being rendered, the selection pretends to be a decoration to + // ensure text is drawn above the selection. isTop = true; // Apply selection foreground if applicable if (colors.selectionForeground) { @@ -477,11 +459,9 @@ export class DomRendererRowFactory { } } - /* - * apply CSS classes - * slightly faster than using classList by omitting - * checks for doubled entries (code above should not have doublets) - */ + // apply CSS classes + // slightly faster than using classList by omitting + // checks for doubled entries (code above should not have doublets) if (classes.length) { charElement.className = classes.join(' '); classes.length = 0; @@ -524,10 +504,8 @@ export class DomRendererRowFactory { // Calculate and store in cache if (adjustedColor === undefined) { - /* - * Dim cells only require half the contrast, otherwise they wouldn't be distinguishable from - * non-dim cells - */ + // Dim cells only require half the contrast, otherwise they wouldn't be distinguishable from + // non-dim cells const ratio = this._optionsService.rawOptions.minimumContrastRatio / (cell.isDim() ? 2 : 1); adjustedColor = color.ensureContrastRatio(bgOverride ?? bg, fgOverride ?? fg, ratio); cache.setColor((bgOverride ?? bg).rgba, (fgOverride ?? fg).rgba, adjustedColor ?? null); diff --git a/src/browser/renderer/dom/WidthCache.ts b/src/browser/renderer/dom/WidthCache.ts index 1bf66204d..b598bbd6f 100644 --- a/src/browser/renderer/dom/WidthCache.ts +++ b/src/browser/renderer/dom/WidthCache.ts @@ -31,20 +31,16 @@ export interface IWidthCacheFontVariantCanvas { } export class WidthCache implements IDisposable { - /* - * flat cache for regular variant up to CacheSettings.FLAT_SIZE - * NOTE: ~4x faster access than holey (serving >>80% of terminal content) - * It has a small memory footprint (only 1MB for full BMP caching), - * still the sweet spot is not reached before touching 32k different codepoints, - * thus we store the remaining <<20% of terminal data in a holey structure. - */ + // flat cache for regular variant up to CacheSettings.FLAT_SIZE + // NOTE: ~4x faster access than holey (serving >>80% of terminal content) + // It has a small memory footprint (only 1MB for full BMP caching), + // still the sweet spot is not reached before touching 32k different codepoints, + // thus we store the remaining <<20% of terminal data in a holey structure. protected _flat = new Float32Array(WidthCacheSettings.FLAT_SIZE); - /* - * holey cache for bold, italic and bold&italic for any string - * FIXME: can grow really big over time (~8.5 MB for full BMP caching), - * so a shared API across terminals is needed - */ + // holey cache for bold, italic and bold&italic for any string + // FIXME: can grow really big over time (~8.5 MB for full BMP caching), + // so a shared API across terminals is needed protected _holey: Map | undefined; private _font = ''; diff --git a/src/browser/renderer/shared/RendererUtils.test.ts b/src/browser/renderer/shared/RendererUtils.test.ts index 87f6a3f20..4512cb6d9 100644 --- a/src/browser/renderer/shared/RendererUtils.test.ts +++ b/src/browser/renderer/shared/RendererUtils.test.ts @@ -13,10 +13,8 @@ describe('RendererUtils', () => { let line = 1; let variantOffset = 0; - /* - * should line 1 - * =,_,=_,=_, - */ + // should line 1 + // =,_,=_,=_, let cells = [cellWidth, cellWidth, doubleCellWidth, doubleCellWidth]; let result = [1, 0, 0, 0]; for (let index = 0; index < cells.length; index++) { @@ -25,10 +23,8 @@ describe('RendererUtils', () => { assert.equal(variantOffset, result[index]); } - /* - * should line 2 - * ==__==__==_,_==__==__==,__==__==__==__==__==__,==__==__==__==__==__==, - */ + // should line 2 + // ==__==__==_,_==__==__==,__==__==__==__==__==__,==__==__==__==__==__==, line = 2; variantOffset = 0; cells = [cellWidth, cellWidth, doubleCellWidth, doubleCellWidth]; @@ -39,10 +35,8 @@ describe('RendererUtils', () => { assert.equal(variantOffset, result[index]); } - /* - * should line 3 - * ===___===__,_===___===_,__===___===___===___==,=___===___===___===___, - */ + // should line 3 + // ===___===__,_===___===_,__===___===___===___==,=___===___===___===___, line = 3; variantOffset = 0; cells = [cellWidth, cellWidth, doubleCellWidth, doubleCellWidth]; diff --git a/src/browser/renderer/shared/RendererUtils.ts b/src/browser/renderer/shared/RendererUtils.ts index 30925f32f..010643648 100644 --- a/src/browser/renderer/shared/RendererUtils.ts +++ b/src/browser/renderer/shared/RendererUtils.ts @@ -13,11 +13,9 @@ export function throwIfFalsy(value: T | undefined | null): T { } export function isPowerlineGlyph(codepoint: number): boolean { - /* - * Only return true for Powerline symbols which require - * different padding and should be excluded from minimum contrast - * ratio standards - */ + // Only return true for Powerline symbols which require + // different padding and should be excluded from minimum contrast + // ratio standards return 0xE0A4 <= codepoint && codepoint <= 0xE0D6; } @@ -50,10 +48,8 @@ export function allowRescaling(codepoint: number | undefined, width: number, gly return ( // Is single cell width width === 1 && - /* - * Glyph exceeds cell bounds, add 50% to avoid hurting readability by rescaling glyphs that - * barely overlap - */ + // Glyph exceeds cell bounds, add 50% to avoid hurting readability by rescaling glyphs that + // barely overlap glyphSizeX > Math.ceil(deviceCellWidth * 1.5) && // Never rescale ascii codepoint !== undefined && codepoint > 0xFF && diff --git a/src/browser/scrollable/abstractScrollbar.ts b/src/browser/scrollable/abstractScrollbar.ts index 0463471b5..64c25d9d8 100644 --- a/src/browser/scrollable/abstractScrollbar.ts +++ b/src/browser/scrollable/abstractScrollbar.ts @@ -1,9 +1,7 @@ -/* - *--------------------------------------------------------------------------------------------- +/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. - *-------------------------------------------------------------------------------------------- - */ + *--------------------------------------------------------------------------------------------*/ import * as dom from '../Dom'; import { FastDomNode } from './fastDomNode'; diff --git a/src/browser/scrollable/fastDomNode.ts b/src/browser/scrollable/fastDomNode.ts index 206416677..1a795c2be 100644 --- a/src/browser/scrollable/fastDomNode.ts +++ b/src/browser/scrollable/fastDomNode.ts @@ -1,9 +1,7 @@ -/* - *--------------------------------------------------------------------------------------------- +/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. - *-------------------------------------------------------------------------------------------- - */ + *--------------------------------------------------------------------------------------------*/ export class FastDomNode { diff --git a/src/browser/scrollable/globalPointerMoveMonitor.ts b/src/browser/scrollable/globalPointerMoveMonitor.ts index b2e9b588a..a80e0507f 100644 --- a/src/browser/scrollable/globalPointerMoveMonitor.ts +++ b/src/browser/scrollable/globalPointerMoveMonitor.ts @@ -1,9 +1,7 @@ -/* - *--------------------------------------------------------------------------------------------- +/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. - *-------------------------------------------------------------------------------------------- - */ + *--------------------------------------------------------------------------------------------*/ import * as dom from '../Dom'; import { DisposableStore, IDisposable, toDisposable } from 'common/Lifecycle'; diff --git a/src/browser/scrollable/horizontalScrollbar.ts b/src/browser/scrollable/horizontalScrollbar.ts index 8e48ea7ff..299a3e473 100644 --- a/src/browser/scrollable/horizontalScrollbar.ts +++ b/src/browser/scrollable/horizontalScrollbar.ts @@ -1,9 +1,7 @@ -/* - *--------------------------------------------------------------------------------------------- +/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. - *-------------------------------------------------------------------------------------------- - */ + *--------------------------------------------------------------------------------------------*/ import { AbstractScrollbar, ISimplifiedPointerEvent, IScrollbarHost } from './abstractScrollbar'; import { IScrollableElementResolvedOptions } from './scrollableElementOptions'; diff --git a/src/browser/scrollable/mouseEvent.ts b/src/browser/scrollable/mouseEvent.ts index 482ba82aa..a12b63d52 100644 --- a/src/browser/scrollable/mouseEvent.ts +++ b/src/browser/scrollable/mouseEvent.ts @@ -1,9 +1,7 @@ -/* - *--------------------------------------------------------------------------------------------- +/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. - *-------------------------------------------------------------------------------------------- - */ + *--------------------------------------------------------------------------------------------*/ import * as platform from 'common/Platform'; diff --git a/src/browser/scrollable/scrollable.ts b/src/browser/scrollable/scrollable.ts index 9f8cbb6f7..18275ffe5 100644 --- a/src/browser/scrollable/scrollable.ts +++ b/src/browser/scrollable/scrollable.ts @@ -1,9 +1,7 @@ -/* - *--------------------------------------------------------------------------------------------- +/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. - *-------------------------------------------------------------------------------------------- - */ + *--------------------------------------------------------------------------------------------*/ import { Emitter, IEvent } from 'common/Event'; import { Disposable, IDisposable } from 'common/Lifecycle'; diff --git a/src/browser/scrollable/scrollableElement.ts b/src/browser/scrollable/scrollableElement.ts index a41dde991..4039d0c4e 100644 --- a/src/browser/scrollable/scrollableElement.ts +++ b/src/browser/scrollable/scrollableElement.ts @@ -1,9 +1,7 @@ -/* - *--------------------------------------------------------------------------------------------- +/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. - *-------------------------------------------------------------------------------------------- - */ + *--------------------------------------------------------------------------------------------*/ import * as dom from '../Dom'; import { FastDomNode } from './fastDomNode'; diff --git a/src/browser/scrollable/scrollableElementOptions.ts b/src/browser/scrollable/scrollableElementOptions.ts index 0e1d06226..037cd5c11 100644 --- a/src/browser/scrollable/scrollableElementOptions.ts +++ b/src/browser/scrollable/scrollableElementOptions.ts @@ -1,9 +1,7 @@ -/* - *--------------------------------------------------------------------------------------------- +/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. - *-------------------------------------------------------------------------------------------- - */ + *--------------------------------------------------------------------------------------------*/ import { ScrollbarVisibility } from './scrollable'; diff --git a/src/browser/scrollable/scrollbarArrow.ts b/src/browser/scrollable/scrollbarArrow.ts index 5784dc8dc..d543669c1 100644 --- a/src/browser/scrollable/scrollbarArrow.ts +++ b/src/browser/scrollable/scrollbarArrow.ts @@ -1,9 +1,7 @@ -/* - *--------------------------------------------------------------------------------------------- +/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. - *-------------------------------------------------------------------------------------------- - */ + *--------------------------------------------------------------------------------------------*/ import { GlobalPointerMoveMonitor } from './globalPointerMoveMonitor'; import { Widget } from './widget'; diff --git a/src/browser/scrollable/scrollbarState.ts b/src/browser/scrollable/scrollbarState.ts index 7b7751f97..f9129f1b0 100644 --- a/src/browser/scrollable/scrollbarState.ts +++ b/src/browser/scrollable/scrollbarState.ts @@ -1,9 +1,7 @@ -/* - *--------------------------------------------------------------------------------------------- +/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. - *-------------------------------------------------------------------------------------------- - */ + *--------------------------------------------------------------------------------------------*/ /** * The minimal size of the slider (such that it can still be clickable). diff --git a/src/browser/scrollable/scrollbarVisibilityController.ts b/src/browser/scrollable/scrollbarVisibilityController.ts index b56e3524f..59d172bce 100644 --- a/src/browser/scrollable/scrollbarVisibilityController.ts +++ b/src/browser/scrollable/scrollbarVisibilityController.ts @@ -1,9 +1,7 @@ -/* - *--------------------------------------------------------------------------------------------- +/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. - *-------------------------------------------------------------------------------------------- - */ + *--------------------------------------------------------------------------------------------*/ import { FastDomNode } from './fastDomNode'; import { TimeoutTimer } from 'common/Async'; diff --git a/src/browser/scrollable/touch.ts b/src/browser/scrollable/touch.ts index a734a79a1..8ffdfe699 100644 --- a/src/browser/scrollable/touch.ts +++ b/src/browser/scrollable/touch.ts @@ -1,9 +1,7 @@ -/* - *--------------------------------------------------------------------------------------------- +/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. - *-------------------------------------------------------------------------------------------- - */ + *--------------------------------------------------------------------------------------------*/ import * as DomUtils from '../Dom'; import { Disposable, IDisposable, toDisposable } from 'common/Lifecycle'; diff --git a/src/browser/scrollable/verticalScrollbar.ts b/src/browser/scrollable/verticalScrollbar.ts index 9f5a090af..615f443a0 100644 --- a/src/browser/scrollable/verticalScrollbar.ts +++ b/src/browser/scrollable/verticalScrollbar.ts @@ -1,9 +1,7 @@ -/* - *--------------------------------------------------------------------------------------------- +/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. - *-------------------------------------------------------------------------------------------- - */ + *--------------------------------------------------------------------------------------------*/ import { AbstractScrollbar, ISimplifiedPointerEvent, IScrollbarHost } from './abstractScrollbar'; import { IScrollableElementResolvedOptions } from './scrollableElementOptions'; diff --git a/src/browser/scrollable/widget.ts b/src/browser/scrollable/widget.ts index eae4ec185..80220c9b1 100644 --- a/src/browser/scrollable/widget.ts +++ b/src/browser/scrollable/widget.ts @@ -1,9 +1,7 @@ -/* - *--------------------------------------------------------------------------------------------- +/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. - *-------------------------------------------------------------------------------------------- - */ + *--------------------------------------------------------------------------------------------*/ import * as dom from '../Dom'; import { IMouseEvent, StandardMouseEvent } from './mouseEvent'; diff --git a/src/browser/services/CharSizeService.ts b/src/browser/services/CharSizeService.ts index 8d97f233c..6baf9b50a 100644 --- a/src/browser/services/CharSizeService.ts +++ b/src/browser/services/CharSizeService.ts @@ -61,10 +61,8 @@ abstract class BaseMeasureStategy extends Disposable implements IMeasureStrategy protected _result: IMeasureResult = { width: 0, height: 0 }; protected _validateAndSet(width: number | undefined, height: number | undefined): void { - /* - * If values are 0 then the element is likely currently display:none, in which case we should - * retain the previous value. - */ + // If values are 0 then the element is likely currently display:none, in which case we should + // retain the previous value. if (width !== undefined && width > 0 && height !== undefined && height > 0) { this._result.width = width; this._result.height = height; diff --git a/src/browser/services/CharacterJoinerService.ts b/src/browser/services/CharacterJoinerService.ts index d0ded2623..ca4f1984e 100644 --- a/src/browser/services/CharacterJoinerService.ts +++ b/src/browser/services/CharacterJoinerService.ts @@ -13,10 +13,8 @@ import { ICharacterJoinerService } from 'browser/services/Services'; export class JoinedCellData extends AttributeData implements ICellData { private _width: number; - /* - * .content carries no meaning for joined CellData, simply nullify it - * thus we have to overload all other .content accessors - */ + // .content carries no meaning for joined CellData, simply nullify it + // thus we have to overload all other .content accessors public content: number = 0; public fg: number; public bg: number; @@ -44,10 +42,8 @@ export class JoinedCellData extends AttributeData implements ICellData { } public getCode(): number { - /* - * code always gets the highest possible fake codepoint (read as -1) - * this is needed as code is used by caches as identifier - */ + // code always gets the highest possible fake codepoint (read as -1) + // this is needed as code is used by caches as identifier return 0x1FFFFF; } @@ -105,12 +101,10 @@ export class CharacterJoinerService implements ICharacterJoinerService { const ranges: [number, number][] = []; const lineStr = line.translateToString(true); - /* - * Because some cells can be represented by multiple javascript characters, - * we track the cell and the string indexes separately. This allows us to - * translate the string ranges we get from the joiners back into cell ranges - * for use when rendering - */ + // Because some cells can be represented by multiple javascript characters, + // we track the cell and the string indexes separately. This allows us to + // translate the string ranges we get from the joiners back into cell ranges + // for use when rendering let rangeStartColumn = 0; let currentStringIndex = 0; let rangeStartStringIndex = 0; @@ -127,10 +121,8 @@ export class CharacterJoinerService implements ICharacterJoinerService { // End of range if (this._workCell.fg !== rangeAttrFG || this._workCell.bg !== rangeAttrBG) { - /* - * If we ended up with a sequence of more than one character, - * look for ranges to join. - */ + // If we ended up with a sequence of more than one character, + // look for ranges to join. if (x - rangeStartColumn > 1) { const joinedRanges = this._getJoinedRanges( lineStr, @@ -181,11 +173,9 @@ export class CharacterJoinerService implements ICharacterJoinerService { */ private _getJoinedRanges(line: string, startIndex: number, endIndex: number, lineData: IBufferLine, startCol: number): [number, number][] { const text = line.substring(startIndex, endIndex); - /* - * At this point we already know that there is at least one joiner so - * we can just pull its value and assign it directly rather than - * merging it into an empty array, which incurs unnecessary writes. - */ + // At this point we already know that there is at least one joiner so + // we can just pull its value and assign it directly rather than + // merging it into an empty array, which incurs unnecessary writes. let allJoinedRanges: [number, number][] = []; try { allJoinedRanges = this._characterJoiners[0].handler(text); @@ -230,10 +220,8 @@ export class CharacterJoinerService implements ICharacterJoinerService { const width = line.getWidth(x); const length = line.getString(x).length || WHITESPACE_CELL_CHAR.length; - /* - * We skip zero-width characters when creating the string to join the text - * so we do the same here - */ + // We skip zero-width characters when creating the string to join the text + // so we do the same here if (width === 0) { continue; } @@ -256,12 +244,10 @@ export class CharacterJoinerService implements ICharacterJoinerService { break; } - /* - * Ranges can be on adjacent characters. Because the end index of the - * ranges are exclusive, this means that the index for the start of a - * range can be the same as the end index of the previous range. To - * account for the start of the next range, we check here just in case. - */ + // Ranges can be on adjacent characters. Because the end index of the + // ranges are exclusive, this means that the index for the start of a + // range can be the same as the end index of the previous range. To + // account for the start of the next range, we check here just in case. if (currentRange[0] <= currentStringIndex) { currentRange[0] = x; currentRangeStarted = true; @@ -270,17 +256,13 @@ export class CharacterJoinerService implements ICharacterJoinerService { } } - /* - * Adjust the string index based on the character length to line up with - * the column adjustment - */ + // Adjust the string index based on the character length to line up with + // the column adjustment currentStringIndex += length; } - /* - * If there is still a range left at the end, it must extend all the way to - * the end of the line. - */ + // If there is still a range left at the end, it must extend all the way to + // the end of the line. if (currentRange) { currentRange[1] = this._bufferService.cols; } @@ -306,19 +288,15 @@ export class CharacterJoinerService implements ICharacterJoinerService { } if (newRange[1] <= range[1]) { - /* - * Case 2: New range is either wholly contained within the - * search range or overlaps with the front of it - */ + // Case 2: New range is either wholly contained within the + // search range or overlaps with the front of it range[0] = Math.min(newRange[0], range[0]); return ranges; } if (newRange[0] < range[1]) { - /* - * Case 3: New range either wholly contains the search range - * or overlaps with the end of it - */ + // Case 3: New range either wholly contains the search range + // or overlaps with the end of it range[0] = Math.min(newRange[0], range[0]); inRange = true; } @@ -327,28 +305,22 @@ export class CharacterJoinerService implements ICharacterJoinerService { continue; } else { if (newRange[1] <= range[0]) { - /* - * Case 5: New range extends from previous range but doesn't - * reach the current one - */ + // Case 5: New range extends from previous range but doesn't + // reach the current one ranges[i - 1][1] = newRange[1]; return ranges; } if (newRange[1] <= range[1]) { - /* - * Case 6: New range extends from prvious range into the - * current range - */ + // Case 6: New range extends from prvious range into the + // current range ranges[i - 1][1] = Math.max(newRange[1], range[1]); ranges.splice(i, 1); return ranges; } - /* - * Case 7: New range extends from previous range past the - * end of the current range - */ + // Case 7: New range extends from previous range past the + // end of the current range ranges.splice(i, 1); i--; } diff --git a/src/browser/services/MouseService.test.ts b/src/browser/services/MouseService.test.ts index 63e14954a..f395083e3 100644 --- a/src/browser/services/MouseService.test.ts +++ b/src/browser/services/MouseService.test.ts @@ -127,10 +127,8 @@ describe('MouseService _triggerMouseEvent', () => { assert.equal(trigger({ col: 0, row: 0, x: 0, y: 0, button: CoreMouseButton.RIGHT, action: CoreMouseAction.DOWN }), true); assert.equal(trigger({ col: 0, row: 0, x: 0, y: 0, button: CoreMouseButton.WHEEL, action: CoreMouseAction.UP }), true); assert.equal(trigger({ col: 0, row: 0, x: 0, y: 0, button: CoreMouseButton.NONE, action: CoreMouseAction.MOVE }), true); - /* - * should not report in any case - * invalid button + action combinations - */ + // should not report in any case + // invalid button + action combinations assert.equal(trigger({ col: 0, row: 0, x: 0, y: 0, button: CoreMouseButton.WHEEL, action: CoreMouseAction.MOVE }), false); assert.equal(trigger({ col: 0, row: 0, x: 0, y: 0, button: CoreMouseButton.NONE, action: CoreMouseAction.DOWN }), false); assert.equal(trigger({ col: 0, row: 0, x: 0, y: 0, button: CoreMouseButton.NONE, action: CoreMouseAction.UP }), false); diff --git a/src/browser/services/MouseService.ts b/src/browser/services/MouseService.ts index 2f35957b7..d701796c2 100644 --- a/src/browser/services/MouseService.ts +++ b/src/browser/services/MouseService.ts @@ -147,10 +147,8 @@ export class MouseService implements IMouseService { return false; } - /* - * exit if we cannot determine valid button/action values - * do nothing for higher buttons than wheel - */ + // exit if we cannot determine valid button/action values + // do nothing for higher buttons than wheel if (action === undefined || but === undefined || but > CoreMouseButton.WHEEL) { return false; } @@ -206,23 +204,19 @@ export class MouseService implements IMouseService { ev.preventDefault(); ctx.focus(); - /* - * Don't send the mouse button to the pty if mouse events are disabled or - * if the selection manager is having selection forced (ie. a modifier is - * held). - */ + // Don't send the mouse button to the pty if mouse events are disabled or + // if the selection manager is having selection forced (ie. a modifier is + // held). if (!this._mouseStateService.areMouseEventsActive || this._selectionService.shouldForceSelection(ev)) { return; } this._sendEvent(ctx, ev); - /* - * Register additional global handlers which should keep reporting outside - * of the terminal element. - * Note: Other emulators also do this for 'mousedown' while a button - * is held, we currently limit 'mousedown' to the terminal only. - */ + // Register additional global handlers which should keep reporting outside + // of the terminal element. + // Note: Other emulators also do this for 'mousedown' while a button + // is held, we currently limit 'mousedown' to the terminal only. if (ctx.requestedEvents.mouseup) { ctx.target.document.addEventListener('mouseup', ctx.requestedEvents.mouseup); } @@ -242,14 +236,12 @@ export class MouseService implements IMouseService { } if (!this._bufferService.buffer.hasScrollback) { - /* - * Convert wheel events into up/down events when the buffer does not have scrollback, this - * enables scrolling in apps hosted in the alt buffer such as vim or tmux even when mouse - * events are not enabled. - * This used implementation used get the actual lines/partial lines scrolled from the - * viewport but since moving to the new viewport implementation has been simplified to - * simply send a single up or down sequence. - */ + // Convert wheel events into up/down events when the buffer does not have scrollback, this + // enables scrolling in apps hosted in the alt buffer such as vim or tmux even when mouse + // events are not enabled. + // This used implementation used get the actual lines/partial lines scrolled from the + // viewport but since moving to the new viewport implementation has been simplified to + // simply send a single up or down sequence. // Do nothing if there's no vertical scroll const deltaY = ev.deltaY; diff --git a/src/browser/services/RenderService.ts b/src/browser/services/RenderService.ts index 433a24eb5..e999b7eac 100644 --- a/src/browser/services/RenderService.ts +++ b/src/browser/services/RenderService.ts @@ -88,11 +88,9 @@ export class RenderService extends Disposable implements IRenderService { this._register(this._optionsService.onOptionChange(() => this._handleOptionsChanged())); this._register(this._charSizeService.onCharSizeChange(() => this.handleCharSizeChanged())); - /* - * Do a full refresh whenever any decoration is added or removed. This may not actually result - * in changes but since decorations should be used sparingly or added/removed all in the same - * frame this should have minimal performance impact. - */ + // Do a full refresh whenever any decoration is added or removed. This may not actually result + // in changes but since decorations should be used sparingly or added/removed all in the same + // frame this should have minimal performance impact. this._register(decorationService.onDecorationRegistered(() => this._fullRefresh())); this._register(decorationService.onDecorationRemoved(() => this._fullRefresh())); @@ -126,10 +124,8 @@ export class RenderService extends Disposable implements IRenderService { } private _registerIntersectionObserver(w: Window & typeof globalThis, screenElement: HTMLElement): void { - /* - * Detect whether IntersectionObserver is detected and enable renderer pause - * and resume based on terminal visibility if so - */ + // Detect whether IntersectionObserver is detected and enable renderer pause + // and resume based on terminal visibility if so if ('IntersectionObserver' in w) { const observer = new w.IntersectionObserver(e => this._handleIntersectionChange(e[e.length - 1]), { threshold: 0 }); this._observerDisposable.value = toDisposable(() => { @@ -190,20 +186,16 @@ export class RenderService extends Disposable implements IRenderService { return; } - /* - * Skip rendering if synchronized output mode is enabled. This check must happen here - * (in addition to refreshRows) to handle renders that were queued before the mode was enabled. - */ + // Skip rendering if synchronized output mode is enabled. This check must happen here + // (in addition to refreshRows) to handle renders that were queued before the mode was enabled. if (this._coreService.decPrivateModes.synchronizedOutput) { this._syncOutputHandler.bufferRows(start, end); return; } - /* - * Since this is debounced, a resize event could have happened between the time a refresh was - * requested and when this triggers. Clamp the values of start and end to ensure they're valid - * given the current viewport state. - */ + // Since this is debounced, a resize event could have happened between the time a refresh was + // requested and when this triggers. Clamp the values of start and end to ensure they're valid + // given the current viewport state. start = Math.min(start, this._rowCount - 1); end = Math.min(end, this._rowCount - 1); @@ -285,10 +277,8 @@ export class RenderService extends Disposable implements IRenderService { } public handleDevicePixelRatioChange(): void { - /* - * Force char size measurement as DomMeasureStrategy(getBoundingClientRect) is not stable - * when devicePixelRatio changes - */ + // Force char size measurement as DomMeasureStrategy(getBoundingClientRect) is not stable + // when devicePixelRatio changes this._charSizeService.measure(); if (!this._renderer.value) { diff --git a/src/browser/services/SelectionService.test.ts b/src/browser/services/SelectionService.test.ts index 822bb6b6d..be62ccc6b 100644 --- a/src/browser/services/SelectionService.test.ts +++ b/src/browser/services/SelectionService.test.ts @@ -257,10 +257,8 @@ describe('SelectionService', () => { ])); // The a is here to prevent the space being trimmed in selectionText selectionService.selectWordAt([0, 0]); assert.equal(selectionService.selectionText, ' '); - /* - * ZWJ emojis do not combine in the terminal so the family emoji used here consumed 4 cells - * The selection text should retain ZWJ chars despite not combining on the terminal - */ + // ZWJ emojis do not combine in the terminal so the family emoji used here consumed 4 cells + // The selection text should retain ZWJ chars despite not combining on the terminal selectionService.selectWordAt([1, 0]); assert.equal(selectionService.selectionText, '👨‍👩‍👧‍👦'); selectionService.selectWordAt([2, 0]); @@ -475,10 +473,8 @@ describe('SelectionService', () => { }); it('should select a column of text with double character emojis', () => { - /* - * TODO the case this is testing works for me in the demo webapp, - * but doing it programmatically fails. - */ + // TODO the case this is testing works for me in the demo webapp, + // but doing it programmatically fails. buffer.lines.length = 3; buffer.lines.set(0, stringToRow('a ')); buffer.lines.set(1, stringArrayToRow(['😁', ' '])); diff --git a/src/browser/services/SelectionService.ts b/src/browser/services/SelectionService.ts index 29ed1b827..39692a871 100644 --- a/src/browser/services/SelectionService.ts +++ b/src/browser/services/SelectionService.ts @@ -152,10 +152,8 @@ export class SelectionService extends Disposable implements ISelectionService { this._removeMouseDownListeners(); })); - /* - * Clear selection when resizing vertically. This experience could be improved, this is the - * simple option to fix the buggy behavior. https://github.com/xtermjs/xterm.js/issues/5300 - */ + // Clear selection when resizing vertically. This experience could be improved, this is the + // simple option to fix the buggy behavior. https://github.com/xtermjs/xterm.js/issues/5300 this._register(this._bufferService.onResize(e => { if (e.rowsChanged) { this.clearSelection(); @@ -217,10 +215,8 @@ export class SelectionService extends Disposable implements ISelectionService { return ''; } - /* - * For column selection it's not enough to rely on final selection's swapping of reversed - * values, it also needs the x coordinates to swap independently of the y coordinate is needed - */ + // For column selection it's not enough to rely on final selection's swapping of reversed + // values, it also needs the x coordinates to swap independently of the y coordinate is needed const startCol = start[0] < end[0] ? start[0] : end[0]; const endCol = start[0] < end[0] ? end[0] : start[0]; for (let i = start[1]; i <= end[1]; i++) { @@ -255,10 +251,8 @@ export class SelectionService extends Disposable implements ISelectionService { } } - /* - * Format string by replacing non-breaking space chars with regular spaces - * and joining the array into a multi-line string. - */ + // Format string by replacing non-breaking space chars with regular spaces + // and joining the array into a multi-line string. const formattedResult = result.map(line => { return line.replace(ALL_NON_BREAKING_SPACE_REGEX, ' '); }).join(Browser.isWindows ? '\r\n' : '\n'); @@ -287,10 +281,8 @@ export class SelectionService extends Disposable implements ISelectionService { this._refreshAnimationFrame = this._coreBrowserService.window.requestAnimationFrame(() => this._refresh()); } - /* - * If the platform is Linux and the refresh call comes from a mouse event, - * we need to update the selection for middle click to paste selection. - */ + // If the platform is Linux and the refresh call comes from a mouse event, + // we need to update the selection for middle click to paste selection. if (Browser.isLinux && isLinuxMouseSelection) { const selectionText = this.selectionText; if (selectionText.length) { @@ -455,10 +447,8 @@ export class SelectionService extends Disposable implements ISelectionService { */ public handleMouseDown(event: MouseEvent): void { this._mouseDownTimeStamp = event.timeStamp; - /* - * If we have selection, we want the context menu on right click even if the - * terminal is in mouse mode. - */ + // If we have selection, we want the context menu on right click even if the + // terminal is in mouse mode. if (event.button === 2 && this.hasSelection) { return; } @@ -571,10 +561,8 @@ export class SelectionService extends Disposable implements ISelectionService { return; } - /* - * If the mouse is over the second half of a wide character, adjust the - * selection to cover the whole character - */ + // If the mouse is over the second half of a wide character, adjust the + // selection to cover the whole character if (line.hasWidth(this._model.selectionStart[0]) === 0) { this._model.selectionStart[0]++; } @@ -617,25 +605,19 @@ export class SelectionService extends Disposable implements ISelectionService { * @param event The mousemove event. */ private _handleMouseMove(event: MouseEvent): void { - /* - * If the mousemove listener is active it means that a selection is - * currently being made, we should stop propagation to prevent mouse events - * to be sent to the pty. - */ + // If the mousemove listener is active it means that a selection is + // currently being made, we should stop propagation to prevent mouse events + // to be sent to the pty. event.stopImmediatePropagation(); - /* - * Do nothing if there is no selection start, this can happen if the first - * click in the terminal is an incremental click - */ + // Do nothing if there is no selection start, this can happen if the first + // click in the terminal is an incremental click if (!this._model.selectionStart) { return; } - /* - * Record the previous position so we know whether to redraw the selection - * at the end. - */ + // Record the previous position so we know whether to redraw the selection + // at the end. const previousSelectionEnd = this._model.selectionEnd ? [this._model.selectionEnd[0], this._model.selectionEnd[1]] : null; // Set the initial selection end based on the mouse coordinates @@ -659,11 +641,9 @@ export class SelectionService extends Disposable implements ISelectionService { // Determine the amount of scrolling that will happen. this._dragScrollAmount = this._getMouseEventScrollAmount(event); - /* - * If the cursor was above or below the viewport, make sure it's at the - * start or end of the viewport respectively. This should only happen when - * NOT in column select mode. - */ + // If the cursor was above or below the viewport, make sure it's at the + // start or end of the viewport respectively. This should only happen when + // NOT in column select mode. if (this._activeSelectionMode !== SelectionMode.COLUMN) { if (this._dragScrollAmount > 0) { this._model.selectionEnd[0] = this._bufferService.cols; @@ -672,11 +652,9 @@ export class SelectionService extends Disposable implements ISelectionService { } } - /* - * If the character is a wide character include the cell to the right in the - * selection. Note that selections at the very end of the line will never - * have a character. - */ + // If the character is a wide character include the cell to the right in the + // selection. Note that selections at the very end of the line will never + // have a character. const buffer = this._bufferService.buffer; if (this._model.selectionEnd[1] < buffer.lines.length) { const line = buffer.lines.get(this._model.selectionEnd[1]); @@ -705,12 +683,10 @@ export class SelectionService extends Disposable implements ISelectionService { } if (this._dragScrollAmount) { this._onRequestScrollLines.fire({ amount: this._dragScrollAmount, suppressScrollEvent: false }); - /* - * Re-evaluate selection - * If the cursor was above or below the viewport, make sure it's at the - * start or end of the viewport respectively. This should only happen when - * NOT in column select mode. - */ + // Re-evaluate selection + // If the cursor was above or below the viewport, make sure it's at the + // start or end of the viewport respectively. This should only happen when + // NOT in column select mode. const buffer = this._bufferService.buffer; if (this._dragScrollAmount > 0) { if (this._activeSelectionMode !== SelectionMode.COLUMN) { @@ -789,12 +765,10 @@ export class SelectionService extends Disposable implements ISelectionService { private _handleBufferActivate(e: {activeBuffer: IBuffer, inactiveBuffer: IBuffer}): void { this.clearSelection(); - /* - * Only adjust the selection on trim, shiftElements is rarely used (only in - * reverseIndex) and delete in a splice is only ever used when the same - * number of elements was just added. Given this is could actually be - * beneficial to leave the selection as is for these cases. - */ + // Only adjust the selection on trim, shiftElements is rarely used (only in + // reverseIndex) and delete in a splice is only ever used when the same + // number of elements was just added. Given this is could actually be + // beneficial to leave the selection as is for these cases. this._trimListener.dispose(); this._trimListener = e.activeBuffer.lines.onTrim(amount => this._handleTrim(amount)); } @@ -810,17 +784,13 @@ export class SelectionService extends Disposable implements ISelectionService { for (let i = 0; x >= i; i++) { const length = bufferLine.loadCell(i, this._workCell).getChars().length; if (this._workCell.getWidth() === 0) { - /* - * Wide characters aren't included in the line string so decrement the - * index so the index is back on the wide character. - */ + // Wide characters aren't included in the line string so decrement the + // index so the index is back on the wide character. charIndex--; } else if (length > 1 && x !== i) { - /* - * Emojis take up multiple characters, so adjust accordingly. For these - * we don't want ot include the character at the column as we're - * returning the start index in the string, not the end index. - */ + // Emojis take up multiple characters, so adjust accordingly. For these + // we don't want ot include the character at the column as we're + // returning the start index in the string, not the end index. charIndex += length - 1; } } @@ -883,19 +853,15 @@ export class SelectionService extends Disposable implements ISelectionService { endIndex++; } } else { - /* - * Expand until whitespace is hit. This algorithm works by scanning left - * and right from the starting position, keeping both the index format - * (line) and the column format (bufferLine) in sync. When a wide - * character is hit, it is recorded and the column index is adjusted. - */ + // Expand until whitespace is hit. This algorithm works by scanning left + // and right from the starting position, keeping both the index format + // (line) and the column format (bufferLine) in sync. When a wide + // character is hit, it is recorded and the column index is adjusted. let startCol = coords[0]; let endCol = coords[0]; - /* - * Consider the initial position, skip it and increment the wide char - * variable - */ + // Consider the initial position, skip it and increment the wide char + // variable if (bufferLine.getWidth(startCol) === 0) { leftWideCharCount++; startCol--; @@ -921,10 +887,8 @@ export class SelectionService extends Disposable implements ISelectionService { leftWideCharCount++; startCol--; } else if (length > 1) { - /* - * If the next character's string is longer than 1 char (eg. emoji), - * adjust the index - */ + // If the next character's string is longer than 1 char (eg. emoji), + // adjust the index leftLongCharOffset += length - 1; startIndex -= length - 1; } @@ -939,10 +903,8 @@ export class SelectionService extends Disposable implements ISelectionService { rightWideCharCount++; endCol++; } else if (length > 1) { - /* - * If the next character's string is longer than 1 char (eg. emoji), - * adjust the index - */ + // If the next character's string is longer than 1 char (eg. emoji), + // adjust the index rightLongCharOffset += length - 1; endIndex += length - 1; } @@ -954,20 +916,16 @@ export class SelectionService extends Disposable implements ISelectionService { // Incremenet the end index so it is at the start of the next character endIndex++; - /* - * Calculate the start _column_, converting the the string indexes back to - * column coordinates. - */ + // Calculate the start _column_, converting the the string indexes back to + // column coordinates. let start = startIndex // The index of the selection's start char in the line string + charOffset // The difference between the initial char's column and index - leftWideCharCount // The number of wide chars left of the initial char + leftLongCharOffset; // The number of additional chars left of the initial char added by columns with strings longer than 1 (emojis) - /* - * Calculate the length in _columns_, converting the the string indexes back - * to column coordinates. - */ + // Calculate the length in _columns_, converting the the string indexes back + // to column coordinates. let length = Math.min(this._bufferService.cols, // Disallow lengths larger than the terminal cols endIndex // The index of the selection's end char in the line string - startIndex // The index of the selection's start char in the line string @@ -1044,10 +1002,8 @@ export class SelectionService extends Disposable implements ISelectionService { endRow--; } - /* - * Adjust wrapped length value, this only needs to happen when values are reversed as in that - * case we're interested in the start of the word, not the end - */ + // Adjust wrapped length value, this only needs to happen when values are reversed as in that + // case we're interested in the start of the word, not the end if (!this._model.areSelectionValuesReversed()) { while (wordPosition.start + wordPosition.length > this._bufferService.cols) { wordPosition.length -= this._bufferService.cols; @@ -1065,10 +1021,8 @@ export class SelectionService extends Disposable implements ISelectionService { * @param cell The cell to check. */ private _isCharWordSeparator(cell: ICellData): boolean { - /* - * Zero width characters are never separators as they are always to the - * right of wide characters - */ + // Zero width characters are never separators as they are always to the + // right of wide characters if (cell.getWidth() === 0) { return false; } diff --git a/src/common/CircularList.ts b/src/common/CircularList.ts index 9843a470c..3663bfc17 100644 --- a/src/common/CircularList.ts +++ b/src/common/CircularList.ts @@ -52,10 +52,8 @@ export class CircularList extends Disposable implements ICircularList { return; } - /* - * Reconstruct array, starting at index 0. Only transfer values from the - * indexes 0 to length. - */ + // Reconstruct array, starting at index 0. Only transfer values from the + // indexes 0 to length. const newArray = new Array(newMaxLength); for (let i = 0; i < Math.min(newMaxLength, this.length); i++) { newArray[i] = this._array[this._getCyclicIndex(i)]; diff --git a/src/common/Color.ts b/src/common/Color.ts index eef573796..b3d13fc99 100644 --- a/src/common/Color.ts +++ b/src/common/Color.ts @@ -27,11 +27,9 @@ export namespace channels { } export function toRgba(r: number, g: number, b: number, a: number = 0xFF): number { - /* - * Note: The aggregated number is RGBA32 (BE), thus needs to be converted to ABGR32 - * on LE systems, before it can be used for direct 32-bit buffer writes. - * >>> 0 forces an unsigned int - */ + // Note: The aggregated number is RGBA32 (BE), thus needs to be converted to ABGR32 + // on LE systems, before it can be used for direct 32-bit buffer writes. + // >>> 0 forces an unsigned int return (r << 24 | g << 16 | b << 8 | a) >>> 0; } @@ -199,10 +197,8 @@ export namespace css { throw new Error('css.toColor: Unsupported css format'); } - /* - * Validate the color using canvas fillStyle - * See https://html.spec.whatwg.org/multipage/canvas.html#fill-and-stroke-styles - */ + // Validate the color using canvas fillStyle + // See https://html.spec.whatwg.org/multipage/canvas.html#fill-and-stroke-styles $ctx.fillStyle = $litmusColor; $ctx.fillStyle = css; if (typeof $ctx.fillStyle !== 'string') { @@ -217,11 +213,9 @@ export namespace css { throw new Error('css.toColor: Unsupported css format'); } - /* - * Extract the color from the canvas' fillStyle property which exposes the color value in rgba() - * format - * See https://html.spec.whatwg.org/multipage/canvas.html#serialisation-of-a-color - */ + // Extract the color from the canvas' fillStyle property which exposes the color value in rgba() + // format + // See https://html.spec.whatwg.org/multipage/canvas.html#serialisation-of-a-color return { rgba: channels.toRgba($r, $g, $b, $a), css @@ -327,10 +321,8 @@ export namespace rgba { } export function reduceLuminance(bgRgba: number, fgRgba: number, ratio: number): number { - /* - * This is a naive but fast approach to reducing luminance as converting to - * HSL and back is expensive - */ + // This is a naive but fast approach to reducing luminance as converting to + // HSL and back is expensive const bgR = (bgRgba >> 24) & 0xFF; const bgG = (bgRgba >> 16) & 0xFF; const bgB = (bgRgba >> 8) & 0xFF; @@ -349,10 +341,8 @@ export namespace rgba { } export function increaseLuminance(bgRgba: number, fgRgba: number, ratio: number): number { - /* - * This is a naive but fast approach to increasing luminance as converting to - * HSL and back is expensive - */ + // This is a naive but fast approach to increasing luminance as converting to + // HSL and back is expensive const bgR = (bgRgba >> 24) & 0xFF; const bgG = (bgRgba >> 16) & 0xFF; const bgB = (bgRgba >> 8) & 0xFF; diff --git a/src/common/CoreTerminal.ts b/src/common/CoreTerminal.ts index 3e16fe0a2..9bd7faaf5 100644 --- a/src/common/CoreTerminal.ts +++ b/src/common/CoreTerminal.ts @@ -176,10 +176,8 @@ export abstract class CoreTerminal extends Disposable implements ICoreTerminal { x = Math.max(x, BufferServiceConstants.MINIMUM_COLS); y = Math.max(y, BufferServiceConstants.MINIMUM_ROWS); - /* - * Flush pending writes before resize to avoid race conditions where async - * writes are processed with incorrect dimensions - */ + // Flush pending writes before resize to avoid race conditions where async + // writes are processed with incorrect dimensions this._writeBuffer.flushSync(); this._bufferService.resize(x, y); diff --git a/src/common/InputHandler.test.ts b/src/common/InputHandler.test.ts index cf41d5746..f9da81a25 100644 --- a/src/common/InputHandler.test.ts +++ b/src/common/InputHandler.test.ts @@ -613,10 +613,8 @@ describe('InputHandler', () => { await inputHandler.parseP('a'.repeat(bufferService.cols + 9)); // line 1 and 2 for (let i = 3; i < bufferService.rows; ++i) await inputHandler.parseP('a'.repeat(bufferService.cols)); - /* - * params[1] left and above with wrap - * confirm precondition that line 2 is wrapped - */ + // params[1] left and above with wrap + // confirm precondition that line 2 is wrapped assert.equal(bufferService.buffer.lines.get(2)!.isWrapped, true); bufferService.buffer.y = 2; bufferService.buffer.x = 40; @@ -630,10 +628,8 @@ describe('InputHandler', () => { await inputHandler.parseP('a'.repeat(bufferService.cols + 9)); // line 1 and 2 for (let i = 3; i < bufferService.rows; ++i) await inputHandler.parseP('a'.repeat(bufferService.cols)); - /* - * params[1] left and above with wrap - * confirm precondition that line 2 is wrapped - */ + // params[1] left and above with wrap + // confirm precondition that line 2 is wrapped assert.equal(bufferService.buffer.lines.get(2)!.isWrapped, true); bufferService.buffer.y = 1; bufferService.buffer.x = 90; // Cursor is beyond last column diff --git a/src/common/InputHandler.ts b/src/common/InputHandler.ts index 1fac29d91..2b7142dd0 100644 --- a/src/common/InputHandler.ts +++ b/src/common/InputHandler.ts @@ -33,12 +33,10 @@ const GLEVEL: { [key: string]: number } = { '(': 0, ')': 1, '*': 2, '+': 3, '-': /** * Document xterm VT features here that are currently unsupported */ -/* - * @vt: #N DCS DECUDK "User Defined Keys" "DCS Ps ; Ps \| Pt ST" "Definitions for user-defined keys." - * @vt: #N DCS XTGETTCAP "Request Terminfo String" "DCS + q Pt ST" "Request Terminfo String." - * @vt: #N DCS XTSETTCAP "Set Terminfo Data" "DCS + p Pt ST" "Set Terminfo Data." - * @vt: #N OSC 1 "Set Icon Name" "OSC 1 ; Pt BEL" "Set icon name." - */ +// @vt: #N DCS DECUDK "User Defined Keys" "DCS Ps ; Ps \| Pt ST" "Definitions for user-defined keys." +// @vt: #N DCS XTGETTCAP "Request Terminfo String" "DCS + q Pt ST" "Request Terminfo String." +// @vt: #N DCS XTSETTCAP "Set Terminfo Data" "DCS + p Pt ST" "Set Terminfo Data." +// @vt: #N OSC 1 "Set Icon Name" "OSC 1 ; Pt BEL" "Set icon name." /** * Max length of the UTF32 input buffer. Real memory consumption is 4 times higher. @@ -290,17 +288,13 @@ export class InputHandler extends Disposable implements IInputHandler { this._parser.registerOscHandler(1, new OscHandler(data => this.setIconName(data))); // 2 - title this._parser.registerOscHandler(2, new OscHandler(data => this.setTitle(data))); - /* - * 3 - set property X in the form "prop=value" - * 4 - Change Color Number - */ + // 3 - set property X in the form "prop=value" + // 4 - Change Color Number this._parser.registerOscHandler(4, new OscHandler(data => this.setOrReportIndexedColor(data))); - /* - * 5 - Change Special Color Number - * 6 - Enable/disable Special Color Number c - * 7 - current directory? (not in xterm spec, see https://gitlab.com/gnachman/iterm2/issues/3939) - * 8 - create hyperlink (not in xterm spec, see https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda) - */ + // 5 - Change Special Color Number + // 6 - Enable/disable Special Color Number c + // 7 - current directory? (not in xterm spec, see https://gitlab.com/gnachman/iterm2/issues/3939) + // 8 - create hyperlink (not in xterm spec, see https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda) this._parser.registerOscHandler(8, new OscHandler(data => this.setHyperlink(data))); // 10 - Change VT100 text foreground color to Pt. this._parser.registerOscHandler(10, new OscHandler(data => this.setOrReportFgColor(data))); @@ -308,40 +302,34 @@ export class InputHandler extends Disposable implements IInputHandler { this._parser.registerOscHandler(11, new OscHandler(data => this.setOrReportBgColor(data))); // 12 - Change text cursor color to Pt. this._parser.registerOscHandler(12, new OscHandler(data => this.setOrReportCursorColor(data))); - /* - * 13 - Change mouse foreground color to Pt. - * 14 - Change mouse background color to Pt. - * 15 - Change Tektronix foreground color to Pt. - * 16 - Change Tektronix background color to Pt. - * 17 - Change highlight background color to Pt. - * 18 - Change Tektronix cursor color to Pt. - * 19 - Change highlight foreground color to Pt. - * 46 - Change Log File to Pt. - * 50 - Set Font to Pt. - * 51 - reserved for Emacs shell. - * 52 - Manipulate Selection Data. - * 104 ; c - Reset Color Number c. - */ + // 13 - Change mouse foreground color to Pt. + // 14 - Change mouse background color to Pt. + // 15 - Change Tektronix foreground color to Pt. + // 16 - Change Tektronix background color to Pt. + // 17 - Change highlight background color to Pt. + // 18 - Change Tektronix cursor color to Pt. + // 19 - Change highlight foreground color to Pt. + // 46 - Change Log File to Pt. + // 50 - Set Font to Pt. + // 51 - reserved for Emacs shell. + // 52 - Manipulate Selection Data. + // 104 ; c - Reset Color Number c. this._parser.registerOscHandler(104, new OscHandler(data => this.restoreIndexedColor(data))); - /* - * 105 ; c - Reset Special Color Number c. - * 106 ; c; f - Enable/disable Special Color Number c. - * 110 - Reset VT100 text foreground color. - */ + // 105 ; c - Reset Special Color Number c. + // 106 ; c; f - Enable/disable Special Color Number c. + // 110 - Reset VT100 text foreground color. this._parser.registerOscHandler(110, new OscHandler(data => this.restoreFgColor(data))); // 111 - Reset VT100 text background color. this._parser.registerOscHandler(111, new OscHandler(data => this.restoreBgColor(data))); // 112 - Reset text cursor color. this._parser.registerOscHandler(112, new OscHandler(data => this.restoreCursorColor(data))); - /* - * 113 - Reset mouse foreground color. - * 114 - Reset mouse background color. - * 115 - Reset Tektronix foreground color. - * 116 - Reset Tektronix background color. - * 117 - Reset highlight color. - * 118 - Reset Tektronix cursor color. - * 119 - Reset highlight foreground color. - */ + // 113 - Reset mouse foreground color. + // 114 - Reset mouse background color. + // 115 - Reset Tektronix foreground color. + // 116 - Reset Tektronix background color. + // 117 - Reset highlight color. + // 118 - Reset Tektronix cursor color. + // 119 - Reset highlight foreground color. /** * ESC handlers @@ -478,10 +466,8 @@ export class InputHandler extends Disposable implements IInputHandler { } } - /* - * Clear the dirty row service so we know which lines changed as a result of parsing - * Important: do not clear between async calls, otherwise we lost pending update information. - */ + // Clear the dirty row service so we know which lines changed as a result of parsing + // Important: do not clear between async calls, otherwise we lost pending update information. if (!wasPaused) { this._dirtyRowTracker.clearRange(); } @@ -516,10 +502,8 @@ export class InputHandler extends Disposable implements IInputHandler { this._onCursorMove.fire(); } - /* - * Refresh any dirty rows accumulated as part of parsing, fire only for rows within the - * _viewport_ which is relative to ydisp, not relative to ybase. - */ + // Refresh any dirty rows accumulated as part of parsing, fire only for rows within the + // _viewport_ which is relative to ydisp, not relative to ybase. const viewportEnd = this._dirtyRowTracker.end + (this._bufferService.buffer.ybase - this._bufferService.buffer.ydisp); const viewportStart = this._dirtyRowTracker.start + (this._bufferService.buffer.ybase - this._bufferService.buffer.ydisp); if (viewportStart < this._bufferService.rows) { @@ -541,10 +525,8 @@ export class InputHandler extends Disposable implements IInputHandler { const curAttr = this._curAttrData; let bufferRow = this._activeBuffer.lines.get(this._activeBuffer.ybase + this._activeBuffer.y); - /* - * Defensive check: bufferRow can be undefined if a resize occurred mid-write due to async - * scheduling gaps in WriteBuffer. See https://github.com/xtermjs/xterm.js/issues/5597 - */ + // Defensive check: bufferRow can be undefined if a resize occurred mid-write due to async + // scheduling gaps in WriteBuffer. See https://github.com/xtermjs/xterm.js/issues/5597 if (!bufferRow) { return; } @@ -560,19 +542,15 @@ export class InputHandler extends Disposable implements IInputHandler { for (let pos = start; pos < end; ++pos) { code = data[pos]; - /* - * Soft hyphen's (U+00AD) behavior is ambiguous and differs across terminals. We opt to treat - * it as a zero-width hint to text layout engines and simply ignore it. - */ + // Soft hyphen's (U+00AD) behavior is ambiguous and differs across terminals. We opt to treat + // it as a zero-width hint to text layout engines and simply ignore it. if (code === 0xAD) { continue; } - /* - * get charset replacement character - * charset is only defined for ASCII, therefore we only - * search for an replacement char if code < 127 - */ + // get charset replacement character + // charset is only defined for ASCII, therefore we only + // search for an replacement char if code < 127 if (code < 127 && charset) { const ch = charset[String.fromCharCode(code)]; if (ch) { @@ -593,16 +571,12 @@ export class InputHandler extends Disposable implements IInputHandler { this._oscLinkService.addLineToLink(this._getCurrentLinkId(), this._activeBuffer.ybase + this._activeBuffer.y); } - /* - * goto next line if ch would overflow - * NOTE: To avoid costly width checks here, - * the terminal does not allow a cols < 2. - */ + // goto next line if ch would overflow + // NOTE: To avoid costly width checks here, + // the terminal does not allow a cols < 2. if (this._activeBuffer.x + chWidth - oldWidth > cols) { - /* - * autowrap - DECAWM - * automatically wraps to the beginning of the next line - */ + // autowrap - DECAWM + // automatically wraps to the beginning of the next line if (wraparoundMode) { const oldRow = bufferRow; let oldCol = this._activeBuffer.x - oldWidth; @@ -615,10 +589,8 @@ export class InputHandler extends Disposable implements IInputHandler { if (this._activeBuffer.y >= this._bufferService.rows) { this._activeBuffer.y = this._bufferService.rows - 1; } - /* - * The line already exists (eg. the initial viewport), mark it as a - * wrapped line - */ + // The line already exists (eg. the initial viewport), mark it as a + // wrapped line this._activeBuffer.lines.get(this._activeBuffer.ybase + this._activeBuffer.y)!.isWrapped = true; } // row changed, get it again @@ -627,10 +599,8 @@ export class InputHandler extends Disposable implements IInputHandler { return; } if (oldWidth > 0 && bufferRow instanceof BufferLine) { - /* - * Combining character widens 1 column to 2. - * Move old character to next line. - */ + // Combining character widens 1 column to 2. + // Move old character to next line. bufferRow.copyCellsFrom(oldRow as BufferLine, oldCol, 0, oldWidth, false); } @@ -641,28 +611,22 @@ export class InputHandler extends Disposable implements IInputHandler { } else { this._activeBuffer.x = cols - 1; if (chWidth === 2) { - /* - * FIXME: check for xterm behavior - * What to do here? We got a wide char that does not fit into last cell - */ + // FIXME: check for xterm behavior + // What to do here? We got a wide char that does not fit into last cell continue; } } } - /* - * insert combining char at last cursor position - * this._activeBuffer.x should never be 0 for a combining char - * since they always follow a cell consuming char - * therefore we can test for this._activeBuffer.x to avoid overflow left - */ + // insert combining char at last cursor position + // this._activeBuffer.x should never be 0 for a combining char + // since they always follow a cell consuming char + // therefore we can test for this._activeBuffer.x to avoid overflow left if (shouldJoin && this._activeBuffer.x) { const offset = bufferRow.getWidth(this._activeBuffer.x - 1) ? 1 : 2; - /* - * if empty cell after fullwidth, need to go 2 cells back - * it is save to step 2 cells back here - * since an empty cell is only set by fullwidth chars - */ + // if empty cell after fullwidth, need to go 2 cells back + // it is save to step 2 cells back here + // since an empty cell is only set by fullwidth chars bufferRow.addCodepointToCell(this._activeBuffer.x - offset, code, chWidth); for (let delta = chWidth - oldWidth; --delta >= 0;) { @@ -675,11 +639,9 @@ export class InputHandler extends Disposable implements IInputHandler { if (insertMode) { // right shift cells according to the width bufferRow.insertCells(this._activeBuffer.x, chWidth - oldWidth, this._activeBuffer.getNullCell(curAttr)); - /* - * test last cell - since the last cell has only room for - * a halfwidth char any fullwidth shifted there is lost - * and will be set to empty cell - */ + // test last cell - since the last cell has only room for + // a halfwidth char any fullwidth shifted there is lost + // and will be set to empty cell if (bufferRow.getWidth(cols - 1) === 2) { bufferRow.setCellFromCodepoint(cols - 1, NULL_CELL_CODE, NULL_CELL_WIDTH, curAttr); } @@ -688,11 +650,9 @@ export class InputHandler extends Disposable implements IInputHandler { // write current char to buffer and advance cursor bufferRow.setCellFromCodepoint(this._activeBuffer.x++, code, chWidth, curAttr); - /* - * fullwidth char - also set next cell to placeholder stub and advance cursor - * for graphemes bigger than fullwidth we can simply loop to zero - * we already made sure above, that this._activeBuffer.x + chWidth will not overflow right - */ + // fullwidth char - also set next cell to placeholder stub and advance cursor + // for graphemes bigger than fullwidth we can simply loop to zero + // we already made sure above, that this._activeBuffer.x + chWidth will not overflow right if (chWidth > 0) { while (--chWidth) { // other than a regular empty cell a cell following a wide char has no width @@ -790,13 +750,11 @@ export class InputHandler extends Disposable implements IInputHandler { } else if (this._activeBuffer.y >= this._bufferService.rows) { this._activeBuffer.y = this._bufferService.rows - 1; } else { - /* - * There was an explicit line feed (not just a carriage return), so clear the wrapped state of - * the line. This is particularly important on conpty/Windows where revisiting lines to - * reprint is common, especially on resize. Note that the windowsMode wrapped line heuristics - * can mess with this so windowsMode should be disabled, which is recommended on Windows build - * 21376 and above. - */ + // There was an explicit line feed (not just a carriage return), so clear the wrapped state of + // the line. This is particularly important on conpty/Windows where revisiting lines to + // reprint is common, especially on resize. Note that the windowsMode wrapped line heuristics + // can mess with this so windowsMode should be disabled, which is recommended on Windows build + // 21376 and above. this._activeBuffer.lines.get(this._activeBuffer.ybase + this._activeBuffer.y)!.isWrapped = false; } // If the end of the line is hit, prevent this action from wrapping around to the next line. @@ -841,11 +799,9 @@ export class InputHandler extends Disposable implements IInputHandler { return true; } - /* - * reverse wrap-around is enabled - * other than for normal operation mode, reverse wrap-around allows the cursor - * to be at x=cols to be able to address the last cell of a row by BS - */ + // reverse wrap-around is enabled + // other than for normal operation mode, reverse wrap-around allows the cursor + // to be at x=cols to be able to address the last cell of a row by BS this._restrictCursor(this._bufferService.cols); if (this._activeBuffer.x > 0) { @@ -866,21 +822,17 @@ export class InputHandler extends Disposable implements IInputHandler { this._activeBuffer.lines.get(this._activeBuffer.ybase + this._activeBuffer.y)!.isWrapped = false; this._activeBuffer.y--; this._activeBuffer.x = this._bufferService.cols - 1; - /* - * find last taken cell - last cell can have 3 different states: - * - hasContent(true) + hasWidth(1): narrow char - we are done - * - hasWidth(0): second part of wide char - we are done - * - hasContent(false) + hasWidth(1): empty cell due to early wrapping wide char, go one - * cell further back - */ + // find last taken cell - last cell can have 3 different states: + // - hasContent(true) + hasWidth(1): narrow char - we are done + // - hasWidth(0): second part of wide char - we are done + // - hasContent(false) + hasWidth(1): empty cell due to early wrapping wide char, go one + // cell further back const line = this._activeBuffer.lines.get(this._activeBuffer.ybase + this._activeBuffer.y)!; if (line.hasWidth(this._activeBuffer.x) && !line.hasContent(this._activeBuffer.x)) { this._activeBuffer.x--; - /* - * We do this only once, since width=1 + hasContent=false currently happens only once - * before early wrapping of a wide char. - * This needs to be fixed once we support graphemes taking more than 2 cells. - */ + // We do this only once, since width=1 + hasContent=false currently happens only once + // before early wrapping of a wide char. + // This needs to be fixed once we support graphemes taking more than 2 cells. } } } @@ -961,10 +913,8 @@ export class InputHandler extends Disposable implements IInputHandler { * Set relative cursor position. */ private _moveCursor(x: number, y: number): void { - /* - * for relative changes we have to make sure we are within 0 .. cols/rows - 1 - * before calculating the new position - */ + // for relative changes we have to make sure we are within 0 .. cols/rows - 1 + // before calculating the new position this._restrictCursor(); this._setCursor(this._activeBuffer.x + x, this._activeBuffer.y + y); } @@ -1404,10 +1354,8 @@ export class InputHandler extends Disposable implements IInputHandler { const scrollBottomRowsOffset = this._bufferService.rows - 1 - this._activeBuffer.scrollBottom; const scrollBottomAbsolute = this._bufferService.rows - 1 + this._activeBuffer.ybase - scrollBottomRowsOffset + 1; while (param--) { - /* - * test: echo -e '\e[44m\e[1L\e[0m' - * blankLine(true) - xterm/linux behavior - */ + // test: echo -e '\e[44m\e[1L\e[0m' + // blankLine(true) - xterm/linux behavior this._activeBuffer.lines.splice(scrollBottomAbsolute - 1, 1); this._activeBuffer.lines.splice(row, 0, this._activeBuffer.getBlankLine(this._eraseAttrData())); } @@ -1440,10 +1388,8 @@ export class InputHandler extends Disposable implements IInputHandler { j = this._bufferService.rows - 1 - this._activeBuffer.scrollBottom; j = this._bufferService.rows - 1 + this._activeBuffer.ybase - j; while (param--) { - /* - * test: echo -e '\e[44m\e[1M\e[0m' - * blankLine(true) - xterm/linux behavior - */ + // test: echo -e '\e[44m\e[1M\e[0m' + // blankLine(true) - xterm/linux behavior this._activeBuffer.lines.splice(row, 1); this._activeBuffer.lines.splice(j, 0, this._activeBuffer.getBlankLine(this._eraseAttrData())); } @@ -1791,20 +1737,16 @@ export class InputHandler extends Disposable implements IInputHandler { if (params.params[0] > 0) { return true; } - /* - * xterm and urxvt - * seem to spit this - * out around ~370 times (?). - */ + // xterm and urxvt + // seem to spit this + // out around ~370 times (?). if (this._is('xterm')) { this._coreService.triggerDataEvent(C0.ESC + '[>0;276;0c'); } else if (this._is('rxvt-unicode')) { this._coreService.triggerDataEvent(C0.ESC + '[>85;95;0c'); } else if (this._is('linux')) { - /* - * not supported by linux console. - * linux console echoes parameters. - */ + // not supported by linux console. + // linux console echoes parameters. this._coreService.triggerDataEvent(params.params[0] + 'c'); } else if (this._is('screen')) { this._coreService.triggerDataEvent(C0.ESC + '[>83;40003;0c'); @@ -2037,17 +1979,13 @@ export class InputHandler extends Disposable implements IInputHandler { this._mouseStateService.activeProtocol = 'DRAG'; break; case 1003: // any event mouse - /* - * any event - sends motion events, - * even if there is no button held down. - */ + // any event - sends motion events, + // even if there is no button held down. this._mouseStateService.activeProtocol = 'ANY'; break; case 1004: // send focusin/focusout events - /* - * focusin: ^[[I - * focusout: ^[[O - */ + // focusin: ^[[I + // focusout: ^[[O this._coreService.decPrivateModes.sendFocus = true; this._onRequestSendFocus.fire(); break; @@ -2470,12 +2408,10 @@ export class InputHandler extends Disposable implements IInputHandler { * Returns advance for params index. */ private _extractColor(params: IParams, pos: number, attr: IAttributeData): number { - /* - * normalize params - * meaning: [target, CM, ign, val, val, val] - * RGB : [ 38/48, 2, ign, r, g, b] - * P256 : [ 38/48, 5, ign, v, ign, ign] - */ + // normalize params + // meaning: [target, CM, ign, val, val, val] + // RGB : [ 38/48, 2, ign, r, g, b] + // P256 : [ 38/48, 5, ign, v, ign, ign] const accu = [0, 0, -1, 0, 0, 0]; // alignment placeholder for non color space sequences @@ -2541,10 +2477,8 @@ export class InputHandler extends Disposable implements IInputHandler { * 4:5 - dashed underline */ private _processUnderline(style: number, attr: IAttributeData): void { - /* - * treat extended attrs as immutable, thus always clone from old one - * this is needed since the buffer only holds references to it - */ + // treat extended attrs as immutable, thus always clone from old one + // this is needed since the buffer only holds references to it attr.extended = attr.extended.clone(); // default to 1 == single underline @@ -2567,10 +2501,8 @@ export class InputHandler extends Disposable implements IInputHandler { attr.fg = DEFAULT_ATTR_DATA.fg; attr.bg = DEFAULT_ATTR_DATA.bg; attr.extended = attr.extended.clone(); - /* - * Reset underline style and color. Note that we don't want to reset other - * fields such as the url id. - */ + // Reset underline style and color. Note that we don't want to reset other + // fields such as the url id. attr.extended.underlineStyle = UnderlineStyle.NONE; attr.extended.underlineColor &= ~(Attributes.CM_MASK | Attributes.RGB_MASK); attr.updateExtended(); @@ -2709,10 +2641,8 @@ export class InputHandler extends Disposable implements IInputHandler { // blink attr.fg |= FgFlags.BLINK; } else if (p === 7) { - /* - * inverse and positive - * test with: echo -e '\e[31m\e[42mhello\e[7mworld\e[27mhi\e[m' - */ + // inverse and positive + // test with: echo -e '\e[31m\e[42mhello\e[7mworld\e[27mhi\e[m' attr.fg |= FgFlags.INVERSE; } else if (p === 8) { // invisible @@ -2826,10 +2756,8 @@ export class InputHandler extends Disposable implements IInputHandler { // @vt: #P[Only CPR is supported.] CSI DECDSR "DEC Device Status Report" "CSI ? Ps n" "Only CPR is supported (same as DSR)." public deviceStatusPrivate(params: IParams): boolean { - /* - * modern xterm doesnt seem to - * respond to any of these except ?6, 6, and 5 - */ + // modern xterm doesnt seem to + // respond to any of these except ?6, 6, and 5 switch (params.params[0]) { case 6: // cursor position @@ -2838,28 +2766,20 @@ export class InputHandler extends Disposable implements IInputHandler { this._coreService.triggerDataEvent(`${C0.ESC}[?${y};${x}R`); break; case 15: - /* - * no printer - * this.handler(C0.ESC + '[?11n'); - */ + // no printer + // this.handler(C0.ESC + '[?11n'); break; case 25: - /* - * dont support user defined keys - * this.handler(C0.ESC + '[?21n'); - */ + // dont support user defined keys + // this.handler(C0.ESC + '[?21n'); break; case 26: - /* - * north american keyboard - * this.handler(C0.ESC + '[?27;1;0;0n'); - */ + // north american keyboard + // this.handler(C0.ESC + '[?27;1;0;0n'); break; case 53: - /* - * no dec locator/mouse - * this.handler(C0.ESC + '[?50n'); - */ + // no dec locator/mouse + // this.handler(C0.ESC + '[?50n'); break; case 996: // color scheme query (https://contour-terminal.org/vt-extensions/color-palette-update-notifications/) @@ -3481,11 +3401,9 @@ export class InputHandler extends Disposable implements IInputHandler { public reverseIndex(): boolean { this._restrictCursor(); if (this._activeBuffer.y === this._activeBuffer.scrollTop) { - /* - * possibly move the code below to term.reverseScroll(); - * test: echo -ne '\e[1;1H\e[44m\eM\e[0m' - * blankLine(true) is xterm/linux behavior - */ + // possibly move the code below to term.reverseScroll(); + // test: echo -ne '\e[1;1H\e[44m\eM\e[0m' + // blankLine(true) is xterm/linux behavior const scrollRegionHeight = this._activeBuffer.scrollBottom - this._activeBuffer.scrollTop; this._activeBuffer.lines.shiftElements(this._activeBuffer.ybase + this._activeBuffer.y, scrollRegionHeight, 1); this._activeBuffer.lines.set(this._activeBuffer.ybase + this._activeBuffer.y, this._activeBuffer.getBlankLine(this._eraseAttrData())); diff --git a/src/common/Platform.ts b/src/common/Platform.ts index ad2d1424c..09ba097e6 100644 --- a/src/common/Platform.ts +++ b/src/common/Platform.ts @@ -9,18 +9,14 @@ interface INavigator { platform: string; } -/* - * We're declaring a navigator global here as we expect it in all runtimes (node and browser), but - * we want this module to live in common. - */ +// We're declaring a navigator global here as we expect it in all runtimes (node and browser), but +// we want this module to live in common. declare const navigator: INavigator; declare const process: unknown; -/* - * navigator.userAgent is also checked here because bundling with the process module can cause - * issues otherwise. Note that navigator exists in Node.js 21+ but the userAgent is - * "Node.js/". - */ +// navigator.userAgent is also checked here because bundling with the process module can cause +// issues otherwise. Note that navigator exists in Node.js 21+ but the userAgent is +// "Node.js/". export const isNode = (typeof process !== 'undefined' && 'title' in (process as any) && (typeof navigator === 'undefined' || navigator.userAgent.startsWith('Node.js/'))) ? true : false; const userAgent = (isNode) ? 'node' : navigator.userAgent; const platform = (isNode) ? 'node' : navigator.platform; @@ -48,11 +44,9 @@ export function getSafariVersion(): number { return parseInt(majorVersion[1]); } -/* - * Find the users platform. We use this to interpret the meta key - * and ISO third level shifts. - * http://stackoverflow.com/q/19877924/577598 - */ +// Find the users platform. We use this to interpret the meta key +// and ISO third level shifts. +// http://stackoverflow.com/q/19877924/577598 export const isMac = ['Macintosh', 'MacIntel', 'MacPPC', 'Mac68K'].includes(platform); export const isWindows = ['Windows', 'Win16', 'Win32', 'WinCE'].includes(platform); export const isLinux = platform.indexOf('Linux') >= 0; diff --git a/src/common/SortedList.ts b/src/common/SortedList.ts index 8ae39d3d8..c6dc62085 100644 --- a/src/common/SortedList.ts +++ b/src/common/SortedList.ts @@ -191,10 +191,8 @@ export class SortedList { return mid; } } - /* - * key not in list - * still return closest min (also used as insert position) - */ + // key not in list + // still return closest min (also used as insert position) return min; } } diff --git a/src/common/TaskQueue.ts b/src/common/TaskQueue.ts index ae9041d3a..3cad0a140 100644 --- a/src/common/TaskQueue.ts +++ b/src/common/TaskQueue.ts @@ -83,23 +83,17 @@ abstract class TaskQueue implements ITaskQueue { if (!this._tasks[this._i]()) { this._i++; } - /* - * other than performance.now, performance.now might not be stable (changes on wall clock - * changes), this is not an issue here as a clock change during a short running task is very - * unlikely in case it still happened and leads to negative duration, simply assume 1 msec - */ + // other than performance.now, performance.now might not be stable (changes on wall clock + // changes), this is not an issue here as a clock change during a short running task is very + // unlikely in case it still happened and leads to negative duration, simply assume 1 msec taskDuration = Math.max(1, performance.now() - taskDuration); longestTask = Math.max(taskDuration, longestTask); - /* - * Guess the following task will take a similar time to the longest task in this batch, allow - * additional room to try avoid exceeding the deadline - */ + // Guess the following task will take a similar time to the longest task in this batch, allow + // additional room to try avoid exceeding the deadline deadlineRemaining = deadline.timeRemaining(); if (longestTask * 1.5 > deadlineRemaining) { - /* - * Warn when the time exceeding the deadline is over 20ms, if this happens in practice the - * task should be split into sub-tasks to ensure the UI remains responsive. - */ + // Warn when the time exceeding the deadline is over 20ms, if this happens in practice the + // task should be split into sub-tasks to ensure the UI remains responsive. if (lastDeadlineRemaining - taskDuration < -20) { this._logService.warn(`task queue exceeded allotted deadline by ${Math.abs(Math.round(lastDeadlineRemaining - taskDuration))}ms`); } diff --git a/src/common/Types.ts b/src/common/Types.ts index fb95f5fe7..1981a910f 100644 --- a/src/common/Types.ts +++ b/src/common/Types.ts @@ -310,10 +310,8 @@ export const enum CoreMouseButton { RIGHT = 2, NONE = 3, WHEEL = 4, - /* - * additional buttons 1..8 - * untested! - */ + // additional buttons 1..8 + // untested! AUX1 = 8, AUX2 = 9, AUX3 = 10, @@ -532,7 +530,7 @@ export interface IInputHandler { /** * OSC 0 - * OSC 2 + * OSC 2 */ setTitle(data: string): boolean; /** OSC 4 */ setOrReportIndexedColor(data: string): boolean; /** OSC 10 */ setOrReportFgColor(data: string): boolean; @@ -548,16 +546,16 @@ export interface IInputHandler { /** ESC > */ keypadNumericMode(): boolean; /** * ESC % G - * ESC % @ + * ESC % @ */ selectDefaultCharset(): boolean; /** * ESC ( C - * ESC ) C - * ESC * C - * ESC + C - * ESC - C - * ESC . C - * ESC / C + * ESC ) C + * ESC * C + * ESC + C + * ESC - C + * ESC . C + * ESC / C */ selectCharset(collectAndFlag: string): boolean; /** ESC D */ index(): boolean; /** ESC H */ tabSet(): boolean; @@ -565,10 +563,10 @@ export interface IInputHandler { /** ESC c */ fullReset(): boolean; /** * ESC n - * ESC o - * ESC | - * ESC } - * ESC ~ + * ESC o + * ESC | + * ESC } + * ESC ~ */ setgLevel(level: number): boolean; /** ESC # 8 */ screenAlignmentPattern(): boolean; } diff --git a/src/common/WindowsMode.ts b/src/common/WindowsMode.ts index f59343286..7cff094b2 100644 --- a/src/common/WindowsMode.ts +++ b/src/common/WindowsMode.ts @@ -7,18 +7,16 @@ import { CHAR_DATA_CODE_INDEX, NULL_CELL_CODE, WHITESPACE_CELL_CODE } from 'comm import { IBufferService } from 'common/services/Services'; export function updateWindowsModeWrappedState(bufferService: IBufferService): void { - /* - * Winpty does not support wraparound mode which means that lines will never - * be marked as wrapped. This causes issues for things like copying a line - * retaining the wrapped new line characters or if consumers are listening - * in on the data stream. - * - * The workaround for this is to listen to every incoming line feed and mark - * the line as wrapped if the last character in the previous line is not a - * space. This is certainly not without its problems, but generally on - * Windows when text reaches the end of the terminal it's likely going to be - * wrapped. - */ + // Winpty does not support wraparound mode which means that lines will never + // be marked as wrapped. This causes issues for things like copying a line + // retaining the wrapped new line characters or if consumers are listening + // in on the data stream. + // + // The workaround for this is to listen to every incoming line feed and mark + // the line as wrapped if the last character in the previous line is not a + // space. This is certainly not without its problems, but generally on + // Windows when text reaches the end of the terminal it's likely going to be + // wrapped. const line = bufferService.buffer.lines.get(bufferService.buffer.ybase + bufferService.buffer.y - 1); const lastChar = line?.get(bufferService.cols - 1); diff --git a/src/common/buffer/Buffer.test.ts b/src/common/buffer/Buffer.test.ts index f26807a94..abfea7b06 100644 --- a/src/common/buffer/Buffer.test.ts +++ b/src/common/buffer/Buffer.test.ts @@ -348,10 +348,8 @@ describe('Buffer', () => { assert.equal(buffer.lines.get(4)!.translateToString(), ' '); }); it('should remove the correct amount of rows when reflowing larger', () => { - /* - * This is a regression test to ensure that successive wrapped lines that are getting - * 3+ lines removed on a reflow actually remove the right lines - */ + // This is a regression test to ensure that successive wrapped lines that are getting + // 3+ lines removed on a reflow actually remove the right lines buffer.fillViewportRows(); buffer.resize(10, 10); buffer.y = 2; @@ -431,12 +429,10 @@ describe('Buffer', () => { buffer.lines.get(2)!.set(i, [0, char, 1, code]); } buffer.y = 3; - /* - * Buffer: - * abcdefghij - * 0123456789 - * abcdefghij - */ + // Buffer: + // abcdefghij + // 0123456789 + // abcdefghij const firstMarker = buffer.addMarker(0); const secondMarker = buffer.addMarker(1); const thirdMarker = buffer.addMarker(2); @@ -496,12 +492,10 @@ describe('Buffer', () => { buffer.lines.get(2)!.set(i, [0, char, 1, code]); } buffer.y = 10; - /* - * Buffer: - * abcdefghij - * 0123456789 - * abcdefghij - */ + // Buffer: + // abcdefghij + // 0123456789 + // abcdefghij const firstMarker = buffer.addMarker(0); const secondMarker = buffer.addMarker(1); const thirdMarker = buffer.addMarker(2); @@ -545,11 +539,9 @@ describe('Buffer', () => { buffer.lines.get(1)!.set(0, [0, 'c', 1, 'c'.charCodeAt(0)]); buffer.lines.get(1)!.set(1, [0, 'd', 1, 'd'.charCodeAt(0)]); buffer.lines.get(1)!.isWrapped = true; - /* - * Buffer: - * "ab " (wrapped) - * "cd" - */ + // Buffer: + // "ab " (wrapped) + // "cd" buffer.resize(5, 10); assert.equal(buffer.ybase, 0); assert.equal(buffer.lines.length, 10); @@ -578,11 +570,9 @@ describe('Buffer', () => { buffer.lines.get(1)!.set(i, [0, '', 0, 0]); } buffer.lines.get(1)!.isWrapped = true; - /* - * Buffer: - * 汉语汉语汉语 (wrapped) - * 汉语汉语汉语 - */ + // Buffer: + // 汉语汉语汉语 (wrapped) + // 汉语汉语汉语 assert.equal(buffer.lines.get(0)!.translateToString(true), '汉语汉语汉语'); assert.equal(buffer.lines.get(1)!.translateToString(true), '汉语汉语汉语'); buffer.resize(13, 10); @@ -607,11 +597,9 @@ describe('Buffer', () => { buffer.lines.get(1)!.set(0, [0, 'c', 1, 'c'.charCodeAt(0)]); buffer.lines.get(1)!.set(1, [0, 'd', 1, 'd'.charCodeAt(0)]); buffer.lines.get(1)!.isWrapped = true; - /* - * Buffer: - * "ab " (wrapped) - * "cd" - */ + // Buffer: + // "ab " (wrapped) + // "cd" buffer.resize(3, 10); assert.equal(buffer.y, 2); assert.equal(buffer.ybase, 0); @@ -643,11 +631,9 @@ describe('Buffer', () => { buffer.lines.get(1)!.set(i, [0, '', 0, 0]); } buffer.lines.get(1)!.isWrapped = true; - /* - * Buffer: - * 汉语汉语汉语 (wrapped) - * 汉语汉语汉语 - */ + // Buffer: + // 汉语汉语汉语 (wrapped) + // 汉语汉语汉语 assert.equal(buffer.lines.get(0)!.translateToString(true), '汉语汉语汉语'); assert.equal(buffer.lines.get(1)!.translateToString(true), '汉语汉语汉语'); buffer.resize(11, 10); @@ -682,19 +668,17 @@ describe('Buffer', () => { describe('reflowLarger cases', () => { beforeEach(() => { - /* - * Setup buffer state: - * 'ab' - * 'cd' (wrapped) - * 'ef' - * 'gh' (wrapped) - * 'ij' - * 'kl' (wrapped) - * ' ' - * ' ' - * ' ' - * ' ' - */ + // Setup buffer state: + // 'ab' + // 'cd' (wrapped) + // 'ef' + // 'gh' (wrapped) + // 'ij' + // 'kl' (wrapped) + // ' ' + // ' ' + // ' ' + // ' ' buffer.fillViewportRows(); buffer.resize(2, 10); buffer.lines.get(0)!.set(0, [0, 'a', 1, 'a'.charCodeAt(0)]); @@ -875,19 +859,17 @@ describe('Buffer', () => { }); describe('reflowSmaller cases', () => { beforeEach(() => { - /* - * Setup buffer state: - * 'abcd' - * 'efgh' (wrapped) - * 'ijkl' - * ' ' - * ' ' - * ' ' - * ' ' - * ' ' - * ' ' - * ' ' - */ + // Setup buffer state: + // 'abcd' + // 'efgh' (wrapped) + // 'ijkl' + // ' ' + // ' ' + // ' ' + // ' ' + // ' ' + // ' ' + // ' ' buffer.fillViewportRows(); buffer.resize(4, 10); buffer.lines.get(0)!.set(0, [0, 'a', 1, 'a'.charCodeAt(0)]); @@ -1319,10 +1301,8 @@ describe('Buffer', () => { assert.equal(line.length, INIT_COLS / 2 - 1); } - /* - * wait for a bit to give IdleTaskQueue a chance to kick in - * and finish memory cleaning - */ + // wait for a bit to give IdleTaskQueue a chance to kick in + // and finish memory cleaning await new Promise(r => setTimeout(r, 30)); // cleanup should have realigned memory with exact bytelength diff --git a/src/common/buffer/Buffer.ts b/src/common/buffer/Buffer.ts index a572418c4..34ef02f6a 100644 --- a/src/common/buffer/Buffer.ts +++ b/src/common/buffer/Buffer.ts @@ -169,25 +169,19 @@ export class Buffer extends Disposable implements IBuffer { // count bufferlines with overly big memory to be cleaned afterwards let dirtyMemoryLines = 0; - /* - * Increase max length if needed before adjustments to allow space to fill - * as required. - */ + // Increase max length if needed before adjustments to allow space to fill + // as required. const newMaxLength = this._getCorrectBufferLength(newRows); if (newMaxLength > this.lines.maxLength) { this.lines.maxLength = newMaxLength; } - /* - * if (this._cols > newCols) { - * console.log('increase!'); - * } - */ + // if (this._cols > newCols) { + // console.log('increase!'); + // } - /* - * The following adjustments should only happen if the buffer has been - * initialized/filled. - */ + // The following adjustments should only happen if the buffer has been + // initialized/filled. if (this.lines.length > 0) { // Deal with columns increasing (reducing needs to happen after reflow) if (this._cols < newCols) { @@ -203,17 +197,13 @@ export class Buffer extends Disposable implements IBuffer { for (let y = this._rows; y < newRows; y++) { if (this.lines.length < newRows + this.ybase) { if (this._optionsService.rawOptions.windowsPty.backend !== undefined || this._optionsService.rawOptions.windowsPty.buildNumber !== undefined) { - /* - * Just add the new missing rows on Windows as conpty reprints the screen with it's - * view of the world. Once a line enters scrollback for conpty it remains there - */ + // Just add the new missing rows on Windows as conpty reprints the screen with it's + // view of the world. Once a line enters scrollback for conpty it remains there this.lines.push(new BufferLine(this._stringCache, newCols, nullCell, false)); } else { if (this.ybase > 0 && this.lines.length <= this.ybase + this.y + addToY + 1) { - /* - * There is room above the buffer and there are no empty elements below the line, - * scroll up - */ + // There is room above the buffer and there are no empty elements below the line, + // scroll up this.ybase--; addToY++; if (this.ydisp > 0) { @@ -221,10 +211,8 @@ export class Buffer extends Disposable implements IBuffer { this.ydisp--; } } else { - /* - * Add a blank line if there is no buffer left at the top to scroll to, or if there - * are blank lines after the cursor - */ + // Add a blank line if there is no buffer left at the top to scroll to, or if there + // are blank lines after the cursor this.lines.push(new BufferLine(this._stringCache, newCols, nullCell, false)); } } @@ -245,10 +233,8 @@ export class Buffer extends Disposable implements IBuffer { } } - /* - * Reduce max length if needed after adjustments, this is done after as it - * would otherwise cut data from the bottom of the buffer. - */ + // Reduce max length if needed after adjustments, this is done after as it + // would otherwise cut data from the bottom of the buffer. if (newMaxLength < this.lines.maxLength) { // Trim from the top of the buffer and adjust ybase and ydisp. const amountToTrim = this.lines.length - newMaxLength; @@ -289,10 +275,8 @@ export class Buffer extends Disposable implements IBuffer { this._cols = newCols; this._rows = newRows; - /* - * Ensure the cursor position invariant: ybase + y must be within buffer bounds - * This can be violated during reflow or when shrinking rows - */ + // Ensure the cursor position invariant: ybase + y must be within buffer bounds + // This can be violated during reflow or when shrinking rows if (this.lines.length > 0) { const maxY = Math.max(0, this.lines.length - this.ybase - 1); this.y = Math.min(this.y, maxY); @@ -309,10 +293,8 @@ export class Buffer extends Disposable implements IBuffer { private _batchedMemoryCleanup(): boolean { let normalRun = true; if (this._memoryCleanupPosition >= this.lines.length) { - /* - * cleanup made it once through all lines, thus rescan in loop below to also catch shifted - * lines, which should finish rather quick if there are no more cleanups pending - */ + // cleanup made it once through all lines, thus rescan in loop below to also catch shifted + // lines, which should finish rather quick if there are no more cleanups pending this._memoryCleanupPosition = 0; normalRun = false; } @@ -324,11 +306,9 @@ export class Buffer extends Disposable implements IBuffer { return true; } } - /* - * normal runs always need another rescan afterwards - * if we made it here with normalRun=false, we are in a final run - * and can end the cleanup task for sure - */ + // normal runs always need another rescan afterwards + // if we made it here with normalRun=false, we are in a final run + // and can end the cleanup task for sure return normalRun; } @@ -389,10 +369,8 @@ export class Buffer extends Disposable implements IBuffer { private _reflowSmaller(newCols: number, newRows: number): void { const reflowCursorLine = this._optionsService.rawOptions.reflowCursorLine; const nullCell = this.getNullCell(DEFAULT_ATTR_DATA); - /* - * Gather all BufferLines that need to be inserted into the Buffer here so that they can be - * batched up and only committed once - */ + // Gather all BufferLines that need to be inserted into the Buffer here so that they can be + // batched up and only committed once const toInsert = []; let countToInsert = 0; // Go backwards as many lines may be trimmed and this will avoid considering them @@ -411,10 +389,8 @@ export class Buffer extends Disposable implements IBuffer { } if (!reflowCursorLine) { - /* - * If these lines contain the cursor don't touch them, the program will handle fixing up - * wrapped lines with the cursor - */ + // If these lines contain the cursor don't touch them, the program will handle fixing up + // wrapped lines with the cursor const absoluteY = this.ybase + this.y; if (absoluteY >= y && absoluteY < y + wrappedLines.length) { continue; @@ -440,10 +416,8 @@ export class Buffer extends Disposable implements IBuffer { } if (newLines.length > 0) { toInsert.push({ - /* - * countToInsert here gets the actual index, taking into account other inserted items. - * using this we can iterate through the list forwards - */ + // countToInsert here gets the actual index, taking into account other inserted items. + // using this we can iterate through the list forwards start: y + wrappedLines.length + countToInsert, newLines }); @@ -463,10 +437,8 @@ export class Buffer extends Disposable implements IBuffer { while (srcLineIndex >= 0) { const cellsToCopy = Math.min(srcCol, destCol); if (wrappedLines[destLineIndex] === undefined) { - /* - * Sanity check that the line exists, this has been known to fail for an unknown reason - * which would stop the reflow from happening if an exception would throw. - */ + // Sanity check that the line exists, this has been known to fail for an unknown reason + // which would stop the reflow from happening if an exception would throw. break; } wrappedLines[destLineIndex].copyCellsFrom(wrappedLines[srcLineIndex], srcCol - cellsToCopy, destCol - cellsToCopy, cellsToCopy, true); @@ -514,16 +486,12 @@ export class Buffer extends Disposable implements IBuffer { this.savedY = Math.min(this.savedY + linesToAdd, this.ybase + newRows - 1); } - /* - * Rearrange lines in the buffer if there are any insertions, this is done at the end rather - * than earlier so that it's a single O(n) pass through the buffer, instead of O(n^2) from many - * costly calls to CircularList.splice. - */ + // Rearrange lines in the buffer if there are any insertions, this is done at the end rather + // than earlier so that it's a single O(n) pass through the buffer, instead of O(n^2) from many + // costly calls to CircularList.splice. if (toInsert.length > 0) { - /* - * Record buffer insert events and then play them back backwards so that the indexes are - * correct - */ + // Record buffer insert events and then play them back backwards so that the indexes are + // correct const insertEvents: IInsertEvent[] = []; // Record original lines so they don't get overridden when we rearrange the list diff --git a/src/common/buffer/BufferLine.test.ts b/src/common/buffer/BufferLine.test.ts index 7e8f73f4c..3aec28872 100644 --- a/src/common/buffer/BufferLine.test.ts +++ b/src/common/buffer/BufferLine.test.ts @@ -288,11 +288,9 @@ describe('BufferLine', function(): void { assert.equal(line2.isWrapped, line.isWrapped); }); it('should support combining chars', function(): void { - /* - * CHAR_DATA_CODE_INDEX resembles current behavior in InputHandler.print - * --> set code to the last charCodeAt value of the string - * Note: needs to be fixed once the string pointer is in place - */ + // CHAR_DATA_CODE_INDEX resembles current behavior in InputHandler.print + // --> set code to the last charCodeAt value of the string + // Note: needs to be fixed once the string pointer is in place const line = new TestBufferLine(2, createCellData(1, 'e\u0301', 1)); assert.deepEqual(line.toArray(), [[1, 'e\u0301', 1, '\u0301'.charCodeAt(0)], [1, 'e\u0301', 1, '\u0301'.charCodeAt(0)]]); const line2 = new TestBufferLine(5, createCellData(1, 'a', 1), true); @@ -480,11 +478,9 @@ describe('BufferLine', function(): void { }); it('should always return some sane value', function(): void { const columns: number[] = []; - /* - * sanity check - broken line with invalid out of bound null width cells - * this can atm happen with deleting/inserting chars in inputhandler by "breaking" - * fullwidth pairs --> needs to be fixed after settling BufferLine impl - */ + // sanity check - broken line with invalid out of bound null width cells + // this can atm happen with deleting/inserting chars in inputhandler by "breaking" + // fullwidth pairs --> needs to be fixed after settling BufferLine impl const line = new TestBufferLine(10, NULL_CELL_DATA, false); assert.equal(line.translateToString(false, undefined, undefined, columns), ' '); assert.deepEqual(columns, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); @@ -504,10 +500,8 @@ describe('BufferLine', function(): void { const line = new TestBufferLine(3, NULL_CELL_DATA, false); line.addCodepointToCell(0, '\u0301'.charCodeAt(0), 0); const cell = line.loadCell(0, new CellData()); - /* - * chars contains single combining char - * width is set to 1 - */ + // chars contains single combining char + // width is set to 1 assert.deepEqual(cell.getAsCharData(), [DEFAULT_ATTR, '\u0301', 1, 0x0301]); // do not account a single combining char as combined assert.equal(cell.isCombined(), 0); @@ -519,10 +513,8 @@ describe('BufferLine', function(): void { line.setCell(0, cell); line.addCodepointToCell(0, '\u0301'.charCodeAt(0), 0); line.loadCell(0, cell); - /* - * chars contains 3 chars - * width is set to 1 - */ + // chars contains 3 chars + // width is set to 1 assert.deepEqual(cell.getAsCharData(), [123, 'e\u0301\u0301', 1, 0x0301]); // do not account a single combining char as combined assert.equal(cell.isCombined(), Content.IS_COMBINED_MASK); @@ -534,10 +526,8 @@ describe('BufferLine', function(): void { line.setCell(0, cell); line.addCodepointToCell(0, '\u0301'.charCodeAt(0), 0); line.loadCell(0, cell); - /* - * chars contains 2 chars - * width is set to 1 - */ + // chars contains 2 chars + // width is set to 1 assert.deepEqual(cell.getAsCharData(), [123, 'e\u0301', 1, 0x0301]); // do not account a single combining char as combined assert.equal(cell.isCombined(), Content.IS_COMBINED_MASK); diff --git a/src/common/buffer/BufferLine.ts b/src/common/buffer/BufferLine.ts index 0cef299e1..be200f112 100644 --- a/src/common/buffer/BufferLine.ts +++ b/src/common/buffer/BufferLine.ts @@ -9,13 +9,11 @@ import { CellData } from 'common/buffer/CellData'; import { Attributes, BgFlags, CHAR_DATA_ATTR_INDEX, CHAR_DATA_CHAR_INDEX, CHAR_DATA_WIDTH_INDEX, Content, NULL_CELL_CHAR, NULL_CELL_CODE, NULL_CELL_WIDTH, WHITESPACE_CELL_CHAR } from 'common/buffer/Constants'; import { stringFromCodePoint } from 'common/input/TextDecoder'; -/* - * Buffer memory layout: - * - * [0]: content `uint32_t` - wcwidth(2) comb(1) codepoint(21) - * [1]: fg `uint32_t` - flags(8) r(8) g(8) b(8) - * [2]: bg `uint32_t` - flags(8) r(8) g(8) b(8) - */ +// Buffer memory layout: +// +// [0]: content `uint32_t` - wcwidth(2) comb(1) codepoint(21) +// [1]: fg `uint32_t` - flags(8) r(8) g(8) b(8) +// [2]: bg `uint32_t` - flags(8) r(8) g(8) b(8) const enum Constants { /** The number of 32 bit array indices taken by one cell. */ @@ -257,19 +255,15 @@ export class BufferLine implements IBufferLine { this._combined[index] += stringFromCodePoint(codePoint); } else { if (content & Content.CODEPOINT_MASK) { - /* - * normal case for combining chars: - * - move current leading char + new one into combined string - * - set combined flag - */ + // normal case for combining chars: + // - move current leading char + new one into combined string + // - set combined flag this._combined[index] = stringFromCodePoint(content & Content.CODEPOINT_MASK) + stringFromCodePoint(codePoint); content &= ~Content.CODEPOINT_MASK; // set codepoint in buffer to 0 content |= Content.IS_COMBINED_MASK; } else { - /* - * should not happen - we actually have no data in the cell yet - * simply set the data in the cell buffer with a width of 1 - */ + // should not happen - we actually have no data in the cell yet + // simply set the data in the cell buffer with a width of 1 content = codePoint | (1 << Content.WIDTH_SHIFT); } } @@ -324,11 +318,9 @@ export class BufferLine implements IBufferLine { } } - /* - * handle fullwidth at pos: - * - reset pos-1 if wide char - * - reset pos if width==0 (previous second cell of a wide char) - */ + // handle fullwidth at pos: + // - reset pos-1 if wide char + // - reset pos if width==0 (previous second cell of a wide char) if (pos && this.getWidth(pos - 1) === 2) { this.setCellFromCodepoint(pos - 1, 0, 1, fillCellData); } diff --git a/src/common/buffer/BufferReflow.ts b/src/common/buffer/BufferReflow.ts index 5eaeae406..44aa0976f 100644 --- a/src/common/buffer/BufferReflow.ts +++ b/src/common/buffer/BufferReflow.ts @@ -23,10 +23,8 @@ export interface INewLayoutResult { * @param reflowCursorLine Whether to reflow the line containing the cursor. */ export function reflowLargerGetLinesToRemove(lines: CircularList, oldCols: number, newCols: number, bufferAbsoluteY: number, nullCell: ICellData, reflowCursorLine: boolean): number[] { - /* - * Gather all BufferLines that need to be removed from the Buffer here so that they can be - * batched up and only committed once - */ + // Gather all BufferLines that need to be removed from the Buffer here so that they can be + // batched up and only committed once const toRemove: number[] = []; for (let y = 0; y < lines.length - 1; y++) { @@ -45,10 +43,8 @@ export function reflowLargerGetLinesToRemove(lines: CircularList, o } if (!reflowCursorLine) { - /* - * If these lines contain the cursor don't touch them, the program will handle fixing up - * wrapped lines with the cursor - */ + // If these lines contain the cursor don't touch them, the program will handle fixing up + // wrapped lines with the cursor if (bufferAbsoluteY >= y && bufferAbsoluteY < i) { y += wrappedLines.length - 1; continue; @@ -184,10 +180,8 @@ export function reflowSmallerGetNewLineLengths(wrappedLines: BufferLine[], oldCo const newLineLengths: number[] = []; const cellsNeeded = wrappedLines.map((l, i) => getWrappedLineTrimmedLength(wrappedLines, i, oldCols)).reduce((p, c) => p + c); - /* - * Use srcCol and srcLine to find the new wrapping point, use that to get the cellsAvailable and - * linesNeeded - */ + // Use srcCol and srcLine to find the new wrapping point, use that to get the cellsAvailable and + // linesNeeded let srcCol = 0; let srcLine = 0; let cellsAvailable = 0; @@ -220,11 +214,9 @@ export function getWrappedLineTrimmedLength(lines: BufferLine[], i: number, cols if (i === lines.length - 1) { return lines[i].getTrimmedLength(); } - /* - * Detect whether the following line starts with a wide character and the end of the current line - * is null, if so then we can be pretty sure the null character should be excluded from the line - * length] - */ + // Detect whether the following line starts with a wide character and the end of the current line + // is null, if so then we can be pretty sure the null character should be excluded from the line + // length] const endsInNull = !(lines[i].hasContent(cols - 1)) && lines[i].getWidth(cols - 1) === 1; const followingLineStartsWithWide = lines[i + 1].getWidth(0) === 2; if (endsInNull && followingLineStartsWithWide) { diff --git a/src/common/buffer/BufferSet.ts b/src/common/buffer/BufferSet.ts index 2bad2171f..32a4bf3b4 100644 --- a/src/common/buffer/BufferSet.ts +++ b/src/common/buffer/BufferSet.ts @@ -43,10 +43,8 @@ export class BufferSet extends Disposable implements IBufferSet { this._normalBuffer.value = this._normal; this._normal.fillViewportRows(); - /* - * The alt buffer should never have scrollback. - * See http://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-The-Alternate-Screen-Buffer - */ + // The alt buffer should never have scrollback. + // See http://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-The-Alternate-Screen-Buffer this._alt = new Buffer(false, this._optionsService, this._bufferService, this._logService); this._altBuffer.value = this._alt; this._activeBuffer = this._normal; @@ -88,11 +86,9 @@ export class BufferSet extends Disposable implements IBufferSet { } this._normal.x = this._alt.x; this._normal.y = this._alt.y; - /* - * The alt buffer should always be cleared when we switch to the normal - * buffer. This frees up memory since the alt buffer should always be new - * when activated. - */ + // The alt buffer should always be cleared when we switch to the normal + // buffer. This frees up memory since the alt buffer should always be new + // when activated. this._alt.clearAllMarkers(); this._alt.clear(); this._activeBuffer = this._normal; @@ -109,10 +105,8 @@ export class BufferSet extends Disposable implements IBufferSet { if (this._activeBuffer === this._alt) { return; } - /* - * Since the alt buffer is always cleared when the normal buffer is - * activated, we want to fill it when switching to it. - */ + // Since the alt buffer is always cleared when the normal buffer is + // activated, we want to fill it when switching to it. this._alt.fillViewportRows(fillAttr); this._alt.x = this._normal.x; this._alt.y = this._normal.y; diff --git a/src/common/buffer/CellData.ts b/src/common/buffer/CellData.ts index 29e4a0f50..43c4c594b 100644 --- a/src/common/buffer/CellData.ts +++ b/src/common/buffer/CellData.ts @@ -65,10 +65,8 @@ export class CellData extends AttributeData implements ICellData { } else if (value[CHAR_DATA_CHAR_INDEX].length === 2) { const code = value[CHAR_DATA_CHAR_INDEX].charCodeAt(0); - /* - * if the 2-char string is a surrogate create single codepoint - * everything else is combined - */ + // if the 2-char string is a surrogate create single codepoint + // everything else is combined if (0xD800 <= code && code <= 0xDBFF) { const second = value[CHAR_DATA_CHAR_INDEX].charCodeAt(1); if (0xDC00 <= second && second <= 0xDFFF) { diff --git a/src/common/input/Keyboard.test.ts b/src/common/input/Keyboard.test.ts index 4e47cbce0..47f516f0f 100644 --- a/src/common/input/Keyboard.test.ts +++ b/src/common/input/Keyboard.test.ts @@ -123,10 +123,8 @@ describe('Keyboard', () => { }); describe('On non-macOS platforms', () => { - /* - * Evalueate alt + arrow key movement, which is a feature of terminal emulators but not VT100 - * http://unix.stackexchange.com/a/108106 - */ + // Evalueate alt + arrow key movement, which is a feature of terminal emulators but not VT100 + // http://unix.stackexchange.com/a/108106 it('should return \\x1b[1;3D for alt+left', () => { assert.equal(testEvaluateKeyboardEvent({ altKey: true, keyCode: 37 }, { isMac: false }).key, '\x1b[1;3D'); // CSI 1;3 D }); diff --git a/src/common/input/Keyboard.ts b/src/common/input/Keyboard.ts index 54bc4a27e..545b436c8 100644 --- a/src/common/input/Keyboard.ts +++ b/src/common/input/Keyboard.ts @@ -43,10 +43,8 @@ export function evaluateKeyboardEvent( ): IKeyboardResult { const result: IKeyboardResult = { type: KeyboardResultType.SEND_KEY, - /* - * Whether to cancel event propagation (NOTE: this may not be needed since the event is - * canceled at the end of keyDown - */ + // Whether to cancel event propagation (NOTE: this may not be needed since the event is + // canceled at the end of keyDown cancel: false, // The new key even to emit key: undefined @@ -102,10 +100,8 @@ export function evaluateKeyboardEvent( case 13: // return/enter if (ev.key === 'c' && ev.ctrlKey) { - /* - * HACK: Safari on iPad, iOS, AppleVisionPro sends key 13 when typing ctrl-c on hardware - * keyboard - */ + // HACK: Safari on iPad, iOS, AppleVisionPro sends key 13 when typing ctrl-c on hardware + // keyboard result.key = C0.ETX; } else { result.key = ev.altKey ? C0.ESC + C0.CR : C0.CR; @@ -175,10 +171,8 @@ export function evaluateKeyboardEvent( case 45: // insert if (!ev.shiftKey && !ev.ctrlKey) { - /* - * or + are used to - * copy-paste on some systems. - */ + // or + are used to + // copy-paste on some systems. result.key = C0.ESC + '[2~'; } break; @@ -352,13 +346,11 @@ export function evaluateKeyboardEvent( } else if (ev.keyCode === 32) { result.key = C0.ESC + (ev.ctrlKey ? C0.NUL : ' '); } else if (ev.key === 'Dead' && ev.code.startsWith('Key')) { - /* - * Reference: https://github.com/xtermjs/xterm.js/issues/3725 - * Alt will produce a "dead key" (initate composition) with some - * of the letters in US layout (e.g. N/E/U). - * It's safe to match against Key* since no other `code` values begin with "Key". - * https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/code/code_values#code_values_on_mac - */ + // Reference: https://github.com/xtermjs/xterm.js/issues/3725 + // Alt will produce a "dead key" (initate composition) with some + // of the letters in US layout (e.g. N/E/U). + // It's safe to match against Key* since no other `code` values begin with "Key". + // https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/code/code_values#code_values_on_mac let keyString = ev.code.slice(3, 4); if (!ev.shiftKey) { keyString = keyString.toLowerCase(); @@ -371,10 +363,8 @@ export function evaluateKeyboardEvent( result.type = KeyboardResultType.SELECT_ALL; } } else if (ev.key && !ev.ctrlKey && !ev.altKey && !ev.metaKey && ev.keyCode >= 48 && ev.key.length === 1) { - /* - * Include only keys that that result in a _single_ character; don't include num lock, - * volume up, etc. - */ + // Include only keys that that result in a _single_ character; don't include num lock, + // volume up, etc. result.key = ev.key; } else if (ev.key && ev.ctrlKey && ev.shiftKey) { switch (ev.code) { diff --git a/src/common/input/KittyKeyboard.test.ts b/src/common/input/KittyKeyboard.test.ts index 44c4ae2bb..2a832d75f 100644 --- a/src/common/input/KittyKeyboard.test.ts +++ b/src/common/input/KittyKeyboard.test.ts @@ -40,10 +40,8 @@ describe('KittyKeyboard', () => { const flags = KittyKeyboardFlags.DISAMBIGUATE_ESCAPE_CODES; it('shift+letter sends plain character in DISAMBIGUATE mode', () => { - /* - * Kitty spec: DISAMBIGUATE only encodes keys ambiguous in legacy encoding - * Shift+a → "A" is not ambiguous, so send plain "A" - */ + // Kitty spec: DISAMBIGUATE only encodes keys ambiguous in legacy encoding + // Shift+a → "A" is not ambiguous, so send plain "A" const result = kitty.evaluate(createEvent({ key: 'A', shiftKey: true }), flags); assert.strictEqual(result.key, 'A'); }); @@ -556,10 +554,8 @@ describe('KittyKeyboard', () => { }); }); - /* - * Enabling REPORT_EVENT_TYPES without DISAMBIGUATE_ESCAPE_CODES doesn't really make sense and - * isn't specified in the spec, but press and repeat events shouldn't get swallowed. - */ + // Enabling REPORT_EVENT_TYPES without DISAMBIGUATE_ESCAPE_CODES doesn't really make sense and + // isn't specified in the spec, but press and repeat events shouldn't get swallowed. describe('REPORT_EVENT_TYPES flag without DISAMBIGUATE_ESCAPE_CODES', () => { const flags = KittyKeyboardFlags.REPORT_EVENT_TYPES; diff --git a/src/common/input/KittyKeyboard.ts b/src/common/input/KittyKeyboard.ts index 658d4e0d9..ff57b4f89 100644 --- a/src/common/input/KittyKeyboard.ts +++ b/src/common/input/KittyKeyboard.ts @@ -437,12 +437,10 @@ export class KittyKeyboard { return result; } - /* - * Spec § "Report all keys as escape codes": "Additionally, with this mode, - * events for pressing modifier keys are reported." — i.e. *without* this - * mode, modifier-key press events are suppressed. Kitty's is_modifier_key() - * treats CapsLock/NumLock/ScrollLock as modifier keys for this rule. - */ + // Spec § "Report all keys as escape codes": "Additionally, with this mode, + // events for pressing modifier keys are reported." — i.e. *without* this + // mode, modifier-key press events are suppressed. Kitty's is_modifier_key() + // treats CapsLock/NumLock/ScrollLock as modifier keys for this rule. if (this._isLockKey(ev) && !(flags & KittyKeyboardFlags.REPORT_ALL_KEYS_AS_ESCAPE_CODES)) { return result; } @@ -476,10 +474,8 @@ export class KittyKeyboard { // Special handling for Enter/Tab/Backspace. const specialKey = keyCode === 13 || keyCode === 9 || keyCode === 127; - /* - * Per spec, Enter/Tab/Backspace will not have release events unless "Report all keys as escape - * codes" is also set. - */ + // Per spec, Enter/Tab/Backspace will not have release events unless "Report all keys as escape + // codes" is also set. if (specialKey && eventType === KittyKeyboardEventType.RELEASE && !(flags & KittyKeyboardFlags.REPORT_ALL_KEYS_AS_ESCAPE_CODES)) { return result; } @@ -489,18 +485,14 @@ export class KittyKeyboard { const useCsiU = !!( flags & KittyKeyboardFlags.REPORT_ALL_KEYS_AS_ESCAPE_CODES || (reportEventTypes && eventType === KittyKeyboardEventType.RELEASE) || - /* - * Enabling REPORT_EVENT_TYPES without DISAMBIGUATE_ESCAPE_CODES doesn't really make sense, so - * just make REPORT_EVENT_TYPES imply DISAMBIGUATE_ESCAPE_CODES here for simplicity. - * See: https://github.com/kovidgoyal/kitty/issues/9999 - */ + // Enabling REPORT_EVENT_TYPES without DISAMBIGUATE_ESCAPE_CODES doesn't really make sense, so + // just make REPORT_EVENT_TYPES imply DISAMBIGUATE_ESCAPE_CODES here for simplicity. + // See: https://github.com/kovidgoyal/kitty/issues/9999 ((flags & KittyKeyboardFlags.DISAMBIGUATE_ESCAPE_CODES || reportEventTypes) && ( - /* - * Per spec, Enter/Tab/Backspace "still generate the same bytes as in legacy mode" and - * consider space to be a text-generating key, so these skip the isFunc fast-path and only - * get CSI u when modifiers are present (handled below). - */ + // Per spec, Enter/Tab/Backspace "still generate the same bytes as in legacy mode" and + // consider space to be a text-generating key, so these skip the isFunc fast-path and only + // get CSI u when modifiers are present (handled below). (isFunc && !specialKey) || ( (modifiers > 0 && ev.key.length !== 1) || diff --git a/src/common/input/TextDecoder.ts b/src/common/input/TextDecoder.ts index cc503166d..7ec9c7cd2 100644 --- a/src/common/input/TextDecoder.ts +++ b/src/common/input/TextDecoder.ts @@ -28,13 +28,11 @@ export function utf32ToString(data: Uint32Array, start: number = 0, end: number for (let i = start; i < end; ++i) { let codepoint = data[i]; if (codepoint > 0xFFFF) { - /* - * JS strings are encoded as UTF16, thus a non BMP codepoint gets converted into a surrogate - * pair conversion rules: - * - subtract 0x10000 from code point, leaving a 20 bit number - * - add high 10 bits to 0xD800 --> first surrogate - * - add low 10 bits to 0xDC00 --> second surrogate - */ + // JS strings are encoded as UTF16, thus a non BMP codepoint gets converted into a surrogate + // pair conversion rules: + // - subtract 0x10000 from code point, leaving a 20 bit number + // - add high 10 bits to 0xD800 --> first surrogate + // - add low 10 bits to 0xDC00 --> second surrogate codepoint -= 0x10000; result += String.fromCharCode((codepoint >> 10) + 0xD800) + String.fromCharCode((codepoint % 0x400) + 0xDC00); } else { diff --git a/src/common/input/UnicodeV6.test.ts b/src/common/input/UnicodeV6.test.ts index 95c2f6e2f..98dc97dc2 100644 --- a/src/common/input/UnicodeV6.test.ts +++ b/src/common/input/UnicodeV6.test.ts @@ -12,10 +12,8 @@ it('wcwidth should match all values from the old implementation', function(): vo // old implementation const wcwidthOld = (function(opts: {nul: number, control: number}): (ucs: number) => number { - /* - * extracted from https://www.cl.cam.ac.uk/%7Emgk25/ucs/wcwidth.c - * combining characters - */ + // extracted from https://www.cl.cam.ac.uk/%7Emgk25/ucs/wcwidth.c + // combining characters const COMBINING_BMP = [ [0x0300, 0x036F], [0x0483, 0x0486], [0x0488, 0x0489], [0x0591, 0x05BD], [0x05BF, 0x05BF], [0x05C1, 0x05C2], @@ -151,20 +149,18 @@ it('wcwidth should match all values from the old implementation', function(): vo } return table; } - /* - * get width from lookup table - * position in container : num / CODEPOINTS_PER_ITEM - * ==> n = table[Math.floor(num / 16)] - * ==> n = table[num >> 4] - * 16 codepoints per number: FFEEDDCCBBAA99887766554433221100 - * position in number : (num % CODEPOINTS_PER_ITEM) * BITWIDTH - * ==> m = (n % 16) * 2 - * ==> m = (num & 15) << 1 - * right shift to position m - * ==> n = n >> m e.g. m=12 000000000000FFEEDDCCBBAA99887766 - * we are only interested in 2 LSBs, cut off higher bits - * ==> n = n & 3 e.g. 000000000000000000000000000000XX - */ + // get width from lookup table + // position in container : num / CODEPOINTS_PER_ITEM + // ==> n = table[Math.floor(num / 16)] + // ==> n = table[num >> 4] + // 16 codepoints per number: FFEEDDCCBBAA99887766554433221100 + // position in number : (num % CODEPOINTS_PER_ITEM) * BITWIDTH + // ==> m = (n % 16) * 2 + // ==> m = (num & 15) << 1 + // right shift to position m + // ==> n = n >> m e.g. m=12 000000000000FFEEDDCCBBAA99887766 + // we are only interested in 2 LSBs, cut off higher bits + // ==> n = n & 3 e.g. 000000000000000000000000000000XX return (num: number): number => { num |= 0; // get asm.js like optimization under V8 if (num < 32) { diff --git a/src/common/input/UnicodeV6.ts b/src/common/input/UnicodeV6.ts index 4b6bd55c0..83265f705 100644 --- a/src/common/input/UnicodeV6.ts +++ b/src/common/input/UnicodeV6.ts @@ -94,10 +94,8 @@ export class UnicodeV6 implements IUnicodeVersionProvider { table.fill(0, 1, 32); table.fill(0, 0x7f, 0xa0); - /* - * apply wide char rules first - * wide chars - */ + // apply wide char rules first + // wide chars table.fill(2, 0x1100, 0x1160); table[0x2329] = 2; table[0x232a] = 2; @@ -111,13 +109,11 @@ export class UnicodeV6 implements IUnicodeVersionProvider { table.fill(2, 0xff00, 0xff61); table.fill(2, 0xffe0, 0xffe7); - /* - * apply combining last to ensure we overwrite - * wrongly wide set chars: - * the original algo evals combining first and falls - * through to wide check so we simply do here the opposite - * combining 0 - */ + // apply combining last to ensure we overwrite + // wrongly wide set chars: + // the original algo evals combining first and falls + // through to wide check so we simply do here the opposite + // combining 0 for (let r = 0; r < BMP_COMBINING.length; ++r) { table.fill(0, BMP_COMBINING[r][0], BMP_COMBINING[r][1] + 1); } diff --git a/src/common/input/Win32InputMode.ts b/src/common/input/Win32InputMode.ts index 23f6ade0f..72907836d 100644 --- a/src/common/input/Win32InputMode.ts +++ b/src/common/input/Win32InputMode.ts @@ -198,10 +198,8 @@ export class Win32InputMode { * Returns 0 for non-character keys. */ private _getUnicodeChar(ev: IKeyboardEvent): number { - /* - * Handle special keys that produce control characters - * Ctrl modifies some of these: Ctrl+Enter=LF, Ctrl+Backspace=DEL - */ + // Handle special keys that produce control characters + // Ctrl modifies some of these: Ctrl+Enter=LF, Ctrl+Backspace=DEL if (ev.ctrlKey && !ev.altKey && !ev.metaKey) { if (ev.key === 'Enter') { return 0x0A; // Line feed (Ctrl+Enter) @@ -247,11 +245,9 @@ export class Win32InputMode { state |= Win32ControlKeyState.SHIFT_PRESSED; } - /* - * Note: We can't distinguish left/right for ctrl/alt in standard browser events, - * so we use the generic pressed flags. The right-side flags are used when - * we can detect them (e.g., via code property). - */ + // Note: We can't distinguish left/right for ctrl/alt in standard browser events, + // so we use the generic pressed flags. The right-side flags are used when + // we can detect them (e.g., via code property). if (ev.ctrlKey) { if (ev.code === 'ControlRight') { state |= Win32ControlKeyState.RIGHT_CTRL_PRESSED; diff --git a/src/common/input/WriteBuffer.ts b/src/common/input/WriteBuffer.ts index 888db9ac5..b936c9dbf 100644 --- a/src/common/input/WriteBuffer.ts +++ b/src/common/input/WriteBuffer.ts @@ -90,16 +90,12 @@ export class WriteBuffer extends Disposable { * @deprecated Unreliable, to be removed soon. */ public writeSync(data: string | Uint8Array, maxSubsequentCalls?: number): void { - /* - * stop writeSync recursions with maxSubsequentCalls argument - * This is dangerous to use as it will lose the current data chunk - * and return immediately. - */ + // stop writeSync recursions with maxSubsequentCalls argument + // This is dangerous to use as it will lose the current data chunk + // and return immediately. if (maxSubsequentCalls !== undefined && this._syncCalls > maxSubsequentCalls) { - /* - * comment next line if a whole loop block should only contain x `writeSync` calls - * (total flat vs. deep nested limit) - */ + // comment next line if a whole loop block should only contain x `writeSync` calls + // (total flat vs. deep nested limit) this._syncCalls = 0; return; } @@ -116,22 +112,18 @@ export class WriteBuffer extends Disposable { } this._isSyncWriting = true; - /* - * force sync processing on pending data chunks to avoid in-band data scrambling - * does the same as innerWrite but without event loop - * we have to do it here as single loop steps to not corrupt loop subject - * by another writeSync call triggered from _action - */ + // force sync processing on pending data chunks to avoid in-band data scrambling + // does the same as innerWrite but without event loop + // we have to do it here as single loop steps to not corrupt loop subject + // by another writeSync call triggered from _action let chunk: string | Uint8Array | undefined; while (chunk = this._writeBuffer.shift()) { this._action(chunk); const cb = this._callbacks.shift(); if (cb) cb(); } - /* - * reset to avoid reprocessing of chunks with scheduled innerWrite call - * stopping scheduled innerWrite by offset > length condition - */ + // reset to avoid reprocessing of chunks with scheduled innerWrite call + // stopping scheduled innerWrite by offset > length condition this._pendingData = 0; this._bufferOffset = 0x7FFFFFFF; @@ -149,11 +141,9 @@ export class WriteBuffer extends Disposable { if (!this._writeBuffer.length) { this._bufferOffset = 0; - /* - * If this is the first write call after the user has done some input, - * parse it immediately to minimize input latency, - * otherwise schedule for the next event - */ + // If this is the first write call after the user has done some input, + // parse it immediately to minimize input latency, + // otherwise schedule for the next event if (this._didUserInput) { this._didUserInput = false; this._pendingData += data.length; @@ -244,19 +234,15 @@ export class WriteBuffer extends Disposable { * throughput by eval'ing `startTime` upfront pulling at least one more chunk into the * current microtask queue (executed before setTimeout). */ - /* - * const continuation: (r: boolean) => void = performance.now() - startTime >= - * Constants.WRITE_TIMEOUT_MS - * ? r => setTimeout(() => this._innerWrite(0, r)) - * : r => this._innerWrite(startTime, r); - */ + // const continuation: (r: boolean) => void = performance.now() - startTime >= + // Constants.WRITE_TIMEOUT_MS + // ? r => setTimeout(() => this._innerWrite(0, r)) + // : r => this._innerWrite(startTime, r); - /* - * Handle exceptions synchronously to current band position, idea: - * 1. spawn a single microtask which we allow to throw hard - * 2. spawn a promise immediately resolving to `true` - * (executed on the same queue, thus properly aligned before continuation happens) - */ + // Handle exceptions synchronously to current band position, idea: + // 1. spawn a single microtask which we allow to throw hard + // 2. spawn a promise immediately resolving to `true` + // (executed on the same queue, thus properly aligned before continuation happens) result.catch(err => { queueMicrotask(() => {throw err;}); return Promise.resolve(false); @@ -274,10 +260,8 @@ export class WriteBuffer extends Disposable { } } if (this._writeBuffer.length > this._bufferOffset) { - /* - * Allow renderer to catch up before processing the next batch - * trim already processed chunks if we are above threshold - */ + // Allow renderer to catch up before processing the next batch + // trim already processed chunks if we are above threshold if (this._bufferOffset > Constants.WRITE_BUFFER_LENGTH_THRESHOLD) { this._writeBuffer = this._writeBuffer.slice(this._bufferOffset); this._callbacks = this._callbacks.slice(this._bufferOffset); diff --git a/src/common/input/XParseColor.ts b/src/common/input/XParseColor.ts index 2f4a79dc8..ae4a3bcae 100644 --- a/src/common/input/XParseColor.ts +++ b/src/common/input/XParseColor.ts @@ -50,11 +50,9 @@ export function parseColor(data: string): [number, number, number] | undefined { } } - /* - * Named colors are currently not supported due to the large addition to the xterm.js bundle size - * they would add. In order to support named colors, we would need some way of optionally loading - * additional payloads so startup/download time is not bloated (see #3530). - */ + // Named colors are currently not supported due to the large addition to the xterm.js bundle size + // they would add. In order to support named colors, we would need some way of optionally loading + // additional payloads so startup/download time is not bloated (see #3530). } // pad hex output to requested bit width diff --git a/src/common/parser/ApcParser.ts b/src/common/parser/ApcParser.ts index 453c71110..8f921f8cb 100644 --- a/src/common/parser/ApcParser.ts +++ b/src/common/parser/ApcParser.ts @@ -181,10 +181,8 @@ export class ApcHandler implements IApcHandler { } else if (success) { ret = this._handler(this._data); if (ret instanceof Promise) { - /* - * need to hold data until `ret` got resolved - * dont care for errors, data will be freed anyway on next start - */ + // need to hold data until `ret` got resolved + // dont care for errors, data will be freed anyway on next start return ret.then(res => { this._data = ''; this._hitLimit = false; diff --git a/src/common/parser/DcsParser.ts b/src/common/parser/DcsParser.ts index 849b82709..692200230 100644 --- a/src/common/parser/DcsParser.ts +++ b/src/common/parser/DcsParser.ts @@ -147,12 +147,10 @@ export class DcsHandler implements IDcsHandler { constructor(private _handler: (data: string, params: IParams) => boolean | Promise) { } public hook(params: IParams): void { - /* - * since we need to preserve params until `unhook`, we have to clone it - * (only borrowed from parser and spans multiple parser states) - * perf optimization: - * clone only, if we have non empty params, otherwise stick with default - */ + // since we need to preserve params until `unhook`, we have to clone it + // (only borrowed from parser and spans multiple parser states) + // perf optimization: + // clone only, if we have non empty params, otherwise stick with default this._params = (params.length > 1 || params.params[0]) ? params.clone() : EMPTY_PARAMS; this._data = ''; this._hitLimit = false; @@ -176,10 +174,8 @@ export class DcsHandler implements IDcsHandler { } else if (success) { ret = this._handler(this._data, this._params); if (ret instanceof Promise) { - /* - * need to hold data and params until `ret` got resolved - * dont care for errors, data will be freed anyway on next start - */ + // need to hold data and params until `ret` got resolved + // dont care for errors, data will be freed anyway on next start return ret.then(res => { this._params = EMPTY_PARAMS; this._data = ''; diff --git a/src/common/parser/EscapeSequenceParser.test.ts b/src/common/parser/EscapeSequenceParser.test.ts index 17cc37ee3..2d5a04df7 100644 --- a/src/common/parser/EscapeSequenceParser.test.ts +++ b/src/common/parser/EscapeSequenceParser.test.ts @@ -2065,10 +2065,8 @@ async function throwsAsync(fn: () => Promise, message?: string | undefined) } describe('EscapeSequenceParser - async', () => { - /* - * sequences: SGR 1;31 | hello SP | ESC %G | wor | ESC E | ld! | SGR 0 | EXE \r\n | $> | DCS 1;2 a [xyz] ST | OSC 1;foo=bar ST | APC X abc ST | FIN - * needed handlers: CSI m, PRINT, ESC %G, ESC E, EXE \r, EXE \n, OSC 1, APC X - */ + // sequences: SGR 1;31 | hello SP | ESC %G | wor | ESC E | ld! | SGR 0 | EXE \r\n | $> | DCS 1;2 a [xyz] ST | OSC 1;foo=bar ST | APC X abc ST | FIN + // needed handlers: CSI m, PRINT, ESC %G, ESC E, EXE \r, EXE \n, OSC 1, APC X const INPUT = '\x1b[1;31mhello \x1b%Gwor\x1bEld!\x1b[0m\r\n$>\x1bP1;2axyz\x1b\\\x1b]1;foo=bar\x1b\\\x1b_Xabc\x1b\\FIN'; let RESULT: any[]; let parser: TestEscapeSequenceParser; @@ -2157,10 +2155,8 @@ describe('EscapeSequenceParser - async', () => { it('sync parse call does not work anymore', () => { assert.notEqual(!parseSync(parser, INPUT), true); assert.notDeepEqual(callstack, RESULT); - /* - * due to sync calling we should save exactly one saved stack - * proper continuation is not possible anymore, as we lost the promise resolve value - */ + // due to sync calling we should save exactly one saved stack + // proper continuation is not possible anymore, as we lost the promise resolve value assert.equal(parser.trackedStack.length, 1); }); it('improper continuation should throw', async () => { @@ -2226,10 +2222,8 @@ describe('EscapeSequenceParser - async', () => { // split to single char input for (let i = 0; i < INPUT.length; ++i) { - /* - * Note: a single fully awaited parse call always ends in sync mode, - * which re-enables faster sync processing in the higher up callstack - */ + // Note: a single fully awaited parse call always ends in sync mode, + // which re-enables faster sync processing in the higher up callstack await parseP(parser, INPUT[i]); } assert.deepEqual(callstack, RESULT); diff --git a/src/common/parser/EscapeSequenceParser.ts b/src/common/parser/EscapeSequenceParser.ts index be4877766..d944112db 100644 --- a/src/common/parser/EscapeSequenceParser.ts +++ b/src/common/parser/EscapeSequenceParser.ts @@ -15,22 +15,20 @@ import { ApcParser } from 'common/parser/ApcParser'; /** * VT commands done by the parser */ -/* - * @vt: #Y ESC CSI "Control Sequence Introducer" "ESC [" "Start of a CSI sequence." - * @vt: #Y ESC OSC "Operating System Command" "ESC ]" "Start of an OSC sequence." - * @vt: #Y ESC DCS "Device Control String" "ESC P" "Start of a DCS sequence." - * @vt: #Y ESC ST "String Terminator" "ESC \" "Terminator used for string type sequences." - * @vt: #Y ESC PM "Privacy Message" "ESC ^" "Start of a privacy message." - * @vt: #Y ESC APC "Application Program Command" "ESC _" "Start of an APC sequence." - * @vt: #Y C1 CSI "Control Sequence Introducer" "\x9B" "Start of a CSI sequence." - * @vt: #Y C1 OSC "Operating System Command" "\x9D" "Start of an OSC sequence." - * @vt: #Y C1 DCS "Device Control String" "\x90" "Start of a DCS sequence." - * @vt: #Y C1 ST "String Terminator" "\x9C" "Terminator used for string type sequences." - * @vt: #Y C1 PM "Privacy Message" "\x9E" "Start of a privacy message." - * @vt: #Y C1 APC "Application Program Command" "\x9F" "Start of an APC sequence." - * @vt: #Y C0 NUL "Null" "\0, \x00" "NUL is ignored." - * @vt: #Y C0 ESC "Escape" "\e, \x1B" "Start of a sequence. Cancels any other sequence." - */ +// @vt: #Y ESC CSI "Control Sequence Introducer" "ESC [" "Start of a CSI sequence." +// @vt: #Y ESC OSC "Operating System Command" "ESC ]" "Start of an OSC sequence." +// @vt: #Y ESC DCS "Device Control String" "ESC P" "Start of a DCS sequence." +// @vt: #Y ESC ST "String Terminator" "ESC \" "Terminator used for string type sequences." +// @vt: #Y ESC PM "Privacy Message" "ESC ^" "Start of a privacy message." +// @vt: #Y ESC APC "Application Program Command" "ESC _" "Start of an APC sequence." +// @vt: #Y C1 CSI "Control Sequence Introducer" "\x9B" "Start of a CSI sequence." +// @vt: #Y C1 OSC "Operating System Command" "\x9D" "Start of an OSC sequence." +// @vt: #Y C1 DCS "Device Control String" "\x90" "Start of a DCS sequence." +// @vt: #Y C1 ST "String Terminator" "\x9C" "Terminator used for string type sequences." +// @vt: #Y C1 PM "Privacy Message" "\x9E" "Start of a privacy message." +// @vt: #Y C1 APC "Application Program Command" "\x9F" "Start of an APC sequence." +// @vt: #Y C0 NUL "Null" "\0, \x00" "NUL is ignored." +// @vt: #Y C0 ESC "Escape" "\e, \x1B" "Start of a sequence. Cancels any other sequence." /** * Table values are generated like this: @@ -97,10 +95,8 @@ const NON_ASCII_PRINTABLE = 0xA0; * Taken from https://vt100.net/emu/dec_ansi_parser. */ export const VT500_TRANSITION_TABLE = (function (): TransitionTable { - /* - * table size: - * (ParserState.STATE_LENGTH - 1) << TableAccess.INDEX_STATE_SHIFT | NON_ASCII_PRINTABLE + 1 - */ + // table size: + // (ParserState.STATE_LENGTH - 1) << TableAccess.INDEX_STATE_SHIFT | NON_ASCII_PRINTABLE + 1 const table: TransitionTable = new TransitionTable(4257); // range macro for byte @@ -504,11 +500,9 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP this._params.resetZdm(); this._collect = 0; this.precedingJoinState = 0; - /* - * abort pending continuation from async handler - * Here the RESET type indicates, that the next parse call will - * ignore any saved stack, instead continues sync with next codepoint from GROUND - */ + // abort pending continuation from async handler + // Here the RESET type indicates, that the next parse call will + // ignore any saved stack, instead continues sync with next codepoint from GROUND if (this._parseStack.state !== ParserStackType.NONE) { this._parseStack.state = ParserStackType.RESET; this._parseStack.handlers = []; // also release handlers ref @@ -585,10 +579,8 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP // resume from async handler if (this._parseStack.state) { - /* - * allow sync parser reset even in continuation mode - * Note: can be used to recover parser from improper continuation error below - */ + // allow sync parser reset even in continuation mode + // Note: can be used to recover parser from improper continuation error below if (this._parseStack.state === ParserStackType.RESET) { this._parseStack.state = ParserStackType.NONE; start = this._parseStack.chunkPos + 1; // continue with next codepoint in GROUND @@ -613,11 +605,9 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP throw new Error('improper continuation due to previous async handler, giving up parsing'); } - /* - * we have to resume the old handler loop if: - * - return value of the promise was `false` - * - handlers are not exhausted yet - */ + // we have to resume the old handler loop if: + // - return value of the promise was `false` + // - handlers are not exhausted yet const handlers = this._parseStack.handlers; let handlerPos = this._parseStack.handlerPos - 1; switch (this._parseStack.state) { @@ -804,10 +794,8 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP const handlers = this._csiHandlers[this._collect << 8 | code]; let j = handlers ? handlers.length - 1 : -1; for (; j >= 0; j--) { - /* - * true means success and to stop bubbling - * a promise indicates an async handler that needs to finish before progressing - */ + // true means success and to stop bubbling + // a promise indicates an async handler that needs to finish before progressing handlerResult = handlers[j](this._params); if (handlerResult === true) { break; @@ -845,10 +833,8 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP const handlersEsc = this._escHandlers[this._collect << 8 | code]; let jj = handlersEsc ? handlersEsc.length - 1 : -1; for (; jj >= 0; jj--) { - /* - * true means success and to stop bubbling - * a promise indicates an async handler that needs to finish before progressing - */ + // true means success and to stop bubbling + // a promise indicates an async handler that needs to finish before progressing handlerResult = handlersEsc[jj](); if (handlerResult === true) { break; @@ -870,10 +856,8 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP this._dcsParser.hook(this._collect << 8 | code, this._params); break; case ParserAction.DCS_PUT: - /* - * inner loop - exit DCS_PUT: 0x18, 0x1a, 0x1b, 0x7f, 0x80 - 0x9f - * unhook triggered by: 0x1b, 0x9c (success) and 0x18, 0x1a (abort) - */ + // inner loop - exit DCS_PUT: 0x18, 0x1a, 0x1b, 0x7f, 0x80 - 0x9f + // unhook triggered by: 0x1b, 0x9c (success) and 0x18, 0x1a (abort) for (let j = i + 1; ; ++j) { if (j >= length || (code = data[j]) === 0x18 || code === 0x1a || code === 0x1b || (code > 0x7f && code < NON_ASCII_PRINTABLE)) { this._dcsParser.put(data, i, j); @@ -921,10 +905,8 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP this._apcParser.start(this._collect << 8 | code); break; case ParserAction.APC_PUT: - /* - * inner loop - exit APC_PUT: 0x18, 0x1a, 0x1b, 0x9c - * allowed: 00/08 .. 00/13, 02/00 .. 07/14 + NON_ASCII_PRINTABLE - */ + // inner loop - exit APC_PUT: 0x18, 0x1a, 0x1b, 0x9c + // allowed: 00/08 .. 00/13, 02/00 .. 07/14 + NON_ASCII_PRINTABLE for (let j = i + 1; ; ++j) { if (j < length && ( (data[j] >= 0x20 && data[j] < 0x7f) || (data[j] >= 0x08 && data[j] < 0x0e) || data[j] >= NON_ASCII_PRINTABLE diff --git a/src/common/parser/OscParser.ts b/src/common/parser/OscParser.ts index d348f483b..1f0c8cb23 100644 --- a/src/common/parser/OscParser.ts +++ b/src/common/parser/OscParser.ts @@ -133,11 +133,9 @@ export class OscParser implements IOscParser { } // do nothing if command was faulty if (this._state !== OscState.ABORT) { - /* - * if we are still in ID state and get an early end - * means that the command has no payload thus we still have - * to announce START and send END right after - */ + // if we are still in ID state and get an early end + // means that the command has no payload thus we still have + // to announce START and send END right after if (this._state === OscState.ID) { this._start(); } @@ -168,11 +166,9 @@ export class OscParser implements IOscParser { } j--; } - /* - * cleanup left over handlers - * we always have to call .end for proper cleanup, - * here we use `success` to indicate whether a handler should execute - */ + // cleanup left over handlers + // we always have to call .end for proper cleanup, + // here we use `success` to indicate whether a handler should execute for (; j >= 0; j--) { handlerResult = this._active[j].end(false); if (handlerResult instanceof Promise) { @@ -226,10 +222,8 @@ export class OscHandler implements IOscHandler { } else if (success) { ret = this._handler(this._data); if (ret instanceof Promise) { - /* - * need to hold data until `ret` got resolved - * dont care for errors, data will be freed anyway on next start - */ + // need to hold data until `ret` got resolved + // dont care for errors, data will be freed anyway on next start return ret.then(res => { this._data = ''; this._hitLimit = false; diff --git a/src/common/services/BufferService.ts b/src/common/services/BufferService.ts index ecf71eaf8..06ac6b10a 100644 --- a/src/common/services/BufferService.ts +++ b/src/common/services/BufferService.ts @@ -103,28 +103,22 @@ export class BufferService extends Disposable implements IBufferService { buffer.ydisp++; } } else { - /* - * When the buffer is full and the user has scrolled up, keep the text - * stable unless ydisp is right at the top - */ + // When the buffer is full and the user has scrolled up, keep the text + // stable unless ydisp is right at the top if (this.isUserScrolling) { buffer.ydisp = Math.max(buffer.ydisp - 1, 0); } } } else { - /* - * scrollTop is non-zero which means no line will be going to the - * scrollback, instead we can just shift them in-place. - */ + // scrollTop is non-zero which means no line will be going to the + // scrollback, instead we can just shift them in-place. const scrollRegionHeight = bottomRow - topRow + 1 /* as it's zero-based */; buffer.lines.shiftElements(topRow + 1, scrollRegionHeight - 1, -1); buffer.lines.set(bottomRow, newLine.clone()); } - /* - * Move the viewport to the bottom of the buffer unless the user is - * scrolling. - */ + // Move the viewport to the bottom of the buffer unless the user is + // scrolling. if (!this.isUserScrolling) { buffer.ydisp = buffer.ybase; } diff --git a/src/common/services/InstantiationService.ts b/src/common/services/InstantiationService.ts index f11ff7f6e..7e7695485 100644 --- a/src/common/services/InstantiationService.ts +++ b/src/common/services/InstantiationService.ts @@ -4,12 +4,10 @@ * * This was heavily inspired from microsoft/vscode's dependency injection system (MIT). */ -/* - *--------------------------------------------------------------------------------------------- +/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. - *-------------------------------------------------------------------------------------------- - */ + *--------------------------------------------------------------------------------------------*/ import { IInstantiationService, IServiceIdentifier } from 'common/services/Services'; import { getServiceDependencies } from 'common/services/ServiceRegistry'; diff --git a/src/common/services/LogService.ts b/src/common/services/LogService.ts index eea09f47d..8bc1e0aaa 100644 --- a/src/common/services/LogService.ts +++ b/src/common/services/LogService.ts @@ -16,10 +16,8 @@ interface IConsole { warn: LogType; } -/* - * console is available on both node.js and browser contexts but the common - * module doesn't depend on them so we need to explicitly declare it. - */ +// console is available on both node.js and browser contexts but the common +// module doesn't depend on them so we need to explicitly declare it. declare const console: IConsole; const optionsKeyToLogLevel: { [key: string]: LogLevelEnum } = { diff --git a/src/common/services/MouseStateService.ts b/src/common/services/MouseStateService.ts index 6316dbfc9..21420460a 100644 --- a/src/common/services/MouseStateService.ts +++ b/src/common/services/MouseStateService.ts @@ -105,10 +105,8 @@ function eventCode(e: ICoreMouseEvent, isSGR: boolean): number { if (e.action === CoreMouseAction.MOVE) { code |= CoreMouseAction.MOVE; } else if (e.action === CoreMouseAction.UP && !isSGR) { - /* - * special case - only SGR can report button on release - * all others have to go with NONE - */ + // special case - only SGR can report button on release + // all others have to go with NONE code |= CoreMouseButton.NONE; } } @@ -128,12 +126,10 @@ const DEFAULT_ENCODINGS: { [key: string]: CoreMouseEncoding } = { */ DEFAULT: (e: ICoreMouseEvent) => { const params = [eventCode(e, false) + 32, e.col + 32, e.row + 32]; - /* - * supress mouse report if we exceed addressible range - * Note this is handled differently by emulators - * - xterm: sends 0;0 coords instead - * - vte, konsole: no report - */ + // supress mouse report if we exceed addressible range + // Note this is handled differently by emulators + // - xterm: sends 0;0 coords instead + // - vte, konsole: no report if (params[0] > 255 || params[1] > 255 || params[2] > 255) { return ''; } diff --git a/src/common/services/OptionsService.ts b/src/common/services/OptionsService.ts index 252b38ef9..ec647aaa5 100644 --- a/src/common/services/OptionsService.ts +++ b/src/common/services/OptionsService.ts @@ -90,10 +90,8 @@ export class OptionsService extends Disposable implements IOptionsService { this.options = { ... defaultOptions }; this._setupOptions(); - /* - * Clear out options that could link outside xterm.js as they could easily cause an embedder - * memory leak - */ + // Clear out options that could link outside xterm.js as they could easily cause an embedder + // memory leak this._register(toDisposable(() => { this.rawOptions.linkHandler = null; this.rawOptions.documentOverride = null; diff --git a/src/common/services/ServiceRegistry.ts b/src/common/services/ServiceRegistry.ts index fef07d54c..af51d1641 100644 --- a/src/common/services/ServiceRegistry.ts +++ b/src/common/services/ServiceRegistry.ts @@ -4,12 +4,10 @@ * * This was heavily inspired from microsoft/vscode's dependency injection system (MIT). */ -/* - *--------------------------------------------------------------------------------------------- +/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. - *-------------------------------------------------------------------------------------------- - */ + *--------------------------------------------------------------------------------------------*/ import { IServiceIdentifier } from 'common/services/Services'; diff --git a/src/common/services/UnicodeService.ts b/src/common/services/UnicodeService.ts index a04bf00a1..d0dd8584d 100644 --- a/src/common/services/UnicodeService.ts +++ b/src/common/services/UnicodeService.ts @@ -78,20 +78,16 @@ export class UnicodeService implements IUnicodeService { // surrogate pair first if (0xD800 <= code && code <= 0xDBFF) { if (++i >= length) { - /* - * this should not happen with strings retrieved from - * Buffer.translateToString as it converts from UTF-32 - * and therefore always should contain the second part - * for any other string we still have to handle it somehow: - * simply treat the lonely surrogate first as a single char (UCS-2 behavior) - */ + // this should not happen with strings retrieved from + // Buffer.translateToString as it converts from UTF-32 + // and therefore always should contain the second part + // for any other string we still have to handle it somehow: + // simply treat the lonely surrogate first as a single char (UCS-2 behavior) return result + this.wcwidth(code); } const second = s.charCodeAt(i); - /* - * convert surrogate pair to high codepoint only for valid second part (UTF-16) - * otherwise treat them independently (UCS-2 behavior) - */ + // convert surrogate pair to high codepoint only for valid second part (UTF-16) + // otherwise treat them independently (UCS-2 behavior) if (0xDC00 <= second && second <= 0xDFFF) { code = (code - 0xD800) * 0x400 + second - 0xDC00 + 0x10000; } else { diff --git a/src/headless/public/Terminal.ts b/src/headless/public/Terminal.ts index d9d604917..0d4e6e93b 100644 --- a/src/headless/public/Terminal.ts +++ b/src/headless/public/Terminal.ts @@ -58,11 +58,9 @@ export class Terminal extends Disposable implements ITerminalApi { } private _checkReadonlyOptions(propName: string): void { - /* - * Throw an error if any constructor only option is modified - * from terminal.options - * Modifications from anywhere else are allowed - */ + // Throw an error if any constructor only option is modified + // from terminal.options + // Modifications from anywhere else are allowed if (CONSTRUCTOR_ONLY_OPTIONS.includes(propName)) { throw new Error(`Option "${propName}" can only be set in the constructor`); } diff --git a/test/playwright/InputHandler.test.ts b/test/playwright/InputHandler.test.ts index 451346f3a..eec2938a8 100644 --- a/test/playwright/InputHandler.test.ts +++ b/test/playwright/InputHandler.test.ts @@ -227,11 +227,9 @@ test.describe('InputHandler Integration Tests', () => { await ctx.proxy.write('\x1b[2S'); await pollFor(ctx.page, () => getLinesAsArray(5), ['3', '4', '5', '', '']); }); - /* - * This is intentionally not implemented here are image support lives within an addon - * test.skip('CSI ? Pi ; Pa ; Pv S - XTSMGRAPHICS: Set or request graphics attribute, xterm', async () => { - * }); - */ + // This is intentionally not implemented here are image support lives within an addon + // test.skip('CSI ? Pi ; Pa ; Pv S - XTSMGRAPHICS: Set or request graphics attribute, xterm', async () => { + // }); test('CSI Ps T - SD: Scroll down Ps lines (default = 1), VT420', async () => { await ctx.proxy.write('1\r\n2\r\n3\r\n4\r\n5'); await pollFor(ctx.page, () => getLinesAsArray(5), ['1', '2', '3', '4', '5']); @@ -351,10 +349,8 @@ test.describe('InputHandler Integration Tests', () => { await pollFor(ctx.page, () => getLinesAsArray(3), ['aoo', '', ' b']); }); test('CSI Ps g - TBC: Tab Clear (default = 0)', async () => { - /* - * Default: Clear tab stop at cursor position - * Move to column 9 (first tab stop), clear it, go back to column 1, tab should skip to column 17 - */ + // Default: Clear tab stop at cursor position + // Move to column 9 (first tab stop), clear it, go back to column 1, tab should skip to column 17 await ctx.proxy.write('\x1b[9G\x1b[g\x1b[1G\ta'); await pollFor(ctx.page, () => getLinesAsArray(1), [' a']); // Ps=3: Clear all tab stops @@ -527,10 +523,8 @@ test.describe('InputHandler Integration Tests', () => { test('Ps = 6 9 - Enable left and right margin mode (DECLRMM), VT420 and up', async () => { await assertNoModeChange('\x1b[?69h'); }); - /* - * test.skip('Ps = 8 0 - Enable Sixel Display Mode (DECSDM), VT330, VT340, VT382', async () => { - * }); - */ + // test.skip('Ps = 8 0 - Enable Sixel Display Mode (DECSDM), VT330, VT340, VT382', async () => { + // }); test('Ps = 9 5 - Do not clear screen when DECCOLM is set/reset (DECNCSM), VT510 and up', async () => { await assertNoModeChange('\x1b[?95h'); }); @@ -887,11 +881,9 @@ test.describe('InputHandler Integration Tests', () => { test('Ps = 6 9 - Disable left and right margin mode (DECLRMM), VT420 and up.', async () => { await assertNoModeChange('\x1b[?69l'); }); - /* - * This is intentionally not implemented here are image support lives within an addon - * test.skip('Ps = 8 0 - Disable Sixel Display Mode (DECSDM), VT330, VT340, VT382. Turns on "Sixel Scrolling". See the section Sixel Graphics and mode 8 4 5 2 .', async () => { - * }); - */ + // This is intentionally not implemented here are image support lives within an addon + // test.skip('Ps = 8 0 - Disable Sixel Display Mode (DECSDM), VT330, VT340, VT382. Turns on "Sixel Scrolling". See the section Sixel Graphics and mode 8 4 5 2 .', async () => { + // }); test('Ps = 9 5 - Clear screen when DECCOLM is set/reset (DECNCSM), VT510 and up.', async () => { await assertNoModeChange('\x1b[?95l'); }); diff --git a/test/playwright/MouseTracking.test.ts b/test/playwright/MouseTracking.test.ts index fc3a112e5..355bac3ba 100644 --- a/test/playwright/MouseTracking.test.ts +++ b/test/playwright/MouseTracking.test.ts @@ -12,17 +12,13 @@ test.beforeAll(async ({ browser }) => { }); test.afterAll(async () => await ctx.page.close()); -/* - * adjusted to work inside devcontainer - * see https://github.com/xtermjs/xterm.js/issues/2379 - */ +// adjusted to work inside devcontainer +// see https://github.com/xtermjs/xterm.js/issues/2379 const width = 1280; const height = 960; -/* - * adjust terminal row/col size so we can test - * >80 up to 223 and >255 - */ +// adjust terminal row/col size so we can test +// >80 up to 223 and >255 const fontSize = 6; const cols = 260; const rows = 50; @@ -44,11 +40,9 @@ async function getReports(encoding: string): Promise { return reports.map((report: number[]) => parseReport(encoding, report)); } -/* - * translate cell positions into pixel offset - * always adds +2 in each direction so we dont end up in the wrong cell - * due to rounding issues - */ +// translate cell positions into pixel offset +// always adds +2 in each direction so we dont end up in the wrong cell +// due to rounding issues async function cellPos(col: number, row: number): Promise { const coords: any = await ctx.page.evaluate(` (function() { @@ -171,12 +165,10 @@ function parseReport(encoding: string, msg: number[]): { state: any, row: number test.describe('Mouse Tracking Tests', () => { test.beforeAll(async () => { await ctx.page.setViewportSize({ width, height }); - /* - * patch terminal to get the onData calls - * we encode the msg here to an array of codes to not lose bytes - * (transmission strips non utf8 bytes) - * also resize so we can properly test the edge cases - */ + // patch terminal to get the onData calls + // we encode the msg here to an array of codes to not lose bytes + // (transmission strips non utf8 bytes) + // also resize so we can properly test the edge cases await ctx.page.evaluate(` window.term.onData(e => window.calls.push( Array.from(e).map(el => el.charCodeAt(0)) )); window.term.onBinary(e => window.calls.push( Array.from(e).map(el => el.charCodeAt(0)) )); @@ -222,10 +214,8 @@ test.describe('Mouse Tracking Tests', () => { await mouseUp('left'); await pollFor(ctx.page, () => getReports(encoding), [{ col: 51, row: 11, state: { action: 'press', button: 'left', modifier: { control: false, shift: false, meta: false } } }]); - /* - * test at max rows/cols - * capped at 223 (1-based) - */ + // test at max rows/cols + // capped at 223 (1-based) await mouseMove(223 - 1, rows - 1); await mouseDown('left'); await mouseUp('left'); @@ -237,28 +227,24 @@ test.describe('Mouse Tracking Tests', () => { await mouseUp('left'); await pollFor(ctx.page, () => getReports(encoding), []); - /* - * button press/move/release tests - * left button - */ + // button press/move/release tests + // left button await mouseMove(43, 24); await getReports(encoding); // clear reports await mouseDown('left'); await mouseMove(44, 24); await mouseUp('left'); await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'press', button: 'left', modifier: { control: false, shift: false, meta: false } } }]); - /* - * middle button - * bug: default action not cancelled (adds data to getReports from clipboard under X11) - * await mouseMove(43, 24); - * await getReports(encoding); // clear reports - * await mouseDown('middle'); - * await mouseMove(44, 24); - * await mouseUp('middle'); - * await pollFor(ctx.page, () => getReports(encoding), [{col: 44, row: 25, state: {action: 'press', button: 'middle', modifier: {control: false, shift: false, meta: false}}}]); - * right button - * bug: default action not cancelled (popup shown) - */ + // middle button + // bug: default action not cancelled (adds data to getReports from clipboard under X11) + // await mouseMove(43, 24); + // await getReports(encoding); // clear reports + // await mouseDown('middle'); + // await mouseMove(44, 24); + // await mouseUp('middle'); + // await pollFor(ctx.page, () => getReports(encoding), [{col: 44, row: 25, state: {action: 'press', button: 'middle', modifier: {control: false, shift: false, meta: false}}}]); + // right button + // bug: default action not cancelled (popup shown) await mouseMove(43, 24); await getReports(encoding); // clear reports await mouseDown('right'); @@ -266,20 +252,16 @@ test.describe('Mouse Tracking Tests', () => { await mouseUp('right'); await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'press', button: 'right', modifier: { control: false, shift: false, meta: false } } }]); - /* - * wheel - * await mouseMove(43, 24); - * await getReports(encoding); // clear reports - * await wheelUp(); - * await pollFor(ctx.page, () => getReports(encoding), []); - * await wheelDown(); - * await pollFor(ctx.page, () => getReports(encoding), []); - */ + // wheel + // await mouseMove(43, 24); + // await getReports(encoding); // clear reports + // await wheelUp(); + // await pollFor(ctx.page, () => getReports(encoding), []); + // await wheelDown(); + // await pollFor(ctx.page, () => getReports(encoding), []); - /* - * modifiers - * CTRL - */ + // modifiers + // CTRL await mouseMove(43, 24); await getReports(encoding); // clear reports await ctx.page.keyboard.down('Control'); @@ -302,12 +284,10 @@ test.describe('Mouse Tracking Tests', () => { await ctx.page.keyboard.up('Alt'); await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'press', button: 'left', modifier: { control: false, shift: false, meta: false } } }]); - /* - * SHIFT - * note: caught by selection manager - * bug? Why not caught by selection manger on macos? - * bug: no modifier reported - */ + // SHIFT + // note: caught by selection manager + // bug? Why not caught by selection manger on macos? + // bug: no modifier reported await mouseMove(43, 24); await getReports(encoding); // clear reports await ctx.page.keyboard.down('Shift'); // defaults to ShiftLeft @@ -324,10 +304,8 @@ test.describe('Mouse Tracking Tests', () => { ]); } - /* - * all modifiers - * bug: Shift not working - reporting totally wrong coords and modifiers - selection manager again? - */ + // all modifiers + // bug: Shift not working - reporting totally wrong coords and modifiers - selection manager again? await mouseMove(43, 24); await getReports(encoding); // clear reports await ctx.page.keyboard.down('Control'); @@ -373,28 +351,24 @@ test.describe('Mouse Tracking Tests', () => { await mouseUp('left'); await pollFor(ctx.page, () => getReports(encoding), [{ col: cols, row: rows, state: { action: 'press', button: 'left', modifier: { control: false, shift: false, meta: false } } }]); - /* - * button press/move/release tests - * left button - */ + // button press/move/release tests + // left button await mouseMove(43, 24); await getReports(encoding); // clear reports await mouseDown('left'); await mouseMove(44, 24); await mouseUp('left'); await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'press', button: 'left', modifier: { control: false, shift: false, meta: false } } }]); - /* - * middle button - * bug: default action not cancelled (adds data to getReports from clipboard under X11) - * await mouseMove(43, 24); - * await getReports(encoding); // clear reports - * await mouseDown('middle'); - * await mouseMove(44, 24); - * await mouseUp('middle'); - * await pollFor(ctx.page, () => getReports(encoding), [{col: 44, row: 25, state: {action: 'press', button: 'middle', modifier: {control: false, shift: false, meta: false}}}]); - * right button - * bug: default action not cancelled (popup shown) - */ + // middle button + // bug: default action not cancelled (adds data to getReports from clipboard under X11) + // await mouseMove(43, 24); + // await getReports(encoding); // clear reports + // await mouseDown('middle'); + // await mouseMove(44, 24); + // await mouseUp('middle'); + // await pollFor(ctx.page, () => getReports(encoding), [{col: 44, row: 25, state: {action: 'press', button: 'middle', modifier: {control: false, shift: false, meta: false}}}]); + // right button + // bug: default action not cancelled (popup shown) await mouseMove(43, 24); await getReports(encoding); // clear reports await mouseDown('right'); @@ -402,20 +376,16 @@ test.describe('Mouse Tracking Tests', () => { await mouseUp('right'); await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'press', button: 'right', modifier: { control: false, shift: false, meta: false } } }]); - /* - * wheel - * await mouseMove(43, 24); - * await getReports(encoding); // clear reports - * await wheelUp(); - * await pollFor(ctx.page, () => getReports(encoding), []); - * await wheelDown(); - * await pollFor(ctx.page, () => getReports(encoding), []); - */ + // wheel + // await mouseMove(43, 24); + // await getReports(encoding); // clear reports + // await wheelUp(); + // await pollFor(ctx.page, () => getReports(encoding), []); + // await wheelDown(); + // await pollFor(ctx.page, () => getReports(encoding), []); - /* - * modifiers - * CTRL - */ + // modifiers + // CTRL await mouseMove(43, 24); await getReports(encoding); // clear reports await ctx.page.keyboard.down('Control'); @@ -438,10 +408,8 @@ test.describe('Mouse Tracking Tests', () => { await ctx.page.keyboard.up('Alt'); await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'press', button: 'left', modifier: { control: false, shift: false, meta: false } } }]); - /* - * SHIFT - * note: caught by selection manager - */ + // SHIFT + // note: caught by selection manager await mouseMove(43, 24); await getReports(encoding); // clear reports await ctx.page.keyboard.down('Shift'); // defaults to ShiftLeft @@ -458,10 +426,8 @@ test.describe('Mouse Tracking Tests', () => { ]); } - /* - * all modifiers - * bug: Shift not working - reporting totally wrong coords and modifiers - selection manager again? - */ + // all modifiers + // bug: Shift not working - reporting totally wrong coords and modifiers - selection manager again? await mouseMove(43, 24); await getReports(encoding); // clear reports await ctx.page.keyboard.down('Control'); @@ -517,10 +483,8 @@ test.describe('Mouse Tracking Tests', () => { { col: 51, row: 11, state: { action: 'release', button: '', modifier: { control: false, shift: false, meta: false } } } ]); - /* - * test at max rows/cols - * capped at 223 (1-based) - */ + // test at max rows/cols + // capped at 223 (1-based) await mouseMove(223 - 1, rows - 1); await mouseDown('left'); await mouseUp('left'); @@ -529,10 +493,8 @@ test.describe('Mouse Tracking Tests', () => { { col: 223, row: rows, state: { action: 'release', button: '', modifier: { control: false, shift: false, meta: false } } } ]); - /* - * button press/move/release tests - * left button - */ + // button press/move/release tests + // left button await mouseMove(43, 24); await getReports(encoding); // clear reports await mouseDown('left'); @@ -542,18 +504,16 @@ test.describe('Mouse Tracking Tests', () => { { col: 44, row: 25, state: { action: 'press', button: 'left', modifier: { control: false, shift: false, meta: false } } }, { col: 45, row: 25, state: { action: 'release', button: '', modifier: { control: false, shift: false, meta: false } } } ]); - /* - * middle button - * bug: default action not cancelled (adds data to getReports from clipboard under X11) - * await mouseMove(43, 24); - * await getReports(encoding); // clear reports - * await mouseDown('middle'); - * await mouseMove(44, 24); - * await mouseUp('middle'); - * await pollFor(ctx.page, () => getReports(encoding), [{col: 44, row: 25, state: {action: 'press', button: 'middle', modifier: {control: false, shift: false, meta: false}}}]); - * right button - * bug: default action not cancelled (popup shown) - */ + // middle button + // bug: default action not cancelled (adds data to getReports from clipboard under X11) + // await mouseMove(43, 24); + // await getReports(encoding); // clear reports + // await mouseDown('middle'); + // await mouseMove(44, 24); + // await mouseUp('middle'); + // await pollFor(ctx.page, () => getReports(encoding), [{col: 44, row: 25, state: {action: 'press', button: 'middle', modifier: {control: false, shift: false, meta: false}}}]); + // right button + // bug: default action not cancelled (popup shown) await mouseMove(43, 24); await getReports(encoding); // clear reports await mouseDown('right'); @@ -564,20 +524,16 @@ test.describe('Mouse Tracking Tests', () => { { col: 45, row: 25, state: { action: 'release', button: '', modifier: { control: false, shift: false, meta: false } } } ]); - /* - * wheel - * await mouseMove(43, 24); - * await getReports(encoding); // clear reports - * await wheelUp(); - * await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'up', button: 'wheel', modifier: { control: false, shift: false, meta: false } } }]); - * await wheelDown(); - * await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: false, meta: false } } }]); - */ + // wheel + // await mouseMove(43, 24); + // await getReports(encoding); // clear reports + // await wheelUp(); + // await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'up', button: 'wheel', modifier: { control: false, shift: false, meta: false } } }]); + // await wheelDown(); + // await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: false, meta: false } } }]); - /* - * modifiers - * CTRL - */ + // modifiers + // CTRL await mouseMove(43, 24); await getReports(encoding); // clear reports await ctx.page.keyboard.down('Control'); @@ -607,35 +563,31 @@ test.describe('Mouse Tracking Tests', () => { // { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: false, meta: true } } } ]); - /* - * SHIFT - * note: press/release caught by selection manager - * bug: modifier not reported for passed events - * await mouseMove(43, 24); - * await getReports(encoding); // clear reports - * await ctx.page.keyboard.down('Shift'); // defaults to ShiftLeft - * await mouseDown('left'); - * await mouseMove(44, 24); - * await mouseUp('left'); - * // await wheelDown(); - * await ctx.page.keyboard.up('Shift'); - * // if (noShift) { - * // await pollFor(ctx.page, () => getReports(encoding), [ - * // // { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: true, meta: false } } } - * // ]); - * // } else { - * await pollFor(ctx.page, () => getReports(encoding), [ - * { col: 44, row: 25, state: { action: 'press', button: 'left', modifier: { control: false, shift: true, meta: false } } }, - * { col: 45, row: 25, state: { action: 'release', button: '', modifier: { control: false, shift: true, meta: false } } }, - * // { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: true, meta: false } } } - * ]); - * // } - */ + // SHIFT + // note: press/release caught by selection manager + // bug: modifier not reported for passed events + // await mouseMove(43, 24); + // await getReports(encoding); // clear reports + // await ctx.page.keyboard.down('Shift'); // defaults to ShiftLeft + // await mouseDown('left'); + // await mouseMove(44, 24); + // await mouseUp('left'); + // // await wheelDown(); + // await ctx.page.keyboard.up('Shift'); + // // if (noShift) { + // // await pollFor(ctx.page, () => getReports(encoding), [ + // // // { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: true, meta: false } } } + // // ]); + // // } else { + // await pollFor(ctx.page, () => getReports(encoding), [ + // { col: 44, row: 25, state: { action: 'press', button: 'left', modifier: { control: false, shift: true, meta: false } } }, + // { col: 45, row: 25, state: { action: 'release', button: '', modifier: { control: false, shift: true, meta: false } } }, + // // { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: true, meta: false } } } + // ]); + // // } - /* - * all modifiers - * bug: Shift not working - selection manager? - */ + // all modifiers + // bug: Shift not working - selection manager? await mouseMove(43, 24); await getReports(encoding); // clear reports await ctx.page.keyboard.down('Control'); @@ -695,10 +647,8 @@ test.describe('Mouse Tracking Tests', () => { { col: cols, row: rows, state: { action: 'release', button: 'left', modifier: { control: false, shift: false, meta: false } } } ]); - /* - * button press/move/release tests - * left button - */ + // button press/move/release tests + // left button await mouseMove(43, 24); await getReports(encoding); // clear reports await mouseDown('left'); @@ -708,18 +658,16 @@ test.describe('Mouse Tracking Tests', () => { { col: 44, row: 25, state: { action: 'press', button: 'left', modifier: { control: false, shift: false, meta: false } } }, { col: 45, row: 25, state: { action: 'release', button: 'left', modifier: { control: false, shift: false, meta: false } } } ]); - /* - * middle button - * bug: default action not cancelled (adds data to getReports from clipboard under X11) - * await mouseMove(43, 24); - * await getReports(encoding); // clear reports - * await mouseDown('middle'); - * await mouseMove(44, 24); - * await mouseUp('middle'); - * await pollFor(ctx.page, () => getReports(encoding), [{col: 44, row: 25, state: {action: 'press', button: 'middle', modifier: {control: false, shift: false, meta: false}}}]); - * right button - * bug: default action not cancelled (popup shown) - */ + // middle button + // bug: default action not cancelled (adds data to getReports from clipboard under X11) + // await mouseMove(43, 24); + // await getReports(encoding); // clear reports + // await mouseDown('middle'); + // await mouseMove(44, 24); + // await mouseUp('middle'); + // await pollFor(ctx.page, () => getReports(encoding), [{col: 44, row: 25, state: {action: 'press', button: 'middle', modifier: {control: false, shift: false, meta: false}}}]); + // right button + // bug: default action not cancelled (popup shown) await mouseMove(43, 24); await getReports(encoding); // clear reports await mouseDown('right'); @@ -730,20 +678,16 @@ test.describe('Mouse Tracking Tests', () => { { col: 45, row: 25, state: { action: 'release', button: 'right', modifier: { control: false, shift: false, meta: false } } } ]); - /* - * wheel - * await mouseMove(43, 24); - * await getReports(encoding); // clear reports - * await wheelUp(); - * await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'up', button: 'wheel', modifier: { control: false, shift: false, meta: false } } }]); - * await wheelDown(); - * await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: false, meta: false } } }]); - */ + // wheel + // await mouseMove(43, 24); + // await getReports(encoding); // clear reports + // await wheelUp(); + // await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'up', button: 'wheel', modifier: { control: false, shift: false, meta: false } } }]); + // await wheelDown(); + // await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: false, meta: false } } }]); - /* - * modifiers - * CTRL - */ + // modifiers + // CTRL await mouseMove(43, 24); await getReports(encoding); // clear reports await ctx.page.keyboard.down('Control'); @@ -773,34 +717,30 @@ test.describe('Mouse Tracking Tests', () => { // { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: false, meta: true } } } ]); - /* - * SHIFT - * note: press/release caught by selection manager - * await mouseMove(43, 24); - * await getReports(encoding); // clear reports - * await ctx.page.keyboard.down('Shift'); // defaults to ShiftLeft - * await mouseDown('left'); - * await mouseMove(44, 24); - * await mouseUp('left'); - * await wheelDown(); - * await ctx.page.keyboard.up('Shift'); - * if (noShift) { - * await pollFor(ctx.page, () => getReports(encoding), [ - * { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: true, meta: false } } } - * ]); - * } else { - * await pollFor(ctx.page, () => getReports(encoding), [ - * { col: 44, row: 25, state: { action: 'press', button: 'left', modifier: { control: false, shift: true, meta: false } } }, - * { col: 45, row: 25, state: { action: 'release', button: 'left', modifier: { control: false, shift: true, meta: false } } }, - * { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: true, meta: false } } } - * ]); - * } - */ + // SHIFT + // note: press/release caught by selection manager + // await mouseMove(43, 24); + // await getReports(encoding); // clear reports + // await ctx.page.keyboard.down('Shift'); // defaults to ShiftLeft + // await mouseDown('left'); + // await mouseMove(44, 24); + // await mouseUp('left'); + // await wheelDown(); + // await ctx.page.keyboard.up('Shift'); + // if (noShift) { + // await pollFor(ctx.page, () => getReports(encoding), [ + // { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: true, meta: false } } } + // ]); + // } else { + // await pollFor(ctx.page, () => getReports(encoding), [ + // { col: 44, row: 25, state: { action: 'press', button: 'left', modifier: { control: false, shift: true, meta: false } } }, + // { col: 45, row: 25, state: { action: 'release', button: 'left', modifier: { control: false, shift: true, meta: false } } }, + // { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: true, meta: false } } } + // ]); + // } - /* - * all modifiers - * bug: Shift not working - selection manager? - */ + // all modifiers + // bug: Shift not working - selection manager? await mouseMove(43, 24); await getReports(encoding); // clear reports await ctx.page.keyboard.down('Control'); @@ -860,10 +800,8 @@ test.describe('Mouse Tracking Tests', () => { { col: 51, row: 11, state: { action: 'release', button: '', modifier: { control: false, shift: false, meta: false } } } ]); - /* - * test at max rows/cols - * capped at 223 (1-based) - */ + // test at max rows/cols + // capped at 223 (1-based) await mouseMove(223 - 1, rows - 1); await mouseDown('left'); await mouseUp('left'); @@ -872,10 +810,8 @@ test.describe('Mouse Tracking Tests', () => { { col: 223, row: rows, state: { action: 'release', button: '', modifier: { control: false, shift: false, meta: false } } } ]); - /* - * button press/move/release tests - * left button - */ + // button press/move/release tests + // left button await mouseMove(43, 24); await getReports(encoding); // clear reports await mouseDown('left'); @@ -886,18 +822,16 @@ test.describe('Mouse Tracking Tests', () => { { col: 45, row: 25, state: { action: 'move', button: 'left', modifier: { control: false, shift: false, meta: false } } }, { col: 45, row: 25, state: { action: 'release', button: '', modifier: { control: false, shift: false, meta: false } } } ]); - /* - * middle button - * bug: default action not cancelled (adds data to getReports from clipboard under X11) - * await mouseMove(43, 24); - * await getReports(encoding); // clear reports - * await mouseDown('middle'); - * await mouseMove(44, 24); - * await mouseUp('middle'); - * await pollFor(ctx.page, () => getReports(encoding), [{col: 44, row: 25, state: {action: 'press', button: 'middle', modifier: {control: false, shift: false, meta: false}}}]); - * right button - * bug: default action not cancelled (popup shown) - */ + // middle button + // bug: default action not cancelled (adds data to getReports from clipboard under X11) + // await mouseMove(43, 24); + // await getReports(encoding); // clear reports + // await mouseDown('middle'); + // await mouseMove(44, 24); + // await mouseUp('middle'); + // await pollFor(ctx.page, () => getReports(encoding), [{col: 44, row: 25, state: {action: 'press', button: 'middle', modifier: {control: false, shift: false, meta: false}}}]); + // right button + // bug: default action not cancelled (popup shown) await mouseMove(43, 24); await getReports(encoding); // clear reports await mouseDown('right'); @@ -909,20 +843,16 @@ test.describe('Mouse Tracking Tests', () => { { col: 45, row: 25, state: { action: 'release', button: '', modifier: { control: false, shift: false, meta: false } } } ]); - /* - * wheel - * await mouseMove(43, 24); - * await getReports(encoding); // clear reports - * await wheelUp(); - * await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'up', button: 'wheel', modifier: { control: false, shift: false, meta: false } } }]); - * await wheelDown(); - * await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: false, meta: false } } }]); - */ + // wheel + // await mouseMove(43, 24); + // await getReports(encoding); // clear reports + // await wheelUp(); + // await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'up', button: 'wheel', modifier: { control: false, shift: false, meta: false } } }]); + // await wheelDown(); + // await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: false, meta: false } } }]); - /* - * modifiers - * CTRL - */ + // modifiers + // CTRL await mouseMove(43, 24); await getReports(encoding); // clear reports await ctx.page.keyboard.down('Control'); @@ -954,35 +884,31 @@ test.describe('Mouse Tracking Tests', () => { // { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: false, meta: true } } } ]); - /* - * SHIFT - * note: press/release/drag caught by selection manager - * await mouseMove(43, 24); - * await getReports(encoding); // clear reports - * await ctx.page.keyboard.down('Shift'); // defaults to ShiftLeft - * await mouseDown('left'); - * await mouseMove(44, 24); - * await mouseUp('left'); - * await wheelDown(); - * await ctx.page.keyboard.up('Shift'); - * if (noShift) { - * await pollFor(ctx.page, () => getReports(encoding), [ - * { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: true, meta: false } } } - * ]); - * } else { - * await pollFor(ctx.page, () => getReports(encoding), [ - * { col: 44, row: 25, state: { action: 'press', button: 'left', modifier: { control: false, shift: true, meta: false } } }, - * { col: 45, row: 25, state: { action: 'move', button: 'left', modifier: { control: false, shift: true, meta: false } } }, - * { col: 45, row: 25, state: { action: 'release', button: '', modifier: { control: false, shift: true, meta: false } } }, - * { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: true, meta: false } } } - * ]); - * } - */ + // SHIFT + // note: press/release/drag caught by selection manager + // await mouseMove(43, 24); + // await getReports(encoding); // clear reports + // await ctx.page.keyboard.down('Shift'); // defaults to ShiftLeft + // await mouseDown('left'); + // await mouseMove(44, 24); + // await mouseUp('left'); + // await wheelDown(); + // await ctx.page.keyboard.up('Shift'); + // if (noShift) { + // await pollFor(ctx.page, () => getReports(encoding), [ + // { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: true, meta: false } } } + // ]); + // } else { + // await pollFor(ctx.page, () => getReports(encoding), [ + // { col: 44, row: 25, state: { action: 'press', button: 'left', modifier: { control: false, shift: true, meta: false } } }, + // { col: 45, row: 25, state: { action: 'move', button: 'left', modifier: { control: false, shift: true, meta: false } } }, + // { col: 45, row: 25, state: { action: 'release', button: '', modifier: { control: false, shift: true, meta: false } } }, + // { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: true, meta: false } } } + // ]); + // } - /* - * all modifiers - * bug: Shift not working - */ + // all modifiers + // bug: Shift not working await mouseMove(43, 24); await getReports(encoding); // clear reports await ctx.page.keyboard.down('Control'); @@ -1012,10 +938,8 @@ test.describe('Mouse Tracking Tests', () => { await mouseMove(0, 0); await ctx.proxy.write('\x1b[?1002h\x1b[?1006h'); - /* - * test at 0,0 - * bug: release is fired immediately - */ + // test at 0,0 + // bug: release is fired immediately await mouseDown('left'); await pollFor(ctx.page, () => getReports(encoding), [ { col: 1, row: 1, state: { action: 'press', button: 'left', modifier: { control: false, shift: false, meta: false } } } @@ -1046,10 +970,8 @@ test.describe('Mouse Tracking Tests', () => { { col: cols, row: rows, state: { action: 'release', button: 'left', modifier: { control: false, shift: false, meta: false } } } ]); - /* - * button press/move/release tests - * left button - */ + // button press/move/release tests + // left button await mouseMove(43, 24); await getReports(encoding); // clear reports await mouseDown('left'); @@ -1060,18 +982,16 @@ test.describe('Mouse Tracking Tests', () => { { col: 45, row: 25, state: { action: 'move', button: 'left', modifier: { control: false, shift: false, meta: false } } }, { col: 45, row: 25, state: { action: 'release', button: 'left', modifier: { control: false, shift: false, meta: false } } } ]); - /* - * middle button - * bug: default action not cancelled (adds data to getReports from clipboard under X11) - * await mouseMove(43, 24); - * await getReports(encoding); // clear reports - * await mouseDown('middle'); - * await mouseMove(44, 24); - * await mouseUp('middle'); - * await pollFor(ctx.page, () => getReports(encoding), [{col: 44, row: 25, state: {action: 'press', button: 'middle', modifier: {control: false, shift: false, meta: false}}}]); - * right button - * bug: default action not cancelled (popup shown) - */ + // middle button + // bug: default action not cancelled (adds data to getReports from clipboard under X11) + // await mouseMove(43, 24); + // await getReports(encoding); // clear reports + // await mouseDown('middle'); + // await mouseMove(44, 24); + // await mouseUp('middle'); + // await pollFor(ctx.page, () => getReports(encoding), [{col: 44, row: 25, state: {action: 'press', button: 'middle', modifier: {control: false, shift: false, meta: false}}}]); + // right button + // bug: default action not cancelled (popup shown) await mouseMove(43, 24); await getReports(encoding); // clear reports await mouseDown('right'); @@ -1083,20 +1003,16 @@ test.describe('Mouse Tracking Tests', () => { { col: 45, row: 25, state: { action: 'release', button: 'right', modifier: { control: false, shift: false, meta: false } } } ]); - /* - * wheel - * await mouseMove(43, 24); - * await getReports(encoding); // clear reports - * await wheelUp(); - * await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'up', button: 'wheel', modifier: { control: false, shift: false, meta: false } } }]); - * await wheelDown(); - * await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: false, meta: false } } }]); - */ + // wheel + // await mouseMove(43, 24); + // await getReports(encoding); // clear reports + // await wheelUp(); + // await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'up', button: 'wheel', modifier: { control: false, shift: false, meta: false } } }]); + // await wheelDown(); + // await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: false, meta: false } } }]); - /* - * modifiers - * CTRL - */ + // modifiers + // CTRL await mouseMove(43, 24); await getReports(encoding); // clear reports await ctx.page.keyboard.down('Control'); @@ -1128,35 +1044,31 @@ test.describe('Mouse Tracking Tests', () => { // { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: false, meta: true } } } ]); - /* - * SHIFT - * note: press/release/drag caught by selection manager - * await mouseMove(43, 24); - * await getReports(encoding); // clear reports - * await ctx.page.keyboard.down('Shift'); // defaults to ShiftLeft - * await mouseDown('left'); - * await mouseMove(44, 24); - * await mouseUp('left'); - * await wheelDown(); - * await ctx.page.keyboard.up('Shift'); - * if (noShift) { - * await pollFor(ctx.page, () => getReports(encoding), [ - * { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: true, meta: false } } } - * ]); - * } else { - * await pollFor(ctx.page, () => getReports(encoding), [ - * { col: 44, row: 25, state: { action: 'press', button: 'left', modifier: { control: false, shift: true, meta: false } } }, - * { col: 45, row: 25, state: { action: 'move', button: 'left', modifier: { control: false, shift: true, meta: false } } }, - * { col: 45, row: 25, state: { action: 'release', button: 'left', modifier: { control: false, shift: true, meta: false } } }, - * { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: true, meta: false } } } - * ]); - * } - */ + // SHIFT + // note: press/release/drag caught by selection manager + // await mouseMove(43, 24); + // await getReports(encoding); // clear reports + // await ctx.page.keyboard.down('Shift'); // defaults to ShiftLeft + // await mouseDown('left'); + // await mouseMove(44, 24); + // await mouseUp('left'); + // await wheelDown(); + // await ctx.page.keyboard.up('Shift'); + // if (noShift) { + // await pollFor(ctx.page, () => getReports(encoding), [ + // { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: true, meta: false } } } + // ]); + // } else { + // await pollFor(ctx.page, () => getReports(encoding), [ + // { col: 44, row: 25, state: { action: 'press', button: 'left', modifier: { control: false, shift: true, meta: false } } }, + // { col: 45, row: 25, state: { action: 'move', button: 'left', modifier: { control: false, shift: true, meta: false } } }, + // { col: 45, row: 25, state: { action: 'release', button: 'left', modifier: { control: false, shift: true, meta: false } } }, + // { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: true, meta: false } } } + // ]); + // } - /* - * all modifiers - * bug: this is totally broken with wrong coords and messed up modifiers - */ + // all modifiers + // bug: this is totally broken with wrong coords and messed up modifiers await mouseMove(43, 24); await getReports(encoding); // clear reports await ctx.page.keyboard.down('Control'); @@ -1216,10 +1128,8 @@ test.describe('Mouse Tracking Tests', () => { { col: 51, row: 11, state: { action: 'release', button: '', modifier: { control: false, shift: false, meta: false } } } ]); - /* - * test at max rows/cols - * capped at 223 (1-based) - */ + // test at max rows/cols + // capped at 223 (1-based) await mouseMove(223 - 1, rows - 1); await mouseDown('left'); await mouseUp('left'); @@ -1229,10 +1139,8 @@ test.describe('Mouse Tracking Tests', () => { { col: 223, row: rows, state: { action: 'release', button: '', modifier: { control: false, shift: false, meta: false } } } ]); - /* - * button press/move/release tests - * left button - */ + // button press/move/release tests + // left button await mouseMove(43, 24); await mouseDown('left'); await mouseMove(44, 24); @@ -1243,18 +1151,16 @@ test.describe('Mouse Tracking Tests', () => { { col: 45, row: 25, state: { action: 'move', button: 'left', modifier: { control: false, shift: false, meta: false } } }, { col: 45, row: 25, state: { action: 'release', button: '', modifier: { control: false, shift: false, meta: false } } } ]); - /* - * middle button - * bug: default action not cancelled (adds data to getReports from clipboard under X11) - * await mouseMove(43, 24); - * await getReports(encoding); // clear reports - * await mouseDown('middle'); - * await mouseMove(44, 24); - * await mouseUp('middle'); - * await pollFor(ctx.page, () => getReports(encoding), [{col: 44, row: 25, state: {action: 'press', button: 'middle', modifier: {control: false, shift: false, meta: false}}}]); - * right button - * bug: default action not cancelled (popup shown) - */ + // middle button + // bug: default action not cancelled (adds data to getReports from clipboard under X11) + // await mouseMove(43, 24); + // await getReports(encoding); // clear reports + // await mouseDown('middle'); + // await mouseMove(44, 24); + // await mouseUp('middle'); + // await pollFor(ctx.page, () => getReports(encoding), [{col: 44, row: 25, state: {action: 'press', button: 'middle', modifier: {control: false, shift: false, meta: false}}}]); + // right button + // bug: default action not cancelled (popup shown) await mouseMove(43, 24); await mouseDown('right'); await mouseMove(44, 24); @@ -1266,20 +1172,16 @@ test.describe('Mouse Tracking Tests', () => { { col: 45, row: 25, state: { action: 'release', button: '', modifier: { control: false, shift: false, meta: false } } } ]); - /* - * wheel - * await mouseMove(43, 24); - * await getReports(encoding); // clear reports - * await wheelUp(); - * await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'up', button: 'wheel', modifier: { control: false, shift: false, meta: false } } }]); - * await wheelDown(); - * await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: false, meta: false } } }]); - */ + // wheel + // await mouseMove(43, 24); + // await getReports(encoding); // clear reports + // await wheelUp(); + // await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'up', button: 'wheel', modifier: { control: false, shift: false, meta: false } } }]); + // await wheelDown(); + // await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: false, meta: false } } }]); - /* - * modifiers - * CTRL - */ + // modifiers + // CTRL await ctx.page.keyboard.down('Control'); await mouseMove(43, 24); await mouseDown('left'); @@ -1311,36 +1213,32 @@ test.describe('Mouse Tracking Tests', () => { // { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: false, meta: true } } } ]); - /* - * SHIFT - * note: press/release/drag caught by selection manager - * await ctx.page.keyboard.down('Shift'); - * await mouseMove(43, 24); - * await mouseDown('left'); - * await mouseMove(44, 24); - * await mouseUp('left'); - * await wheelDown(); - * await ctx.page.keyboard.up('Shift'); - * if (noShift) { - * await pollFor(ctx.page, () => getReports(encoding), [ - * { col: 44, row: 25, state: { action: 'move', button: '', modifier: { control: false, shift: true, meta: false } } }, - * { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: true, meta: false } } } - * ]); - * } else { - * await pollFor(ctx.page, () => getReports(encoding), [ - * { col: 44, row: 25, state: { action: 'move', button: '', modifier: { control: false, shift: true, meta: false } } }, - * { col: 44, row: 25, state: { action: 'press', button: 'left', modifier: { control: false, shift: true, meta: false } } }, - * { col: 45, row: 25, state: { action: 'move', button: 'left', modifier: { control: false, shift: true, meta: false } } }, - * { col: 45, row: 25, state: { action: 'release', button: '', modifier: { control: false, shift: true, meta: false } } }, - * { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: true, meta: false } } } - * ]); - * } - */ + // SHIFT + // note: press/release/drag caught by selection manager + // await ctx.page.keyboard.down('Shift'); + // await mouseMove(43, 24); + // await mouseDown('left'); + // await mouseMove(44, 24); + // await mouseUp('left'); + // await wheelDown(); + // await ctx.page.keyboard.up('Shift'); + // if (noShift) { + // await pollFor(ctx.page, () => getReports(encoding), [ + // { col: 44, row: 25, state: { action: 'move', button: '', modifier: { control: false, shift: true, meta: false } } }, + // { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: true, meta: false } } } + // ]); + // } else { + // await pollFor(ctx.page, () => getReports(encoding), [ + // { col: 44, row: 25, state: { action: 'move', button: '', modifier: { control: false, shift: true, meta: false } } }, + // { col: 44, row: 25, state: { action: 'press', button: 'left', modifier: { control: false, shift: true, meta: false } } }, + // { col: 45, row: 25, state: { action: 'move', button: 'left', modifier: { control: false, shift: true, meta: false } } }, + // { col: 45, row: 25, state: { action: 'release', button: '', modifier: { control: false, shift: true, meta: false } } }, + // { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: true, meta: false } } } + // ]); + // } - /* - * all modifiers - * bug: Shift not working - */ + // all modifiers + // bug: Shift not working await ctx.page.keyboard.down('Control'); await ctx.page.keyboard.down('Alt'); // await ctx.page.keyboard.down('Shift'); @@ -1394,11 +1292,9 @@ test.describe('Mouse Tracking Tests', () => { { col: 51, row: 11, state: { action: 'release', button: 'left', modifier: { control: false, shift: false, meta: false } } } ]); - /* - * test at max rows/cols - * bug: we are capped at col 95 currently - * fix: allow values up to 223, any bigger should drop to 0 - */ + // test at max rows/cols + // bug: we are capped at col 95 currently + // fix: allow values up to 223, any bigger should drop to 0 await mouseMove(cols - 1, rows - 1); await mouseDown('left'); await mouseUp('left'); @@ -1408,10 +1304,8 @@ test.describe('Mouse Tracking Tests', () => { { col: cols, row: rows, state: { action: 'release', button: 'left', modifier: { control: false, shift: false, meta: false } } } ]); - /* - * button press/move/release tests - * left button - */ + // button press/move/release tests + // left button await mouseMove(43, 24); await mouseDown('left'); await mouseMove(44, 24); @@ -1422,18 +1316,16 @@ test.describe('Mouse Tracking Tests', () => { { col: 45, row: 25, state: { action: 'move', button: 'left', modifier: { control: false, shift: false, meta: false } } }, { col: 45, row: 25, state: { action: 'release', button: 'left', modifier: { control: false, shift: false, meta: false } } } ]); - /* - * middle button - * bug: default action not cancelled (adds data to getReports from clipboard under X11) - * await mouseMove(43, 24); - * await getReports(encoding); // clear reports - * await mouseDown('middle'); - * await mouseMove(44, 24); - * await mouseUp('middle'); - * await pollFor(ctx.page, () => getReports(encoding), [{col: 44, row: 25, state: {action: 'press', button: 'middle', modifier: {control: false, shift: false, meta: false}}}]); - * right button - * bug: default action not cancelled (popup shown) - */ + // middle button + // bug: default action not cancelled (adds data to getReports from clipboard under X11) + // await mouseMove(43, 24); + // await getReports(encoding); // clear reports + // await mouseDown('middle'); + // await mouseMove(44, 24); + // await mouseUp('middle'); + // await pollFor(ctx.page, () => getReports(encoding), [{col: 44, row: 25, state: {action: 'press', button: 'middle', modifier: {control: false, shift: false, meta: false}}}]); + // right button + // bug: default action not cancelled (popup shown) await mouseMove(43, 24); await mouseDown('right'); await mouseMove(44, 24); @@ -1445,20 +1337,16 @@ test.describe('Mouse Tracking Tests', () => { { col: 45, row: 25, state: { action: 'release', button: 'right', modifier: { control: false, shift: false, meta: false } } } ]); - /* - * wheel - * await mouseMove(43, 24); - * await getReports(encoding); // clear reports - * await wheelUp(); - * await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'up', button: 'wheel', modifier: { control: false, shift: false, meta: false } } }]); - * await wheelDown(); - * await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: false, meta: false } } }]); - */ + // wheel + // await mouseMove(43, 24); + // await getReports(encoding); // clear reports + // await wheelUp(); + // await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'up', button: 'wheel', modifier: { control: false, shift: false, meta: false } } }]); + // await wheelDown(); + // await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: false, meta: false } } }]); - /* - * modifiers - * CTRL - */ + // modifiers + // CTRL await ctx.page.keyboard.down('Control'); await mouseMove(43, 24); await mouseDown('left'); @@ -1490,36 +1378,32 @@ test.describe('Mouse Tracking Tests', () => { // { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: false, meta: true } } } ]); - /* - * SHIFT - * note: press/release/drag caught by selection manager - * await ctx.page.keyboard.down('Shift'); - * await mouseMove(43, 24); - * await mouseDown('left'); - * await mouseMove(44, 24); - * await mouseUp('left'); - * await wheelDown(); - * await ctx.page.keyboard.up('Shift'); - * if (noShift) { - * await pollFor(ctx.page, () => getReports(encoding), [ - * { col: 44, row: 25, state: { action: 'move', button: '', modifier: { control: false, shift: true, meta: false } } }, - * { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: true, meta: false } } } - * ]); - * } else { - * await pollFor(ctx.page, () => getReports(encoding), [ - * { col: 44, row: 25, state: { action: 'move', button: '', modifier: { control: false, shift: true, meta: false } } }, - * { col: 44, row: 25, state: { action: 'press', button: 'left', modifier: { control: false, shift: true, meta: false } } }, - * { col: 45, row: 25, state: { action: 'move', button: 'left', modifier: { control: false, shift: true, meta: false } } }, - * { col: 45, row: 25, state: { action: 'release', button: 'left', modifier: { control: false, shift: true, meta: false } } }, - * { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: true, meta: false } } } - * ]); - * } - */ + // SHIFT + // note: press/release/drag caught by selection manager + // await ctx.page.keyboard.down('Shift'); + // await mouseMove(43, 24); + // await mouseDown('left'); + // await mouseMove(44, 24); + // await mouseUp('left'); + // await wheelDown(); + // await ctx.page.keyboard.up('Shift'); + // if (noShift) { + // await pollFor(ctx.page, () => getReports(encoding), [ + // { col: 44, row: 25, state: { action: 'move', button: '', modifier: { control: false, shift: true, meta: false } } }, + // { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: true, meta: false } } } + // ]); + // } else { + // await pollFor(ctx.page, () => getReports(encoding), [ + // { col: 44, row: 25, state: { action: 'move', button: '', modifier: { control: false, shift: true, meta: false } } }, + // { col: 44, row: 25, state: { action: 'press', button: 'left', modifier: { control: false, shift: true, meta: false } } }, + // { col: 45, row: 25, state: { action: 'move', button: 'left', modifier: { control: false, shift: true, meta: false } } }, + // { col: 45, row: 25, state: { action: 'release', button: 'left', modifier: { control: false, shift: true, meta: false } } }, + // { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: true, meta: false } } } + // ]); + // } - /* - * all modifiers - * bug: Shift not working - */ + // all modifiers + // bug: Shift not working await ctx.page.keyboard.down('Control'); await ctx.page.keyboard.down('Alt'); // await ctx.page.keyboard.down('Shift'); diff --git a/test/playwright/SharedRendererTests.ts b/test/playwright/SharedRendererTests.ts index 75b330abd..db5c4c073 100644 --- a/test/playwright/SharedRendererTests.ts +++ b/test/playwright/SharedRendererTests.ts @@ -378,10 +378,8 @@ export function injectSharedRendererTests(ctx: ISharedRendererTestContext): void const r = parseInt(cssColor.slice(1, 3), 16); const g = parseInt(cssColor.slice(3, 5), 16); const b = parseInt(cssColor.slice(5, 7), 16); - /* - * It's difficult to assert the exact color due to rounding, just ensure the color differs - * to the regular color - */ + // It's difficult to assert the exact color due to rounding, just ensure the color differs + // to the regular color await pollFor(ctx.value.page, async () => { const c = await getCellColor(ctx.value, x + 1, y + 1); return ( @@ -785,11 +783,9 @@ export function injectSharedRendererTests(ctx: ISharedRendererTestContext): void await pollFor(ctx.value.page, () => getCellColor(ctx.value, 6, 2), [0xad, 0x7f, 0xa8, 255]); await pollFor(ctx.value.page, () => getCellColor(ctx.value, 7, 2), [0x34, 0xe2, 0xe2, 255]); await pollFor(ctx.value.page, () => getCellColor(ctx.value, 8, 2), [0xee, 0xee, 0xec, 255]); - /* - * Setting and check for minimum contrast values, note that these are not - * exact to the contrast ratio, if the increase luminance algorithm - * changes then these will probably fail - */ + // Setting and check for minimum contrast values, note that these are not + // exact to the contrast ratio, if the increase luminance algorithm + // changes then these will probably fail await ctx.value.page.evaluate(`window.term.options.minimumContrastRatio = 10;`); frameDetails = undefined; await pollFor(ctx.value.page, () => getCellColor(ctx.value, 1, 1), [176, 180, 180, 255]); @@ -856,11 +852,9 @@ export function injectSharedRendererTests(ctx: ISharedRendererTestContext): void await pollFor(ctx.value.page, () => getCellColor(ctx.value, 6, 2), [0xad, 0x7f, 0xa8, 255]); await pollFor(ctx.value.page, () => getCellColor(ctx.value, 7, 2), [0x34, 0xe2, 0xe2, 255]); await pollFor(ctx.value.page, () => getCellColor(ctx.value, 8, 2), [0xee, 0xee, 0xec, 255]); - /* - * Setting and check for minimum contrast values, note that these are not - * exact to the contrast ratio, if the increase luminance algorithm - * changes then these will probably fail - */ + // Setting and check for minimum contrast values, note that these are not + // exact to the contrast ratio, if the increase luminance algorithm + // changes then these will probably fail await ctx.value.page.evaluate(`window.term.options.minimumContrastRatio = 10;`); frameDetails = undefined; await pollFor(ctx.value.page, () => getCellColor(ctx.value, 1, 1), [46, 52, 54, 255]); @@ -941,11 +935,9 @@ export function injectSharedRendererTests(ctx: ISharedRendererTestContext): void await pollForApproximate(ctx.value.page, marginOfError, () => getFgColor(6, 2), [Math.floor((255 + 0xad) / 2), Math.floor((255 + 0x7f) / 2), Math.floor((255 + 0xa8) / 2), 255]); await pollForApproximate(ctx.value.page, marginOfError, () => getFgColor(7, 2), [Math.floor((255 + 0x34) / 2), Math.floor((255 + 0xe2) / 2), Math.floor((255 + 0xe2) / 2), 255]); await pollForApproximate(ctx.value.page, marginOfError, () => getFgColor(8, 2), [Math.floor((255 + 0xee) / 2), Math.floor((255 + 0xee) / 2), Math.floor((255 + 0xec) / 2), 255]); - /* - * Setting and check for minimum contrast values, note that these are not - * exact to the contrast ratio, if the increase luminance algorithm - * changes then these will probably fail - */ + // Setting and check for minimum contrast values, note that these are not + // exact to the contrast ratio, if the increase luminance algorithm + // changes then these will probably fail await waitForRenderAfter(ctx.value, () => ctx.value.page.evaluate(`window.term.options.minimumContrastRatio = 10;`)); await pollForApproximate(ctx.value.page, marginOfError, () => getFgColor(1, 1), [150, 153, 154, 255]); await pollForApproximate(ctx.value.page, marginOfError, () => getFgColor(2, 1), [229, 127, 127, 255]); diff --git a/test/playwright/Terminal.test.ts b/test/playwright/Terminal.test.ts index 387197c6d..2d1133cce 100644 --- a/test/playwright/Terminal.test.ts +++ b/test/playwright/Terminal.test.ts @@ -837,10 +837,8 @@ test.describe('API Integration Tests', () => { test.describe('registerLinkProvider', () => { test('should fire provideLinks when hovering cells', async () => { await openTerminal(ctx); - /* - * Focus the terminal as the cursor will show and trigger a rerender, which can clear the - * active link - */ + // Focus the terminal as the cursor will show and trigger a rerender, which can clear the + // active link await ctx.proxy.focus(); await ctx.page.evaluate(` window.calls = []; @@ -861,10 +859,8 @@ test.describe('API Integration Tests', () => { test('should fire hover and leave events on the link', async () => { await openTerminal(ctx); - /* - * Focus the terminal as the cursor will show and trigger a rerender, which can clear the - * active link - */ + // Focus the terminal as the cursor will show and trigger a rerender, which can clear the + // active link await ctx.page.evaluate('window.term.focus()'); await ctx.proxy.write('foo bar baz'); // Wait for renderer to catch up as links are cleared on render @@ -901,10 +897,8 @@ test.describe('API Integration Tests', () => { test('should work fine when hover and leave callbacks are not provided', async () => { await openTerminal(ctx); - /* - * Focus the terminal as the cursor will show and trigger a rerender, which can clear the - * active link - */ + // Focus the terminal as the cursor will show and trigger a rerender, which can clear the + // active link await ctx.page.evaluate('window.term.focus()'); await ctx.proxy.write('foo bar baz'); // Wait for renderer to catch up as links are cleared on render @@ -946,10 +940,8 @@ test.describe('API Integration Tests', () => { test('should fire activate events when clicking the link', async () => { await openTerminal(ctx); - /* - * Focus the terminal as the cursor will show and trigger a rerender, which can clear the - * active link - */ + // Focus the terminal as the cursor will show and trigger a rerender, which can clear the + // active link await ctx.page.evaluate('window.term.focus()'); await ctx.proxy.write('a b c'); // Wait for renderer to catch up as links are cleared on render @@ -990,10 +982,8 @@ test.describe('API Integration Tests', () => { test('should work when multiple links are provided on the same line', async () => { await openTerminal(ctx); - /* - * Focus the terminal as the cursor will show and trigger a rerender, which can clear the - * active link - */ + // Focus the terminal as the cursor will show and trigger a rerender, which can clear the + // active link await ctx.page.evaluate('window.term.focus()'); await ctx.proxy.write('foo bar baz'); // Wait for renderer to catch up as links are cleared on render @@ -1041,10 +1031,8 @@ test.describe('API Integration Tests', () => { test('should dispose links when hovering away', async () => { await openTerminal(ctx); - /* - * Focus the terminal as the cursor will show and trigger a rerender, which can clear the - * active link - */ + // Focus the terminal as the cursor will show and trigger a rerender, which can clear the + // active link await ctx.page.evaluate('window.term.focus()'); await ctx.proxy.write('foo bar baz'); // Wait for renderer to catch up as links are cleared on render diff --git a/test/playwright/TestUtils.ts b/test/playwright/TestUtils.ts index 76a5eb200..8a76c8820 100644 --- a/test/playwright/TestUtils.ts +++ b/test/playwright/TestUtils.ts @@ -459,10 +459,8 @@ export async function openTerminal( } catch {} } `); - /* - * HACK: Tests may have side effects that could cause the terminal not to be removed. This - * assertion catches this case early. - */ + // HACK: Tests may have side effects that could cause the terminal not to be removed. This + // assertion catches this case early. strictEqual(await ctx.page.evaluate(`document.querySelector('#terminal-container').children.length`), 0, 'there must be no terminals on the page'); let script = ` @@ -503,10 +501,8 @@ export async function openTerminal( await ctx.page.evaluate(script); - /* - * HACK: This is a soft layer breaker that's temporarily included until unicode graphemes have - * more complete integration tests. See https://github.com/xtermjs/xterm.js/pull/4519#discussion_r1285234453 - */ + // HACK: This is a soft layer breaker that's temporarily included until unicode graphemes have + // more complete integration tests. See https://github.com/xtermjs/xterm.js/pull/4519#discussion_r1285234453 if (testOptions.loadUnicodeGraphemesAddon) { await ctx.page.evaluate(` window.unicode = new UnicodeGraphemesAddon(); From 0e823004e6199eb415709672204c3dbdd07e1db6 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 27 May 2026 03:59:42 +0000 Subject: [PATCH 13/47] Use single-line JSDoc comments per handler in Types.ts Document multi-variant escape handlers with separate /** ... */ lines instead of multiline blocks. Co-authored-by: Daniel Imms --- src/common/Types.ts | 44 ++++++++++++++++++++------------------------ 1 file changed, 20 insertions(+), 24 deletions(-) diff --git a/src/common/Types.ts b/src/common/Types.ts index 1981a910f..744446af2 100644 --- a/src/common/Types.ts +++ b/src/common/Types.ts @@ -528,10 +528,9 @@ export interface IInputHandler { /** CSI ' } */ insertColumns(params: IParams): boolean; /** CSI ' ~ */ deleteColumns(params: IParams): boolean; - /** - * OSC 0 - * OSC 2 - */ setTitle(data: string): boolean; + /** OSC 0 */ + /** OSC 2 */ + setTitle(data: string): boolean; /** OSC 4 */ setOrReportIndexedColor(data: string): boolean; /** OSC 10 */ setOrReportFgColor(data: string): boolean; /** OSC 11 */ setOrReportBgColor(data: string): boolean; @@ -544,30 +543,27 @@ export interface IInputHandler { /** ESC E */ nextLine(): boolean; /** ESC = */ keypadApplicationMode(): boolean; /** ESC > */ keypadNumericMode(): boolean; - /** - * ESC % G - * ESC % @ - */ selectDefaultCharset(): boolean; - /** - * ESC ( C - * ESC ) C - * ESC * C - * ESC + C - * ESC - C - * ESC . C - * ESC / C - */ selectCharset(collectAndFlag: string): boolean; + /** ESC % G */ + /** ESC % @ */ + selectDefaultCharset(): boolean; + /** ESC ( C */ + /** ESC ) C */ + /** ESC * C */ + /** ESC + C */ + /** ESC - C */ + /** ESC . C */ + /** ESC / C */ + selectCharset(collectAndFlag: string): boolean; /** ESC D */ index(): boolean; /** ESC H */ tabSet(): boolean; /** ESC M */ reverseIndex(): boolean; /** ESC c */ fullReset(): boolean; - /** - * ESC n - * ESC o - * ESC | - * ESC } - * ESC ~ - */ setgLevel(level: number): boolean; + /** ESC n */ + /** ESC o */ + /** ESC | */ + /** ESC } */ + /** ESC ~ */ + setgLevel(level: number): boolean; /** ESC # 8 */ screenAlignmentPattern(): boolean; } From 88cef9704935cae508307215fd6ba6141925df84 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 27 May 2026 04:01:00 +0000 Subject: [PATCH 14/47] Attach method signatures to last handler JSDoc line in Types.ts Co-authored-by: Daniel Imms --- src/common/Types.ts | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/common/Types.ts b/src/common/Types.ts index 744446af2..07aebbe8f 100644 --- a/src/common/Types.ts +++ b/src/common/Types.ts @@ -529,8 +529,7 @@ export interface IInputHandler { /** CSI ' ~ */ deleteColumns(params: IParams): boolean; /** OSC 0 */ - /** OSC 2 */ - setTitle(data: string): boolean; + /** OSC 2 */ setTitle(data: string): boolean; /** OSC 4 */ setOrReportIndexedColor(data: string): boolean; /** OSC 10 */ setOrReportFgColor(data: string): boolean; /** OSC 11 */ setOrReportBgColor(data: string): boolean; @@ -544,16 +543,14 @@ export interface IInputHandler { /** ESC = */ keypadApplicationMode(): boolean; /** ESC > */ keypadNumericMode(): boolean; /** ESC % G */ - /** ESC % @ */ - selectDefaultCharset(): boolean; + /** ESC % @ */ selectDefaultCharset(): boolean; /** ESC ( C */ /** ESC ) C */ /** ESC * C */ /** ESC + C */ /** ESC - C */ /** ESC . C */ - /** ESC / C */ - selectCharset(collectAndFlag: string): boolean; + /** ESC / C */ selectCharset(collectAndFlag: string): boolean; /** ESC D */ index(): boolean; /** ESC H */ tabSet(): boolean; /** ESC M */ reverseIndex(): boolean; @@ -562,8 +559,7 @@ export interface IInputHandler { /** ESC o */ /** ESC | */ /** ESC } */ - /** ESC ~ */ - setgLevel(level: number): boolean; + /** ESC ~ */ setgLevel(level: number): boolean; /** ESC # 8 */ screenAlignmentPattern(): boolean; } From c62e6dd765a9afd2214a1267006345aadee5a719 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 26 May 2026 21:39:27 -0700 Subject: [PATCH 15/47] Register BufferNamespaceApi onBufferActivate listener with disposables (#5906) Wrap the onBufferActivate subscription in this._register so it is disposed when BufferNamespaceApi is disposed, matching the pattern used elsewhere in the codebase. Co-authored-by: Cursor Agent Co-authored-by: Daniel Imms --- src/common/public/BufferNamespaceApi.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/public/BufferNamespaceApi.ts b/src/common/public/BufferNamespaceApi.ts index 425b1798c..b9744f943 100644 --- a/src/common/public/BufferNamespaceApi.ts +++ b/src/common/public/BufferNamespaceApi.ts @@ -20,7 +20,7 @@ export class BufferNamespaceApi extends Disposable implements IBufferNamespaceAp super(); this._normal = new BufferApiView(this._core.buffers.normal, 'normal'); this._alternate = new BufferApiView(this._core.buffers.alt, 'alternate'); - this._core.buffers.onBufferActivate(() => this._onBufferChange.fire(this.active)); + this._register(this._core.buffers.onBufferActivate(() => this._onBufferChange.fire(this.active))); } public get active(): IBufferApi { if (this._core.buffers.active === this._core.buffers.normal) { return this.normal; } From c5ab83d6978c7d5fd046b6ea1bf924f421a2bf34 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 27 May 2026 15:33:03 +0000 Subject: [PATCH 16/47] Remove stale FIXME comments from charAttributes JSDoc Fixes #5913. Blinking is supported in renderers via TextBlinkStateManager, and bright background colors (100-107) are handled in the SGR loop. Co-authored-by: Daniel Imms --- src/common/InputHandler.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/common/InputHandler.ts b/src/common/InputHandler.ts index 2b7142dd0..33e954811 100644 --- a/src/common/InputHandler.ts +++ b/src/common/InputHandler.ts @@ -2590,10 +2590,6 @@ export class InputHandler extends Disposable implements IInputHandler { * | 3 | CMY color. | #N | * | 4 | CMYK color. | #N | * | 5 | Indexed (256 colors) as `Ps ; 5 ; INDEX` or `Ps : 5 : INDEX`. | #Y | - * - * - * FIXME: blinking is implemented in attrs, but not working in renderers? - * FIXME: remove dead branch for p=100 */ public charAttributes(params: IParams): boolean { // Optimize a single SGR0. From d565a2750e4511db0cfd5882460513f2f61b3d3c Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 27 May 2026 15:33:20 +0000 Subject: [PATCH 17/47] Fix SelectionService trim listener leak with MutableDisposable Register _trimListener via _register(MutableDisposable) so it is disposed when SelectionService is torn down. Use .value assignment on buffer activate instead of manual dispose/swap. Fixes #5914 Co-authored-by: Daniel Imms --- src/browser/services/SelectionService.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/browser/services/SelectionService.ts b/src/browser/services/SelectionService.ts index 39692a871..356cd77fe 100644 --- a/src/browser/services/SelectionService.ts +++ b/src/browser/services/SelectionService.ts @@ -9,7 +9,7 @@ import { moveToCellSequence } from 'browser/input/MoveToCell'; import { SelectionModel } from 'browser/selection/SelectionModel'; import { ISelectionRedrawRequestEvent, ISelectionRequestScrollLinesEvent } from 'browser/selection/Types'; import { ICoreBrowserService, IMouseCoordsService, IRenderService, ISelectionService } from 'browser/services/Services'; -import { Disposable, toDisposable } from 'common/Lifecycle'; +import { Disposable, MutableDisposable, toDisposable } from 'common/Lifecycle'; import * as Browser from 'common/Platform'; import { IBufferLine, ICellData, IDisposable } from 'common/Types'; import { getRangeLength } from 'common/buffer/BufferRange'; @@ -102,7 +102,7 @@ export class SelectionService extends Disposable implements ISelectionService { private _mouseMoveListener: EventListener; private _mouseUpListener: EventListener; - private _trimListener: IDisposable; + private readonly _trimListener = this._register(new MutableDisposable()); private _workCell: CellData = new CellData(); private _mouseDownTimeStamp: number = 0; @@ -140,7 +140,7 @@ export class SelectionService extends Disposable implements ISelectionService { this.clearSelection(); } }); - this._trimListener = this._bufferService.buffer.lines.onTrim(amount => this._handleTrim(amount)); + this._trimListener.value = this._bufferService.buffer.lines.onTrim(amount => this._handleTrim(amount)); this._register(this._bufferService.buffers.onBufferActivate(e => this._handleBufferActivate(e))); this.enable(); @@ -769,8 +769,7 @@ export class SelectionService extends Disposable implements ISelectionService { // reverseIndex) and delete in a splice is only ever used when the same // number of elements was just added. Given this is could actually be // beneficial to leave the selection as is for these cases. - this._trimListener.dispose(); - this._trimListener = e.activeBuffer.lines.onTrim(amount => this._handleTrim(amount)); + this._trimListener.value = e.activeBuffer.lines.onTrim(amount => this._handleTrim(amount)); } /** From adc9dea083d11c7738401f5c33314697d1a55e3c Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 27 May 2026 15:33:28 +0000 Subject: [PATCH 18/47] Fix TextureAtlas remove-emitter leak on dispose Dispose _onRemoveTextureAtlasCanvas in TextureAtlas.dispose() alongside _onAddTextureAtlasCanvas to avoid leaking listeners when the atlas is torn down. Fixes #5917 Co-authored-by: Daniel Imms --- addons/addon-webgl/src/TextureAtlas.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/addons/addon-webgl/src/TextureAtlas.ts b/addons/addon-webgl/src/TextureAtlas.ts index 34188235c..a2857e0a4 100644 --- a/addons/addon-webgl/src/TextureAtlas.ts +++ b/addons/addon-webgl/src/TextureAtlas.ts @@ -109,6 +109,7 @@ export class TextureAtlas implements ITextureAtlas { page.canvas.remove(); } this._onAddTextureAtlasCanvas.dispose(); + this._onRemoveTextureAtlasCanvas.dispose(); } public warmUp(): void { From 7b6c814566754993d0259976da93e0bc17ce79e0 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 27 May 2026 15:33:48 +0000 Subject: [PATCH 19/47] Log errors from onWillOpen handlers instead of swallowing them Fixes #5920 Co-authored-by: Daniel Imms --- src/browser/CoreBrowserTerminal.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/browser/CoreBrowserTerminal.ts b/src/browser/CoreBrowserTerminal.ts index 52eb00025..e44b1717e 100644 --- a/src/browser/CoreBrowserTerminal.ts +++ b/src/browser/CoreBrowserTerminal.ts @@ -563,8 +563,9 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal { try { this._onWillOpen.fire(this.element); + } catch (e) { + this._logService.error('onWillOpen handler threw an exception', e); } - catch { /* fails to load addon for some reason */ } if (!this._renderService.hasRenderer()) { this._renderService.setRenderer(this._createRenderer()); } From 8f2bb32c9cbc04a1e753f038384d846a9fa7da0e Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 27 May 2026 15:34:00 +0000 Subject: [PATCH 20/47] fix(headless): clear markers when clear() is called Headless clear() now calls buffer.clearAllMarkers() to match the browser implementation, preventing markers from surviving clear() and pointing at wrong lines. Fixes #5910 Co-authored-by: Daniel Imms --- src/headless/Terminal.ts | 1 + src/headless/public/Terminal.test.ts | 23 +++++++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/src/headless/Terminal.ts b/src/headless/Terminal.ts index cc8b152b2..2df76c61e 100644 --- a/src/headless/Terminal.ts +++ b/src/headless/Terminal.ts @@ -108,6 +108,7 @@ export class Terminal extends CoreTerminal { // Don't clear if it's already clear return; } + this.buffer.clearAllMarkers(); this.buffer.lines.set(0, this.buffer.lines.get(this.buffer.ybase + this.buffer.y)!); this.buffer.lines.length = 1; this.buffer.ydisp = 0; diff --git a/src/headless/public/Terminal.test.ts b/src/headless/public/Terminal.test.ts index 0b35fb0e7..0c4739744 100644 --- a/src/headless/public/Terminal.test.ts +++ b/src/headless/public/Terminal.test.ts @@ -114,6 +114,29 @@ describe('Headless API Tests', function (): void { } }); + it('clear disposes markers', async () => { + term = new Terminal({ rows: 5, allowProposedApi: true }); + for (let i = 0; i < 10; i++) { + await writeSync('\n\rtest' + i); + } + const markers = [ + term.registerMarker(1)!, + term.registerMarker(2)!, + term.registerMarker(3)!, + term.registerMarker(4)! + ]; + let disposeCount = 0; + for (const marker of markers) { + marker.onDispose(() => disposeCount++); + } + term.clear(); + strictEqual(disposeCount, markers.length); + for (const marker of markers) { + strictEqual(marker.isDisposed, true); + } + strictEqual(term.markers.length, 0); + }); + describe('options', () => { const termOptions = { cols: 80, From 306c3d73ec64ec8bf6ca7634923f32a9eccb95af Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 27 May 2026 15:34:26 +0000 Subject: [PATCH 21/47] Fix flushSync() to fire onWriteParsed flushSync() processes pending writes synchronously (e.g. during CoreTerminal.resize()) but did not emit onWriteParsed, unlike the async _innerWrite path. Addons listening for parse completion could miss updates after resize. Fixes #5912 Co-authored-by: Daniel Imms --- src/common/input/WriteBuffer.test.ts | 15 +++++++++++++++ src/common/input/WriteBuffer.ts | 5 +++++ 2 files changed, 20 insertions(+) diff --git a/src/common/input/WriteBuffer.test.ts b/src/common/input/WriteBuffer.test.ts index f366a6320..cca6bd0be 100644 --- a/src/common/input/WriteBuffer.test.ts +++ b/src/common/input/WriteBuffer.test.ts @@ -125,5 +125,20 @@ describe('WriteBuffer', () => { assert.deepEqual(stack, []); assert.deepEqual(cbStack, []); }); + it('flushSync fires onWriteParsed', () => { + let parsed = 0; + wb.onWriteParsed(() => parsed++); + wb.write('a'); + wb.write('b'); + assert.equal(parsed, 0); + wb.flushSync(); + assert.equal(parsed, 1); + }); + it('flushSync with no pending writes does not fire onWriteParsed', () => { + let parsed = 0; + wb.onWriteParsed(() => parsed++); + wb.flushSync(); + assert.equal(parsed, 0); + }); }); }); diff --git a/src/common/input/WriteBuffer.ts b/src/common/input/WriteBuffer.ts index b936c9dbf..7c6d5d866 100644 --- a/src/common/input/WriteBuffer.ts +++ b/src/common/input/WriteBuffer.ts @@ -71,7 +71,9 @@ export class WriteBuffer extends Disposable { // Process all pending chunks synchronously let chunk: string | Uint8Array | undefined; + let didProcess = false; while (chunk = this._writeBuffer.shift()) { + didProcess = true; this._action(chunk); const cb = this._callbacks.shift(); if (cb) cb(); @@ -84,6 +86,9 @@ export class WriteBuffer extends Disposable { this._callbacks.length = 0; this._isSyncWriting = false; + if (didProcess) { + this._onWriteParsed.fire(); + } } /** From fad4821fd953ebe19e65b7e81da5d972fd1a01c7 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 27 May 2026 15:34:48 +0000 Subject: [PATCH 22/47] =?UTF-8?q?Fix=20O(n=C2=B2)=20payload=20accumulation?= =?UTF-8?q?=20in=20DCS/OSC/APC=20handlers?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace repeated string concatenation with a chunk buffer that joins once on completion. Fixes #5916. Co-authored-by: Daniel Imms --- src/common/parser/ApcParser.ts | 15 +++++------ src/common/parser/DcsParser.ts | 15 +++++------ src/common/parser/OscParser.ts | 15 +++++------ src/common/parser/PayloadStringBuffer.ts | 34 ++++++++++++++++++++++++ 4 files changed, 55 insertions(+), 24 deletions(-) create mode 100644 src/common/parser/PayloadStringBuffer.ts diff --git a/src/common/parser/ApcParser.ts b/src/common/parser/ApcParser.ts index 8f921f8cb..d3f5b8e2e 100644 --- a/src/common/parser/ApcParser.ts +++ b/src/common/parser/ApcParser.ts @@ -7,6 +7,7 @@ import { IApcHandler, IHandlerCollection, ApcFallbackHandlerType, IApcParser, IS import { ParserConstants } from 'common/parser/Constants'; import { utf32ToString } from 'common/input/TextDecoder'; import { IDisposable } from 'common/Types'; +import { PayloadStringBuffer } from 'common/parser/PayloadStringBuffer'; const EMPTY_HANDLERS: IApcHandler[] = []; @@ -153,13 +154,13 @@ export class ApcParser implements IApcParser { export class ApcHandler implements IApcHandler { private static _payloadLimit = ParserConstants.PAYLOAD_LIMIT; - private _data = ''; + private _data = new PayloadStringBuffer(); private _hitLimit: boolean = false; constructor(private _handler: (data: string) => boolean | Promise) { } public start(): void { - this._data = ''; + this._data.reset(); this._hitLimit = false; } @@ -167,9 +168,7 @@ export class ApcHandler implements IApcHandler { if (this._hitLimit) { return; } - this._data += utf32ToString(data, start, end); - if (this._data.length > ApcHandler._payloadLimit) { - this._data = ''; + if (this._data.add(utf32ToString(data, start, end), ApcHandler._payloadLimit)) { this._hitLimit = true; } } @@ -179,18 +178,18 @@ export class ApcHandler implements IApcHandler { if (this._hitLimit) { ret = false; } else if (success) { - ret = this._handler(this._data); + ret = this._handler(this._data.toString()); if (ret instanceof Promise) { // need to hold data until `ret` got resolved // dont care for errors, data will be freed anyway on next start return ret.then(res => { - this._data = ''; + this._data.reset(); this._hitLimit = false; return res; }); } } - this._data = ''; + this._data.reset(); this._hitLimit = false; return ret; } diff --git a/src/common/parser/DcsParser.ts b/src/common/parser/DcsParser.ts index 692200230..be2fa39d5 100644 --- a/src/common/parser/DcsParser.ts +++ b/src/common/parser/DcsParser.ts @@ -8,6 +8,7 @@ import { IDcsHandler, IParams, IHandlerCollection, IDcsParser, DcsFallbackHandle import { utf32ToString } from 'common/input/TextDecoder'; import { Params } from 'common/parser/Params'; import { ParserConstants } from 'common/parser/Constants'; +import { PayloadStringBuffer } from 'common/parser/PayloadStringBuffer'; const EMPTY_HANDLERS: IDcsHandler[] = []; @@ -140,7 +141,7 @@ EMPTY_PARAMS.addParam(0); export class DcsHandler implements IDcsHandler { private static _payloadLimit = ParserConstants.PAYLOAD_LIMIT; - private _data = ''; + private _data = new PayloadStringBuffer(); private _params: IParams = EMPTY_PARAMS; private _hitLimit: boolean = false; @@ -152,7 +153,7 @@ export class DcsHandler implements IDcsHandler { // perf optimization: // clone only, if we have non empty params, otherwise stick with default this._params = (params.length > 1 || params.params[0]) ? params.clone() : EMPTY_PARAMS; - this._data = ''; + this._data.reset(); this._hitLimit = false; } @@ -160,9 +161,7 @@ export class DcsHandler implements IDcsHandler { if (this._hitLimit) { return; } - this._data += utf32ToString(data, start, end); - if (this._data.length > DcsHandler._payloadLimit) { - this._data = ''; + if (this._data.add(utf32ToString(data, start, end), DcsHandler._payloadLimit)) { this._hitLimit = true; } } @@ -172,20 +171,20 @@ export class DcsHandler implements IDcsHandler { if (this._hitLimit) { ret = false; } else if (success) { - ret = this._handler(this._data, this._params); + ret = this._handler(this._data.toString(), this._params); if (ret instanceof Promise) { // need to hold data and params until `ret` got resolved // dont care for errors, data will be freed anyway on next start return ret.then(res => { this._params = EMPTY_PARAMS; - this._data = ''; + this._data.reset(); this._hitLimit = false; return res; }); } } this._params = EMPTY_PARAMS; - this._data = ''; + this._data.reset(); this._hitLimit = false; return ret; } diff --git a/src/common/parser/OscParser.ts b/src/common/parser/OscParser.ts index 1f0c8cb23..6a17af817 100644 --- a/src/common/parser/OscParser.ts +++ b/src/common/parser/OscParser.ts @@ -7,6 +7,7 @@ import { IOscHandler, IHandlerCollection, OscFallbackHandlerType, IOscParser, IS import { OscState, ParserConstants } from 'common/parser/Constants'; import { utf32ToString } from 'common/input/TextDecoder'; import { IDisposable } from 'common/Types'; +import { PayloadStringBuffer } from 'common/parser/PayloadStringBuffer'; const EMPTY_HANDLERS: IOscHandler[] = []; @@ -194,13 +195,13 @@ export class OscParser implements IOscParser { export class OscHandler implements IOscHandler { private static _payloadLimit = ParserConstants.PAYLOAD_LIMIT; - private _data = ''; + private _data = new PayloadStringBuffer(); private _hitLimit: boolean = false; constructor(private _handler: (data: string) => boolean | Promise) { } public start(): void { - this._data = ''; + this._data.reset(); this._hitLimit = false; } @@ -208,9 +209,7 @@ export class OscHandler implements IOscHandler { if (this._hitLimit) { return; } - this._data += utf32ToString(data, start, end); - if (this._data.length > OscHandler._payloadLimit) { - this._data = ''; + if (this._data.add(utf32ToString(data, start, end), OscHandler._payloadLimit)) { this._hitLimit = true; } } @@ -220,18 +219,18 @@ export class OscHandler implements IOscHandler { if (this._hitLimit) { ret = false; } else if (success) { - ret = this._handler(this._data); + ret = this._handler(this._data.toString()); if (ret instanceof Promise) { // need to hold data until `ret` got resolved // dont care for errors, data will be freed anyway on next start return ret.then(res => { - this._data = ''; + this._data.reset(); this._hitLimit = false; return res; }); } } - this._data = ''; + this._data.reset(); this._hitLimit = false; return ret; } diff --git a/src/common/parser/PayloadStringBuffer.ts b/src/common/parser/PayloadStringBuffer.ts new file mode 100644 index 000000000..9caef25ea --- /dev/null +++ b/src/common/parser/PayloadStringBuffer.ts @@ -0,0 +1,34 @@ +/** + * Copyright (c) 2026 The xterm.js authors. All rights reserved. + * @license MIT + */ + +/** + * Accumulates string payloads from multiple chunks without O(n²) string concatenation. + */ +export class PayloadStringBuffer { + private _chunks: string[] = []; + private _length = 0; + + public reset(): void { + this._chunks.length = 0; + this._length = 0; + } + + /** + * @returns true if the payload limit was exceeded (buffer is cleared in that case) + */ + public add(chunk: string, limit: number): boolean { + this._chunks.push(chunk); + this._length += chunk.length; + if (this._length > limit) { + this.reset(); + return true; + } + return false; + } + + public toString(): string { + return this._chunks.join(''); + } +} From c06c3025f28e351935fbc6da7058b0169c9eb9e8 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 27 May 2026 15:35:04 +0000 Subject: [PATCH 23/47] fix(addon-webgl): delete VAOs on renderer dispose Register vertex array objects for disposal in GlyphRenderer and RectangleRenderer, matching existing buffer and program cleanup. Fixes #5918 Co-authored-by: Daniel Imms --- addons/addon-webgl/src/GlyphRenderer.ts | 1 + addons/addon-webgl/src/RectangleRenderer.ts | 1 + addons/addon-webgl/src/Types.ts | 3 ++- 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/addons/addon-webgl/src/GlyphRenderer.ts b/addons/addon-webgl/src/GlyphRenderer.ts index 46b8efeb9..7a7e98496 100644 --- a/addons/addon-webgl/src/GlyphRenderer.ts +++ b/addons/addon-webgl/src/GlyphRenderer.ts @@ -137,6 +137,7 @@ export class GlyphRenderer extends Disposable { // Create and set the vertex array object this._vertexArrayObject = gl.createVertexArray(); + this._register(toDisposable(() => gl.deleteVertexArray(this._vertexArrayObject))); gl.bindVertexArray(this._vertexArrayObject); // Setup a_unitquad, this defines the 4 vertices of a rectangle diff --git a/addons/addon-webgl/src/RectangleRenderer.ts b/addons/addon-webgl/src/RectangleRenderer.ts index 81e129e5c..b2225de3a 100644 --- a/addons/addon-webgl/src/RectangleRenderer.ts +++ b/addons/addon-webgl/src/RectangleRenderer.ts @@ -103,6 +103,7 @@ export class RectangleRenderer extends Disposable { // Create and set the vertex array object this._vertexArrayObject = gl.createVertexArray(); + this._register(toDisposable(() => gl.deleteVertexArray(this._vertexArrayObject))); gl.bindVertexArray(this._vertexArrayObject); // Setup a_unitquad, this defines the 4 vertices of a rectangle diff --git a/addons/addon-webgl/src/Types.ts b/addons/addon-webgl/src/Types.ts index 67c9408d6..b3c51e842 100644 --- a/addons/addon-webgl/src/Types.ts +++ b/addons/addon-webgl/src/Types.ts @@ -28,7 +28,8 @@ export interface ICursorRenderModel { export interface IWebGL2RenderingContext extends WebGLRenderingContext { vertexAttribDivisor(index: number, divisor: number): void; createVertexArray(): IWebGLVertexArrayObject; - bindVertexArray(vao: IWebGLVertexArrayObject): void; + deleteVertexArray(vao: IWebGLVertexArrayObject | null): void; + bindVertexArray(vao: IWebGLVertexArrayObject | null): void; drawElementsInstanced(mode: number, count: number, type: number, offset: number, instanceCount: number): void; } From f43273b3f0c589b063c5e4a772abae173def3679 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 27 May 2026 15:35:05 +0000 Subject: [PATCH 24/47] fix(webgl): include texture size and cell dimensions in atlas cache equality configEquals omitted deviceMaxTextureSize, deviceCellWidth, and deviceCellHeight, which could cause the char atlas cache to reuse an atlas built for a different GPU texture limit or cell size. Fixes #5915 Co-authored-by: Daniel Imms --- addons/addon-webgl/src/CharAtlasUtils.test.ts | 87 +++++++++++++++++++ addons/addon-webgl/src/CharAtlasUtils.ts | 3 + 2 files changed, 90 insertions(+) create mode 100644 addons/addon-webgl/src/CharAtlasUtils.test.ts diff --git a/addons/addon-webgl/src/CharAtlasUtils.test.ts b/addons/addon-webgl/src/CharAtlasUtils.test.ts new file mode 100644 index 000000000..0a135746e --- /dev/null +++ b/addons/addon-webgl/src/CharAtlasUtils.test.ts @@ -0,0 +1,87 @@ +/** + * Copyright (c) 2026 The xterm.js authors. All rights reserved. + * @license MIT + */ + +import { assert } from 'chai'; +import { configEquals } from './CharAtlasUtils'; +import { ICharAtlasConfig } from './Types'; +import { NULL_COLOR } from 'common/Color'; +import { IColor } from 'common/Types'; + +function createTestConfig(overrides: Partial = {}): ICharAtlasConfig { + const color: IColor = { css: '#ffffff', rgba: 0xffffffff }; + const contrastCache = { + clear: () => {}, + setCss: () => {}, + getCss: () => undefined, + setColor: () => {}, + getColor: () => undefined + }; + const colors = { + foreground: color, + background: color, + cursor: NULL_COLOR, + cursorAccent: NULL_COLOR, + selectionForeground: undefined, + selectionBackgroundTransparent: NULL_COLOR, + selectionBackgroundOpaque: NULL_COLOR, + selectionInactiveBackgroundTransparent: NULL_COLOR, + selectionInactiveBackgroundOpaque: NULL_COLOR, + overviewRulerBorder: NULL_COLOR, + scrollbarSliderBackground: NULL_COLOR, + scrollbarSliderHoverBackground: NULL_COLOR, + scrollbarSliderActiveBackground: NULL_COLOR, + ansi: new Array(256).fill(color), + contrastCache, + halfContrastCache: contrastCache + }; + return { + customGlyphs: true, + devicePixelRatio: 1, + deviceMaxTextureSize: 4096, + letterSpacing: 0, + lineHeight: 1, + fontSize: 15, + fontFamily: 'monospace', + fontWeight: 'normal', + fontWeightBold: 'bold', + deviceCellWidth: 10, + deviceCellHeight: 20, + deviceCharWidth: 8, + deviceCharHeight: 16, + allowTransparency: false, + drawBoldTextInBrightColors: true, + minimumContrastRatio: 1, + colors, + ...overrides + }; +} + +describe('CharAtlasUtils', () => { + describe('configEquals', () => { + it('should return true for identical configs', () => { + const a = createTestConfig(); + const b = createTestConfig(); + assert.ok(configEquals(a, b)); + }); + + it('should return false when deviceMaxTextureSize differs', () => { + const a = createTestConfig(); + const b = createTestConfig({ deviceMaxTextureSize: 8192 }); + assert.ok(!configEquals(a, b)); + }); + + it('should return false when deviceCellWidth differs', () => { + const a = createTestConfig(); + const b = createTestConfig({ deviceCellWidth: 11 }); + assert.ok(!configEquals(a, b)); + }); + + it('should return false when deviceCellHeight differs', () => { + const a = createTestConfig(); + const b = createTestConfig({ deviceCellHeight: 21 }); + assert.ok(!configEquals(a, b)); + }); + }); +}); diff --git a/addons/addon-webgl/src/CharAtlasUtils.ts b/addons/addon-webgl/src/CharAtlasUtils.ts index 4ce103e98..d9ca69b39 100644 --- a/addons/addon-webgl/src/CharAtlasUtils.ts +++ b/addons/addon-webgl/src/CharAtlasUtils.ts @@ -59,6 +59,7 @@ export function configEquals(a: ICharAtlasConfig, b: ICharAtlasConfig): boolean } } return a.devicePixelRatio === b.devicePixelRatio && + a.deviceMaxTextureSize === b.deviceMaxTextureSize && a.customGlyphs === b.customGlyphs && a.lineHeight === b.lineHeight && a.letterSpacing === b.letterSpacing && @@ -67,6 +68,8 @@ export function configEquals(a: ICharAtlasConfig, b: ICharAtlasConfig): boolean a.fontWeight === b.fontWeight && a.fontWeightBold === b.fontWeightBold && a.allowTransparency === b.allowTransparency && + a.deviceCellWidth === b.deviceCellWidth && + a.deviceCellHeight === b.deviceCellHeight && a.deviceCharWidth === b.deviceCharWidth && a.deviceCharHeight === b.deviceCharHeight && a.drawBoldTextInBrightColors === b.drawBoldTextInBrightColors && From 4aa02f3fd947838b94ec82e68d248d4d20e9c6f7 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 27 May 2026 15:37:09 +0000 Subject: [PATCH 25/47] fix(addon-webgl): route logging through ILogService instead of console Replace direct console.log/warn/error calls in the WebGL addon with ILogService so messages respect the terminal logLevel and custom logger. Fixes xtermjs/xterm.js#5921 Co-authored-by: Daniel Imms --- addons/addon-webgl/src/GlyphRenderer.ts | 7 ++++--- addons/addon-webgl/src/RectangleRenderer.ts | 6 ++++-- addons/addon-webgl/src/TextureAtlas.ts | 2 +- addons/addon-webgl/src/WebglAddon.ts | 4 +++- addons/addon-webgl/src/WebglRenderer.ts | 13 +++++++------ addons/addon-webgl/src/WebglUtils.ts | 13 +++++++------ .../src/customGlyphs/CustomGlyphRasterizer.ts | 17 +++++++++++------ 7 files changed, 37 insertions(+), 25 deletions(-) diff --git a/addons/addon-webgl/src/GlyphRenderer.ts b/addons/addon-webgl/src/GlyphRenderer.ts index 46b8efeb9..1efde442e 100644 --- a/addons/addon-webgl/src/GlyphRenderer.ts +++ b/addons/addon-webgl/src/GlyphRenderer.ts @@ -9,7 +9,7 @@ import { Disposable, toDisposable } from 'common/Lifecycle'; import { Terminal } from '@xterm/xterm'; import { IRenderModel, IWebGL2RenderingContext, IWebGLVertexArrayObject, type IRasterizedGlyph, type ITextureAtlas } from './Types'; import { createProgram, GLTexture, PROJECTION_MATRIX } from './WebglUtils'; -import type { IOptionsService } from 'common/services/Services'; +import type { ILogService, IOptionsService } from 'common/services/Services'; import { allowRescaling, throwIfFalsy } from 'browser/renderer/shared/RendererUtils'; interface IVertices { @@ -114,7 +114,8 @@ export class GlyphRenderer extends Disposable { private readonly _terminal: Terminal, private readonly _gl: IWebGL2RenderingContext, private _dimensions: IRenderDimensions, - private readonly _optionsService: IOptionsService + private readonly _optionsService: IOptionsService, + private readonly _logService: ILogService ) { super(); @@ -127,7 +128,7 @@ export class GlyphRenderer extends Disposable { TextureAtlas.maxTextureSize = throwIfFalsy(gl.getParameter(gl.MAX_TEXTURE_SIZE) as number | null); } - this._program = throwIfFalsy(createProgram(gl, vertexShaderSource, createFragmentShaderSource(TextureAtlas.maxAtlasPages))); + this._program = throwIfFalsy(createProgram(gl, vertexShaderSource, createFragmentShaderSource(TextureAtlas.maxAtlasPages), this._logService)); this._register(toDisposable(() => gl.deleteProgram(this._program))); // Uniform locations diff --git a/addons/addon-webgl/src/RectangleRenderer.ts b/addons/addon-webgl/src/RectangleRenderer.ts index 81e129e5c..6da92be13 100644 --- a/addons/addon-webgl/src/RectangleRenderer.ts +++ b/addons/addon-webgl/src/RectangleRenderer.ts @@ -14,6 +14,7 @@ import { RenderModelConstants } from './RenderModel'; import { IRenderModel, IWebGL2RenderingContext, IWebGLVertexArrayObject } from './Types'; import { createProgram, expandFloat32Array, PROJECTION_MATRIX } from './WebglUtils'; import { throwIfFalsy } from 'browser/renderer/shared/RendererUtils'; +import type { ILogService } from 'common/services/Services'; const enum VertexAttribLocations { POSITION = 0, @@ -89,13 +90,14 @@ export class RectangleRenderer extends Disposable { private _terminal: Terminal, private _gl: IWebGL2RenderingContext, private _dimensions: IRenderDimensions, - private readonly _themeService: IThemeService + private readonly _themeService: IThemeService, + private readonly _logService: ILogService ) { super(); const gl = this._gl; - this._program = throwIfFalsy(createProgram(gl, vertexShaderSource, fragmentShaderSource)); + this._program = throwIfFalsy(createProgram(gl, vertexShaderSource, fragmentShaderSource, this._logService)); this._register(toDisposable(() => gl.deleteProgram(this._program))); // Uniform locations diff --git a/addons/addon-webgl/src/TextureAtlas.ts b/addons/addon-webgl/src/TextureAtlas.ts index 34188235c..ba7db27d3 100644 --- a/addons/addon-webgl/src/TextureAtlas.ts +++ b/addons/addon-webgl/src/TextureAtlas.ts @@ -529,7 +529,7 @@ export class TextureAtlas implements ITextureAtlas { let customGlyph = false; if (this._config.customGlyphs !== false) { const variantOffset = this._workAttributeData.getUnderlineVariantOffset(); - customGlyph = tryDrawCustomGlyph(this._tmpCtx, chars, padding, padding, this._config.deviceCellWidth, this._config.deviceCellHeight, this._config.deviceCharWidth, this._config.deviceCharHeight, this._config.fontSize, this._config.devicePixelRatio, backgroundColor.css, variantOffset); + customGlyph = tryDrawCustomGlyph(this._tmpCtx, chars, padding, padding, this._config.deviceCellWidth, this._config.deviceCellHeight, this._config.deviceCharWidth, this._config.deviceCharHeight, this._config.fontSize, this._config.devicePixelRatio, this._logService, backgroundColor.css, variantOffset); } // Whether to clear pixels based on a threshold difference between the glyph color and the diff --git a/addons/addon-webgl/src/WebglAddon.ts b/addons/addon-webgl/src/WebglAddon.ts index f21fb704e..624c9ea38 100644 --- a/addons/addon-webgl/src/WebglAddon.ts +++ b/addons/addon-webgl/src/WebglAddon.ts @@ -9,7 +9,7 @@ import { ICharacterJoinerService, ICharSizeService, ICoreBrowserService, IRender import { ITerminal } from 'browser/Types'; import { Disposable, toDisposable } from 'common/Lifecycle'; import { getSafariVersion, isSafari } from 'common/Platform'; -import { ICoreService, IDecorationService, IOptionsService } from 'common/services/Services'; +import { ICoreService, IDecorationService, ILogService, IOptionsService } from 'common/services/Services'; import { IWebGL2RenderingContext } from './Types'; import { WebglRenderer } from './WebglRenderer'; import { Emitter, EventUtils } from 'common/Event'; @@ -65,6 +65,7 @@ export class WebglAddon extends Disposable implements ITerminalAddon, IWebglApi const charSizeService: ICharSizeService = unsafeCore._charSizeService; const coreBrowserService: ICoreBrowserService = unsafeCore._coreBrowserService; const decorationService: IDecorationService = unsafeCore._decorationService; + const logService: ILogService = unsafeCore._logService; const themeService: IThemeService = unsafeCore._themeService; this._renderer = this._register(new WebglRenderer( @@ -74,6 +75,7 @@ export class WebglAddon extends Disposable implements ITerminalAddon, IWebglApi coreBrowserService, coreService, decorationService, + logService, optionsService, themeService, this._customGlyphs, diff --git a/addons/addon-webgl/src/WebglRenderer.ts b/addons/addon-webgl/src/WebglRenderer.ts index 500308bd1..2c20accc7 100644 --- a/addons/addon-webgl/src/WebglRenderer.ts +++ b/addons/addon-webgl/src/WebglRenderer.ts @@ -15,7 +15,7 @@ import { AttributeData } from 'common/buffer/AttributeData'; import { CellData } from 'common/buffer/CellData'; import { Attributes, Content, FgFlags, NULL_CELL_CHAR, NULL_CELL_CODE } from 'common/buffer/Constants'; import { TextBlinkStateManager } from 'browser/renderer/shared/TextBlinkStateManager'; -import { ICoreService, IDecorationService, IOptionsService } from 'common/services/Services'; +import { ICoreService, IDecorationService, ILogService, IOptionsService } from 'common/services/Services'; import { Terminal } from '@xterm/xterm'; import { GlyphRenderer } from './GlyphRenderer'; import { RectangleRenderer } from './RectangleRenderer'; @@ -77,6 +77,7 @@ export class WebglRenderer extends Disposable implements IRenderer { private readonly _coreBrowserService: ICoreBrowserService, private readonly _coreService: ICoreService, private readonly _decorationService: IDecorationService, + private readonly _logService: ILogService, private readonly _optionsService: IOptionsService, private readonly _themeService: IThemeService, private readonly _customGlyphs: boolean = true, @@ -122,19 +123,19 @@ export class WebglRenderer extends Disposable implements IRenderer { this._deviceMaxTextureSize = this._gl.getParameter(this._gl.MAX_TEXTURE_SIZE); this._register(addDisposableListener(this._canvas, 'webglcontextlost', (e) => { - console.log('webglcontextlost event received'); + this._logService.debug('webglcontextlost event received'); // Prevent the default behavior in order to enable WebGL context restoration. e.preventDefault(); // Wait a few seconds to see if the 'webglcontextrestored' event is fired. // If not, dispatch the onContextLoss notification to observers. this._contextRestorationTimeout = setTimeout(() => { this._contextRestorationTimeout = undefined; - console.warn('webgl context not restored; firing onContextLoss'); + this._logService.warn('webgl context not restored; firing onContextLoss'); this._onContextLoss.fire(e); }, 3000 /* ms */); })); this._register(addDisposableListener(this._canvas, 'webglcontextrestored', (e) => { - console.warn('webglcontextrestored event received'); + this._logService.warn('webglcontextrestored event received'); clearTimeout(this._contextRestorationTimeout); this._contextRestorationTimeout = undefined; // The texture atlas and glyph renderer must be fully reinitialized @@ -274,8 +275,8 @@ export class WebglRenderer extends Disposable implements IRenderer { * Initializes members dependent on WebGL context state. */ private _initializeWebGLState(): [RectangleRenderer, GlyphRenderer] { - this._rectangleRenderer.value = new RectangleRenderer(this._terminal, this._gl, this.dimensions, this._themeService); - this._glyphRenderer.value = new GlyphRenderer(this._terminal, this._gl, this.dimensions, this._optionsService); + this._rectangleRenderer.value = new RectangleRenderer(this._terminal, this._gl, this.dimensions, this._themeService, this._logService); + this._glyphRenderer.value = new GlyphRenderer(this._terminal, this._gl, this.dimensions, this._optionsService, this._logService); // Update dimensions and acquire char atlas this.handleCharSizeChanged(); diff --git a/addons/addon-webgl/src/WebglUtils.ts b/addons/addon-webgl/src/WebglUtils.ts index da765c802..9944c6bd3 100644 --- a/addons/addon-webgl/src/WebglUtils.ts +++ b/addons/addon-webgl/src/WebglUtils.ts @@ -4,6 +4,7 @@ */ import { throwIfFalsy } from 'browser/renderer/shared/RendererUtils'; +import type { ILogService } from 'common/services/Services'; /** * A matrix that when multiplies will translate 0-1 coordinates (left to right, @@ -16,21 +17,21 @@ export const PROJECTION_MATRIX = new Float32Array([ -1, 1, 0, 1 ]); -export function createProgram(gl: WebGLRenderingContext, vertexSource: string, fragmentSource: string): WebGLProgram | undefined { +export function createProgram(gl: WebGLRenderingContext, vertexSource: string, fragmentSource: string, logService: ILogService): WebGLProgram | undefined { const program = throwIfFalsy(gl.createProgram()); - gl.attachShader(program, throwIfFalsy(createShader(gl, gl.VERTEX_SHADER, vertexSource))); - gl.attachShader(program, throwIfFalsy(createShader(gl, gl.FRAGMENT_SHADER, fragmentSource))); + gl.attachShader(program, throwIfFalsy(createShader(gl, gl.VERTEX_SHADER, vertexSource, logService))); + gl.attachShader(program, throwIfFalsy(createShader(gl, gl.FRAGMENT_SHADER, fragmentSource, logService))); gl.linkProgram(program); const success = gl.getProgramParameter(program, gl.LINK_STATUS); if (success) { return program; } - console.error(gl.getProgramInfoLog(program)); + logService.error(gl.getProgramInfoLog(program)); gl.deleteProgram(program); } -export function createShader(gl: WebGLRenderingContext, type: number, source: string): WebGLShader | undefined { +export function createShader(gl: WebGLRenderingContext, type: number, source: string, logService: ILogService): WebGLShader | undefined { const shader = throwIfFalsy(gl.createShader(type)); gl.shaderSource(shader, source); gl.compileShader(shader); @@ -39,7 +40,7 @@ export function createShader(gl: WebGLRenderingContext, type: number, source: st return shader; } - console.error(gl.getShaderInfoLog(shader)); + logService.error(gl.getShaderInfoLog(shader)); gl.deleteShader(shader); } diff --git a/addons/addon-webgl/src/customGlyphs/CustomGlyphRasterizer.ts b/addons/addon-webgl/src/customGlyphs/CustomGlyphRasterizer.ts index ba8925727..357d512d2 100644 --- a/addons/addon-webgl/src/customGlyphs/CustomGlyphRasterizer.ts +++ b/addons/addon-webgl/src/customGlyphs/CustomGlyphRasterizer.ts @@ -4,6 +4,7 @@ */ import { throwIfFalsy } from 'browser/renderer/shared/RendererUtils'; +import type { ILogService } from 'common/services/Services'; import { customGlyphDefinitions } from './CustomGlyphDefinitions'; import { CustomGlyphDefinitionType, CustomGlyphScaleType, CustomGlyphVectorType, type CustomGlyphDefinitionPart, type CustomGlyphPathDrawFunctionDefinition, type CustomGlyphPatternDefinition, type ICustomGlyphSolidOctantBlockVector, type ICustomGlyphVectorShape } from './Types'; @@ -22,6 +23,7 @@ export function tryDrawCustomGlyph( deviceCharHeight: number, fontSize: number, devicePixelRatio: number, + logService: ILogService, backgroundColor?: string, variantOffset: number = 0 ): boolean { @@ -30,7 +32,7 @@ export function tryDrawCustomGlyph( // Normalize to array for uniform handling const parts = Array.isArray(unifiedCharDefinition) ? unifiedCharDefinition : [unifiedCharDefinition]; for (const part of parts) { - drawDefinitionPart(ctx, part, xOffset, yOffset, deviceCellWidth, deviceCellHeight, deviceCharWidth, deviceCharHeight, fontSize, devicePixelRatio, backgroundColor, variantOffset); + drawDefinitionPart(ctx, part, xOffset, yOffset, deviceCellWidth, deviceCellHeight, deviceCharWidth, deviceCharHeight, fontSize, devicePixelRatio, logService, backgroundColor, variantOffset); } return true; } @@ -49,6 +51,7 @@ function drawDefinitionPart( deviceCharHeight: number, fontSize: number, devicePixelRatio: number, + logService: ILogService, backgroundColor?: string, variantOffset: number = 0 ): void { @@ -79,7 +82,7 @@ function drawDefinitionPart( drawPatternChar(ctx, part.data, drawXOffset, drawYOffset, drawWidth, drawHeight, variantOffset); break; case CustomGlyphDefinitionType.PATH_FUNCTION: - drawPathFunctionCharacter(ctx, part.data, drawXOffset, drawYOffset, drawWidth, drawHeight, devicePixelRatio, part.strokeWidth); + drawPathFunctionCharacter(ctx, part.data, drawXOffset, drawYOffset, drawWidth, drawHeight, devicePixelRatio, logService, part.strokeWidth); break; case CustomGlyphDefinitionType.PATH: drawPathDefinitionCharacter(ctx, part.data, drawXOffset, drawYOffset, drawWidth, drawHeight, devicePixelRatio, part.strokeWidth); @@ -88,7 +91,7 @@ function drawDefinitionPart( drawPathNegativeDefinitionCharacter(ctx, part.data, drawXOffset, drawYOffset, drawWidth, drawHeight, devicePixelRatio, backgroundColor); break; case CustomGlyphDefinitionType.VECTOR_SHAPE: - drawVectorShape(ctx, part.data, drawXOffset, drawYOffset, drawWidth, drawHeight, fontSize, devicePixelRatio); + drawVectorShape(ctx, part.data, drawXOffset, drawYOffset, drawWidth, drawHeight, fontSize, devicePixelRatio, logService); break; case CustomGlyphDefinitionType.BRAILLE: drawBrailleCharacter(ctx, part.data, drawXOffset, drawYOffset, drawWidth, drawHeight); @@ -510,6 +513,7 @@ function drawPathFunctionCharacter( deviceCellWidth: number, deviceCellHeight: number, devicePixelRatio: number, + logService: ILogService, strokeWidth?: number ): void { ctx.save(); @@ -536,7 +540,7 @@ function drawPathFunctionCharacter( } const f = svgToCanvasInstructionMap[type]; if (!f) { - console.error(`Could not find drawing instructions for "${type}"`); + logService.error(`Could not find drawing instructions for "${type}"`); continue; } const args: string[] = instruction.substring(1).split(','); @@ -598,7 +602,8 @@ function drawVectorShape( deviceCellWidth: number, deviceCellHeight: number, fontSize: number, - devicePixelRatio: number + devicePixelRatio: number, + logService: ILogService ): void { // Clip the cell to make sure drawing doesn't occur beyond bounds const clipRegion = new Path2D(); @@ -619,7 +624,7 @@ function drawVectorShape( } const f = svgToCanvasInstructionMap[type]; if (!f) { - console.error(`Could not find drawing instructions for "${type}"`); + logService.error(`Could not find drawing instructions for "${type}"`); continue; } const args: string[] = instruction.substring(1).split(','); From f757c965ee0abd33dbc9577ef60a52748f3bdfd9 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 27 May 2026 15:38:58 +0000 Subject: [PATCH 26/47] fix(addon-webgl): keep VAO WebGL types non-nullable Co-authored-by: Daniel Imms --- addons/addon-webgl/src/GlyphRenderer.ts | 6 +++--- addons/addon-webgl/src/RectangleRenderer.ts | 6 +++--- addons/addon-webgl/src/Types.ts | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/addons/addon-webgl/src/GlyphRenderer.ts b/addons/addon-webgl/src/GlyphRenderer.ts index 7a7e98496..cfa156037 100644 --- a/addons/addon-webgl/src/GlyphRenderer.ts +++ b/addons/addon-webgl/src/GlyphRenderer.ts @@ -136,9 +136,9 @@ export class GlyphRenderer extends Disposable { this._textureLocation = throwIfFalsy(gl.getUniformLocation(this._program, 'u_texture')); // Create and set the vertex array object - this._vertexArrayObject = gl.createVertexArray(); - this._register(toDisposable(() => gl.deleteVertexArray(this._vertexArrayObject))); - gl.bindVertexArray(this._vertexArrayObject); + const vertexArrayObject = this._vertexArrayObject = gl.createVertexArray(); + this._register(toDisposable(() => gl.deleteVertexArray(vertexArrayObject))); + gl.bindVertexArray(vertexArrayObject); // Setup a_unitquad, this defines the 4 vertices of a rectangle const unitQuadVertices = new Float32Array([0, 0, 1, 0, 0, 1, 1, 1]); diff --git a/addons/addon-webgl/src/RectangleRenderer.ts b/addons/addon-webgl/src/RectangleRenderer.ts index b2225de3a..55926ed14 100644 --- a/addons/addon-webgl/src/RectangleRenderer.ts +++ b/addons/addon-webgl/src/RectangleRenderer.ts @@ -102,9 +102,9 @@ export class RectangleRenderer extends Disposable { this._projectionLocation = throwIfFalsy(gl.getUniformLocation(this._program, 'u_projection')); // Create and set the vertex array object - this._vertexArrayObject = gl.createVertexArray(); - this._register(toDisposable(() => gl.deleteVertexArray(this._vertexArrayObject))); - gl.bindVertexArray(this._vertexArrayObject); + const vertexArrayObject = this._vertexArrayObject = gl.createVertexArray(); + this._register(toDisposable(() => gl.deleteVertexArray(vertexArrayObject))); + gl.bindVertexArray(vertexArrayObject); // Setup a_unitquad, this defines the 4 vertices of a rectangle const unitQuadVertices = new Float32Array([0, 0, 1, 0, 0, 1, 1, 1]); diff --git a/addons/addon-webgl/src/Types.ts b/addons/addon-webgl/src/Types.ts index b3c51e842..294582e37 100644 --- a/addons/addon-webgl/src/Types.ts +++ b/addons/addon-webgl/src/Types.ts @@ -28,8 +28,8 @@ export interface ICursorRenderModel { export interface IWebGL2RenderingContext extends WebGLRenderingContext { vertexAttribDivisor(index: number, divisor: number): void; createVertexArray(): IWebGLVertexArrayObject; - deleteVertexArray(vao: IWebGLVertexArrayObject | null): void; - bindVertexArray(vao: IWebGLVertexArrayObject | null): void; + deleteVertexArray(vao: IWebGLVertexArrayObject): void; + bindVertexArray(vao: IWebGLVertexArrayObject): void; drawElementsInstanced(mode: number, count: number, type: number, offset: number, instanceCount: number): void; } From 534ce1365b1570bd0b8112dbc9892ea058be3ae0 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 27 May 2026 15:40:10 +0000 Subject: [PATCH 27/47] Clear WebGL context-loss timeout on WebglRenderer dispose Fixes #5919 Co-authored-by: Daniel Imms --- addons/addon-webgl/src/WebglRenderer.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/addons/addon-webgl/src/WebglRenderer.ts b/addons/addon-webgl/src/WebglRenderer.ts index 500308bd1..8988d9fc8 100644 --- a/addons/addon-webgl/src/WebglRenderer.ts +++ b/addons/addon-webgl/src/WebglRenderer.ts @@ -158,6 +158,8 @@ export class WebglRenderer extends Disposable implements IRenderer { this._isAttached = this._core.screenElement!.isConnected; this._register(toDisposable(() => { + clearTimeout(this._contextRestorationTimeout); + this._contextRestorationTimeout = undefined; for (const l of this._renderLayers) { l.dispose(); } From 090b77f354e684fc6434029656bc37aafc4dd0d9 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 27 May 2026 15:41:00 +0000 Subject: [PATCH 28/47] Replace custom clone utility with structuredClone Remove common/Clone.ts and its tests. Use the native structuredClone API for cloning default modes, options, and test fixtures. Fixes #5911 Co-authored-by: Daniel Imms --- src/common/Clone.test.ts | 129 ----------------------------- src/common/Clone.ts | 23 ----- src/common/InputHandler.test.ts | 13 ++- src/common/TestUtils.test.ts | 3 +- src/common/services/CoreService.ts | 9 +- 5 files changed, 11 insertions(+), 166 deletions(-) delete mode 100644 src/common/Clone.test.ts delete mode 100644 src/common/Clone.ts diff --git a/src/common/Clone.test.ts b/src/common/Clone.test.ts deleted file mode 100644 index 5fbe4bf9b..000000000 --- a/src/common/Clone.test.ts +++ /dev/null @@ -1,129 +0,0 @@ -/** - * Copyright (c) 2016 The xterm.js authors. All rights reserved. - * @license MIT - */ - -import { assert } from 'chai'; -import { clone } from 'common/Clone'; - -describe('clone', () => { - it('should clone simple objects', () => { - const test = { - a: 1, - b: 2 - }; - - assert.deepEqual(clone(test), { a: 1, b: 2 }); - }); - - it('should clone nested objects', () => { - const test = { - bar: { - a: 1, - b: 2, - c: { - foo: 'bar' - } - } - }; - - assert.deepEqual(clone(test), { - bar: { - a: 1, - b: 2, - c: { - foo: 'bar' - } - } - }); - }); - - it('should clone array values', () => { - const test = { - a: [1, 2, 3], - b: [1, null, 'test', { foo: 'bar' }] - }; - - assert.deepEqual(clone(test), { - a: [1, 2, 3], - b: [1, null, 'test', { foo: 'bar' }] - }); - }); - - it('should stop mutation from occuring on the original object', () => { - const test = { - a: 1, - b: 2, - c: { - foo: 'bar' - } - }; - - const cloned = clone(test); - - test.a = 5; - test.c.foo = 'barbaz'; - - assert.deepEqual(cloned, { - a: 1, - b: 2, - c: { - foo: 'bar' - } - }); - }); - - it('should clone to a maximum depth of 5 by default', () => { - const test = { - a: { - b: { - c: { - d: { - e: { - f: 'foo' - } - } - } - } - } - }; - - const cloned = clone(test); - - test.a.b.c.d.e.f = 'bar'; - - // The values at a greater depth then 5 should not be cloned - assert.equal((cloned as any).a.b.c.d.e.f, 'bar'); - }); - - it('should allow an optional maximum depth to be set', () => { - const test = { - a: { - b: { - c: 'foo' - } - } - }; - - const cloned = clone(test, 2); - - test.a.b.c = 'bar'; - - // The values at a greater depth then 2 should not be cloned - assert.equal((cloned as any).a.b.c, 'bar'); - }); - - it('should not throw when cloning a recursive reference', () => { - const test = { - a: { - b: { - c: {} - } - } - }; - - test.a.b.c = test; - - assert.doesNotThrow(() => clone(test)); - }); -}); diff --git a/src/common/Clone.ts b/src/common/Clone.ts deleted file mode 100644 index 37821fe02..000000000 --- a/src/common/Clone.ts +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Copyright (c) 2016 The xterm.js authors. All rights reserved. - * @license MIT - */ - -/* - * A simple utility for cloning values - */ -export function clone(val: T, depth: number = 5): T { - if (typeof val !== 'object') { - return val; - } - - // If we're cloning an array, use an array as the base, otherwise use an object - const clonedObject: any = Array.isArray(val) ? [] : {}; - - for (const key in val) { - // Recursively clone eack item unless we're at the maximum depth - clonedObject[key] = depth <= 1 ? val[key] : (val[key] && clone(val[key], depth - 1)); - } - - return clonedObject as T; -} diff --git a/src/common/InputHandler.test.ts b/src/common/InputHandler.test.ts index f9da81a25..5c59b062b 100644 --- a/src/common/InputHandler.test.ts +++ b/src/common/InputHandler.test.ts @@ -14,7 +14,6 @@ import { Params } from 'common/parser/Params'; import { MockCoreService, MockBufferService, MockOptionsService, MockLogService, MockMouseStateService, MockCharsetService, MockUnicodeService, MockOscLinkService, extendedAttributes } from 'common/TestUtils.test'; import { IBufferService, ICoreService, type IOscLinkService } from 'common/services/Services'; import { DEFAULT_OPTIONS } from 'common/services/OptionsService'; -import { clone } from 'common/Clone'; import { BufferService } from 'common/services/BufferService'; import { CoreService } from 'common/services/CoreService'; import { OscLinkService } from 'common/services/OscLinkService'; @@ -227,32 +226,32 @@ describe('InputHandler', () => { assert.equal(coreService.decPrivateModes.cursorStyle, undefined); assert.equal(coreService.decPrivateModes.cursorBlink, undefined); - optionsService.options = clone(DEFAULT_OPTIONS); + optionsService.options = structuredClone(DEFAULT_OPTIONS); inputHandler.setCursorStyle(Params.fromArray([1])); assert.equal(coreService.decPrivateModes.cursorStyle, 'block'); assert.equal(coreService.decPrivateModes.cursorBlink, true); - optionsService.options = clone(DEFAULT_OPTIONS); + optionsService.options = structuredClone(DEFAULT_OPTIONS); inputHandler.setCursorStyle(Params.fromArray([2])); assert.equal(coreService.decPrivateModes.cursorStyle, 'block'); assert.equal(coreService.decPrivateModes.cursorBlink, false); - optionsService.options = clone(DEFAULT_OPTIONS); + optionsService.options = structuredClone(DEFAULT_OPTIONS); inputHandler.setCursorStyle(Params.fromArray([3])); assert.equal(coreService.decPrivateModes.cursorStyle, 'underline'); assert.equal(coreService.decPrivateModes.cursorBlink, true); - optionsService.options = clone(DEFAULT_OPTIONS); + optionsService.options = structuredClone(DEFAULT_OPTIONS); inputHandler.setCursorStyle(Params.fromArray([4])); assert.equal(coreService.decPrivateModes.cursorStyle, 'underline'); assert.equal(coreService.decPrivateModes.cursorBlink, false); - optionsService.options = clone(DEFAULT_OPTIONS); + optionsService.options = structuredClone(DEFAULT_OPTIONS); inputHandler.setCursorStyle(Params.fromArray([5])); assert.equal(coreService.decPrivateModes.cursorStyle, 'bar'); assert.equal(coreService.decPrivateModes.cursorBlink, true); - optionsService.options = clone(DEFAULT_OPTIONS); + optionsService.options = structuredClone(DEFAULT_OPTIONS); inputHandler.setCursorStyle(Params.fromArray([6])); assert.equal(coreService.decPrivateModes.cursorStyle, 'bar'); assert.equal(coreService.decPrivateModes.cursorBlink, false); diff --git a/src/common/TestUtils.test.ts b/src/common/TestUtils.test.ts index 5940fa659..69c2d499d 100644 --- a/src/common/TestUtils.test.ts +++ b/src/common/TestUtils.test.ts @@ -5,7 +5,6 @@ import { IBufferService, ICoreService, ILogService, IOptionsService, ITerminalOptions, IMouseStateService, ICharsetService, UnicodeCharProperties, UnicodeCharWidth, IUnicodeService, IUnicodeVersionProvider, LogLevelEnum, IDecorationService, IInternalDecoration, IOscLinkService, type IBufferResizeEvent } from 'common/services/Services'; import { UnicodeService } from 'common/services/UnicodeService'; -import { clone } from 'common/Clone'; import { DEFAULT_OPTIONS } from 'common/services/OptionsService'; import { IBufferSet, IBuffer } from 'common/buffer/Types'; import { BufferSet } from 'common/buffer/BufferSet'; @@ -157,7 +156,7 @@ export class MockLogService implements ILogService { export class MockOptionsService implements IOptionsService { public serviceBrand: any; - public readonly rawOptions: Required = clone(DEFAULT_OPTIONS); + public readonly rawOptions: Required = structuredClone(DEFAULT_OPTIONS); public options: Required = this.rawOptions; public onOptionChange: IEvent = new Emitter().event; constructor(testOptions?: Partial) { diff --git a/src/common/services/CoreService.ts b/src/common/services/CoreService.ts index d8962ec88..f5094f5a9 100644 --- a/src/common/services/CoreService.ts +++ b/src/common/services/CoreService.ts @@ -3,7 +3,6 @@ * @license MIT */ -import { clone } from 'common/Clone'; import { Disposable } from 'common/Lifecycle'; import { IDecPrivateModes, IKittyKeyboardState, IModes } from 'common/Types'; import { IBufferService, ICoreService, ILogService, IOptionsService } from 'common/services/Services'; @@ -61,14 +60,14 @@ export class CoreService extends Disposable implements ICoreService { ) { super(); this.isCursorInitialized = _optionsService.rawOptions.showCursorImmediately ?? false; - this.modes = clone(DEFAULT_MODES); - this.decPrivateModes = clone(DEFAULT_DEC_PRIVATE_MODES); + this.modes = structuredClone(DEFAULT_MODES); + this.decPrivateModes = structuredClone(DEFAULT_DEC_PRIVATE_MODES); this.kittyKeyboard = DEFAULT_KITTY_KEYBOARD_STATE(); } public reset(): void { - this.modes = clone(DEFAULT_MODES); - this.decPrivateModes = clone(DEFAULT_DEC_PRIVATE_MODES); + this.modes = structuredClone(DEFAULT_MODES); + this.decPrivateModes = structuredClone(DEFAULT_DEC_PRIVATE_MODES); this.kittyKeyboard = DEFAULT_KITTY_KEYBOARD_STATE(); } From 30b42838fd0e0674c86970df72f57b314c30bc0b Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 27 May 2026 16:27:26 +0000 Subject: [PATCH 29/47] Extract StringBuilder and LimitedStringBuilder with unit tests Replace PayloadStringBuffer with LimitedStringBuilder from common. Add thorough StringBuilder and LimitedStringBuilder test coverage. Co-authored-by: Daniel Imms --- src/common/StringBuilder.test.ts | 143 +++++++++++++++++++++++ src/common/StringBuilder.ts | 67 +++++++++++ src/common/parser/ApcParser.ts | 6 +- src/common/parser/DcsParser.ts | 6 +- src/common/parser/OscParser.ts | 6 +- src/common/parser/PayloadStringBuffer.ts | 34 ------ 6 files changed, 219 insertions(+), 43 deletions(-) create mode 100644 src/common/StringBuilder.test.ts create mode 100644 src/common/StringBuilder.ts delete mode 100644 src/common/parser/PayloadStringBuffer.ts diff --git a/src/common/StringBuilder.test.ts b/src/common/StringBuilder.test.ts new file mode 100644 index 000000000..1794de0f7 --- /dev/null +++ b/src/common/StringBuilder.test.ts @@ -0,0 +1,143 @@ +/** + * Copyright (c) 2026 The xterm.js authors. All rights reserved. + * @license MIT + */ + +import { assert } from 'chai'; +import { LimitedStringBuilder, StringBuilder } from 'common/StringBuilder'; + +describe('StringBuilder', () => { + it('should start empty', () => { + const builder = new StringBuilder(); + assert.strictEqual(builder.length, 0); + assert.strictEqual(builder.toString(), ''); + }); + + it('should append a single chunk', () => { + const builder = new StringBuilder(); + builder.append('hello'); + assert.strictEqual(builder.length, 5); + assert.strictEqual(builder.toString(), 'hello'); + }); + + it('should join multiple chunks in order', () => { + const builder = new StringBuilder(); + builder.append('foo'); + builder.append('bar'); + builder.append('baz'); + assert.strictEqual(builder.length, 9); + assert.strictEqual(builder.toString(), 'foobarbaz'); + }); + + it('should handle empty chunks', () => { + const builder = new StringBuilder(); + builder.append(''); + builder.append('a'); + builder.append(''); + assert.strictEqual(builder.length, 1); + assert.strictEqual(builder.toString(), 'a'); + }); + + it('should reset accumulated data', () => { + const builder = new StringBuilder(); + builder.append('hello'); + builder.reset(); + assert.strictEqual(builder.length, 0); + assert.strictEqual(builder.toString(), ''); + }); + + it('should allow appending after reset', () => { + const builder = new StringBuilder(); + builder.append('old'); + builder.reset(); + builder.append('new'); + assert.strictEqual(builder.toString(), 'new'); + }); + + it('should accumulate many small chunks without quadratic concatenation', () => { + const builder = new StringBuilder(); + const chunk = 'x'; + const count = 10000; + for (let i = 0; i < count; i++) { + builder.append(chunk); + } + assert.strictEqual(builder.length, count); + assert.strictEqual(builder.toString(), 'x'.repeat(count)); + }); +}); + +describe('LimitedStringBuilder', () => { + it('should expose the configured limit', () => { + const builder = new LimitedStringBuilder(42); + assert.strictEqual(builder.limit, 42); + }); + + it('should start empty', () => { + const builder = new LimitedStringBuilder(10); + assert.strictEqual(builder.length, 0); + assert.strictEqual(builder.toString(), ''); + }); + + it('should accept data up to the limit', () => { + const builder = new LimitedStringBuilder(10); + assert.strictEqual(builder.append('12345'), false); + assert.strictEqual(builder.append('67890'), false); + assert.strictEqual(builder.length, 10); + assert.strictEqual(builder.toString(), '1234567890'); + }); + + it('should accept a single chunk exactly at the limit', () => { + const builder = new LimitedStringBuilder(5); + assert.strictEqual(builder.append('abcde'), false); + assert.strictEqual(builder.length, 5); + assert.strictEqual(builder.toString(), 'abcde'); + }); + + it('should reject data exceeding the limit and clear the buffer', () => { + const builder = new LimitedStringBuilder(5); + builder.append('abc'); + assert.strictEqual(builder.append('def'), true); + assert.strictEqual(builder.length, 0); + assert.strictEqual(builder.toString(), ''); + }); + + it('should reject a single chunk larger than the limit', () => { + const builder = new LimitedStringBuilder(3); + assert.strictEqual(builder.append('toolong'), true); + assert.strictEqual(builder.length, 0); + assert.strictEqual(builder.toString(), ''); + }); + + it('should allow appending again after reset following a limit breach', () => { + const builder = new LimitedStringBuilder(3); + assert.strictEqual(builder.append('abcd'), true); + builder.reset(); + assert.strictEqual(builder.append('ab'), false); + assert.strictEqual(builder.toString(), 'ab'); + }); + + it('should accumulate many chunks before hitting the limit', () => { + const limit = 100; + const builder = new LimitedStringBuilder(limit); + const chunk = 'A'; + for (let i = 0; i < limit; i++) { + assert.strictEqual(builder.append(chunk), false); + } + assert.strictEqual(builder.toString(), 'A'.repeat(limit)); + assert.strictEqual(builder.append('B'), true); + assert.strictEqual(builder.toString(), ''); + }); + + it('should reject when limit is zero and any data is appended', () => { + const builder = new LimitedStringBuilder(0); + assert.strictEqual(builder.append('a'), true); + assert.strictEqual(builder.length, 0); + }); + + it('should allow zero-length appends at the limit', () => { + const builder = new LimitedStringBuilder(0); + assert.strictEqual(builder.append(''), false); + assert.strictEqual(builder.length, 0); + assert.strictEqual(builder.toString(), ''); + }); +}); diff --git a/src/common/StringBuilder.ts b/src/common/StringBuilder.ts new file mode 100644 index 000000000..d9184ce31 --- /dev/null +++ b/src/common/StringBuilder.ts @@ -0,0 +1,67 @@ +/** + * Copyright (c) 2026 The xterm.js authors. All rights reserved. + * @license MIT + */ + +/** + * Accumulates string data from multiple chunks without O(n²) string concatenation. + */ +export class StringBuilder { + private _chunks: string[] = []; + private _length = 0; + + public get length(): number { + return this._length; + } + + public reset(): void { + this._chunks.length = 0; + this._length = 0; + } + + public append(chunk: string): void { + this._chunks.push(chunk); + this._length += chunk.length; + } + + public toString(): string { + return this._chunks.join(''); + } +} + +/** + * String builder that rejects payloads larger than a fixed limit. + */ +export class LimitedStringBuilder { + private readonly _builder = new StringBuilder(); + + constructor(private readonly _limit: number) { } + + public get length(): number { + return this._builder.length; + } + + public get limit(): number { + return this._limit; + } + + public reset(): void { + this._builder.reset(); + } + + /** + * @returns true if the limit was exceeded (buffer is cleared in that case) + */ + public append(chunk: string): boolean { + this._builder.append(chunk); + if (this._builder.length > this._limit) { + this._builder.reset(); + return true; + } + return false; + } + + public toString(): string { + return this._builder.toString(); + } +} diff --git a/src/common/parser/ApcParser.ts b/src/common/parser/ApcParser.ts index d3f5b8e2e..4e647d037 100644 --- a/src/common/parser/ApcParser.ts +++ b/src/common/parser/ApcParser.ts @@ -7,7 +7,7 @@ import { IApcHandler, IHandlerCollection, ApcFallbackHandlerType, IApcParser, IS import { ParserConstants } from 'common/parser/Constants'; import { utf32ToString } from 'common/input/TextDecoder'; import { IDisposable } from 'common/Types'; -import { PayloadStringBuffer } from 'common/parser/PayloadStringBuffer'; +import { LimitedStringBuilder } from 'common/StringBuilder'; const EMPTY_HANDLERS: IApcHandler[] = []; @@ -154,7 +154,7 @@ export class ApcParser implements IApcParser { export class ApcHandler implements IApcHandler { private static _payloadLimit = ParserConstants.PAYLOAD_LIMIT; - private _data = new PayloadStringBuffer(); + private _data = new LimitedStringBuilder(ApcHandler._payloadLimit); private _hitLimit: boolean = false; constructor(private _handler: (data: string) => boolean | Promise) { } @@ -168,7 +168,7 @@ export class ApcHandler implements IApcHandler { if (this._hitLimit) { return; } - if (this._data.add(utf32ToString(data, start, end), ApcHandler._payloadLimit)) { + if (this._data.append(utf32ToString(data, start, end))) { this._hitLimit = true; } } diff --git a/src/common/parser/DcsParser.ts b/src/common/parser/DcsParser.ts index be2fa39d5..25c22b8dd 100644 --- a/src/common/parser/DcsParser.ts +++ b/src/common/parser/DcsParser.ts @@ -8,7 +8,7 @@ import { IDcsHandler, IParams, IHandlerCollection, IDcsParser, DcsFallbackHandle import { utf32ToString } from 'common/input/TextDecoder'; import { Params } from 'common/parser/Params'; import { ParserConstants } from 'common/parser/Constants'; -import { PayloadStringBuffer } from 'common/parser/PayloadStringBuffer'; +import { LimitedStringBuilder } from 'common/StringBuilder'; const EMPTY_HANDLERS: IDcsHandler[] = []; @@ -141,7 +141,7 @@ EMPTY_PARAMS.addParam(0); export class DcsHandler implements IDcsHandler { private static _payloadLimit = ParserConstants.PAYLOAD_LIMIT; - private _data = new PayloadStringBuffer(); + private _data = new LimitedStringBuilder(DcsHandler._payloadLimit); private _params: IParams = EMPTY_PARAMS; private _hitLimit: boolean = false; @@ -161,7 +161,7 @@ export class DcsHandler implements IDcsHandler { if (this._hitLimit) { return; } - if (this._data.add(utf32ToString(data, start, end), DcsHandler._payloadLimit)) { + if (this._data.append(utf32ToString(data, start, end))) { this._hitLimit = true; } } diff --git a/src/common/parser/OscParser.ts b/src/common/parser/OscParser.ts index 6a17af817..3e05f2298 100644 --- a/src/common/parser/OscParser.ts +++ b/src/common/parser/OscParser.ts @@ -7,7 +7,7 @@ import { IOscHandler, IHandlerCollection, OscFallbackHandlerType, IOscParser, IS import { OscState, ParserConstants } from 'common/parser/Constants'; import { utf32ToString } from 'common/input/TextDecoder'; import { IDisposable } from 'common/Types'; -import { PayloadStringBuffer } from 'common/parser/PayloadStringBuffer'; +import { LimitedStringBuilder } from 'common/StringBuilder'; const EMPTY_HANDLERS: IOscHandler[] = []; @@ -195,7 +195,7 @@ export class OscParser implements IOscParser { export class OscHandler implements IOscHandler { private static _payloadLimit = ParserConstants.PAYLOAD_LIMIT; - private _data = new PayloadStringBuffer(); + private _data = new LimitedStringBuilder(OscHandler._payloadLimit); private _hitLimit: boolean = false; constructor(private _handler: (data: string) => boolean | Promise) { } @@ -209,7 +209,7 @@ export class OscHandler implements IOscHandler { if (this._hitLimit) { return; } - if (this._data.add(utf32ToString(data, start, end), OscHandler._payloadLimit)) { + if (this._data.append(utf32ToString(data, start, end))) { this._hitLimit = true; } } diff --git a/src/common/parser/PayloadStringBuffer.ts b/src/common/parser/PayloadStringBuffer.ts deleted file mode 100644 index 9caef25ea..000000000 --- a/src/common/parser/PayloadStringBuffer.ts +++ /dev/null @@ -1,34 +0,0 @@ -/** - * Copyright (c) 2026 The xterm.js authors. All rights reserved. - * @license MIT - */ - -/** - * Accumulates string payloads from multiple chunks without O(n²) string concatenation. - */ -export class PayloadStringBuffer { - private _chunks: string[] = []; - private _length = 0; - - public reset(): void { - this._chunks.length = 0; - this._length = 0; - } - - /** - * @returns true if the payload limit was exceeded (buffer is cleared in that case) - */ - public add(chunk: string, limit: number): boolean { - this._chunks.push(chunk); - this._length += chunk.length; - if (this._length > limit) { - this.reset(); - return true; - } - return false; - } - - public toString(): string { - return this._chunks.join(''); - } -} From 42facac2d98fd4354632f7f4f3e7758174679b47 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 27 May 2026 18:16:51 +0000 Subject: [PATCH 30/47] Use StringBuilder in BufferLine.translateToString MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace O(n²) string concatenation in the translate loop with StringBuilder.append, matching the pattern used elsewhere in the project. Fixes #5939 Co-authored-by: Daniel Imms --- src/common/buffer/BufferLine.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/common/buffer/BufferLine.ts b/src/common/buffer/BufferLine.ts index be200f112..69cde7e8f 100644 --- a/src/common/buffer/BufferLine.ts +++ b/src/common/buffer/BufferLine.ts @@ -8,6 +8,7 @@ import { AttributeData } from 'common/buffer/AttributeData'; import { CellData } from 'common/buffer/CellData'; import { Attributes, BgFlags, CHAR_DATA_ATTR_INDEX, CHAR_DATA_CHAR_INDEX, CHAR_DATA_WIDTH_INDEX, Content, NULL_CELL_CHAR, NULL_CELL_CODE, NULL_CELL_WIDTH, WHITESPACE_CELL_CHAR } from 'common/buffer/Constants'; import { stringFromCodePoint } from 'common/input/TextDecoder'; +import { StringBuilder } from 'common/StringBuilder'; // Buffer memory layout: // @@ -570,12 +571,12 @@ export class BufferLine implements IBufferLine { if (outColumns) { outColumns.length = 0; } - let result = ''; + const resultBuilder = new StringBuilder(); while (startCol < endCol) { const content = this._data[startCol * Constants.CELL_INDICIES + Cell.CONTENT]; const cp = content & Content.CODEPOINT_MASK; const chars = (content & Content.IS_COMBINED_MASK) ? this._combined[startCol] : (cp) ? stringFromCodePoint(cp) : WHITESPACE_CELL_CHAR; - result += chars; + resultBuilder.append(chars); if (outColumns) { for (let i = 0; i < chars.length; ++i) { outColumns.push(startCol); @@ -586,6 +587,7 @@ export class BufferLine implements IBufferLine { if (outColumns) { outColumns.push(startCol); } + const result = resultBuilder.toString(); if (isCanonicalRequest) { const cacheEntry = this._getStringCacheEntry(true)!; cacheEntry.value = result; From bcb4e5298b64cb9bdd67f1841460d516a446d186 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 27 May 2026 18:16:54 +0000 Subject: [PATCH 31/47] Fix duplicate buildNumber guard in Windows ConPTY logic The windowsPty option change handler checked buildNumber twice instead of requiring both backend and buildNumber to be defined. This aligns with Buffer.ts and ensures ConPTY wrapping heuristics are evaluated only when both fields are present. Fixes #5934 Co-authored-by: Daniel Imms --- src/common/CoreTerminal.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/CoreTerminal.ts b/src/common/CoreTerminal.ts index 9bd7faaf5..80168aa65 100644 --- a/src/common/CoreTerminal.ts +++ b/src/common/CoreTerminal.ts @@ -263,7 +263,7 @@ export abstract class CoreTerminal extends Disposable implements ICoreTerminal { private _handleWindowsPtyOptionChange(): void { let value = false; const windowsPty = this.optionsService.rawOptions.windowsPty; - if (windowsPty && windowsPty.buildNumber !== undefined && windowsPty.buildNumber !== undefined) { + if (windowsPty && windowsPty.backend !== undefined && windowsPty.buildNumber !== undefined) { value = !!(windowsPty.backend === 'conpty' && windowsPty.buildNumber < 21376); } if (value) { From 182ff2f63571af0efe1551b3a873cdf5e21707a6 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 27 May 2026 18:17:08 +0000 Subject: [PATCH 32/47] Remove duplicate Object.defineProperty loop in headless Terminal The constructor defined property getters/setters twice on _publicOptions; the first block was immediately overwritten by the second. Align with the browser public Terminal implementation. Fixes #5935 Co-authored-by: Daniel Imms --- src/headless/public/Terminal.ts | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/headless/public/Terminal.ts b/src/headless/public/Terminal.ts index 0d4e6e93b..093ae7909 100644 --- a/src/headless/public/Terminal.ts +++ b/src/headless/public/Terminal.ts @@ -40,15 +40,6 @@ export class Terminal extends Disposable implements ITerminalApi { }; for (const propName in this._core.options) { - Object.defineProperty(this._publicOptions, propName, { - get: () => { - return this._core.options[propName]; - }, - set: (value: any) => { - this._checkReadonlyOptions(propName); - this._core.options[propName] = value; - } - }); const desc = { get: getter.bind(this, propName), set: setter.bind(this, propName) From 17e3b84bcce9c9fce68a114cbec8dd8290bee796 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 27 May 2026 18:17:17 +0000 Subject: [PATCH 33/47] perf: use single loop for cellsNeeded sum in BufferReflow Replace map().reduce() with one accumulator loop in reflowSmallerGetNewLineLengths to avoid allocating an intermediate array when summing wrapped line trimmed lengths. Fixes #5942 Co-authored-by: Daniel Imms --- src/common/buffer/BufferReflow.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/common/buffer/BufferReflow.ts b/src/common/buffer/BufferReflow.ts index 44aa0976f..45a194711 100644 --- a/src/common/buffer/BufferReflow.ts +++ b/src/common/buffer/BufferReflow.ts @@ -178,7 +178,10 @@ export function reflowLargerApplyNewLayout(lines: CircularList, new */ export function reflowSmallerGetNewLineLengths(wrappedLines: BufferLine[], oldCols: number, newCols: number): number[] { const newLineLengths: number[] = []; - const cellsNeeded = wrappedLines.map((l, i) => getWrappedLineTrimmedLength(wrappedLines, i, oldCols)).reduce((p, c) => p + c); + let cellsNeeded = 0; + for (let i = 0; i < wrappedLines.length; i++) { + cellsNeeded += getWrappedLineTrimmedLength(wrappedLines, i, oldCols); + } // Use srcCol and srcLine to find the new wrapping point, use that to get the cellsAvailable and // linesNeeded From ba33decb684608f75d0017714b11aefd3bc54b25 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 27 May 2026 18:18:01 +0000 Subject: [PATCH 34/47] Rename headless core addMarker to registerMarker Align headless Terminal core API with browser CoreBrowserTerminal and the public registerMarker naming. Fixes #5938 Co-authored-by: Daniel Imms --- src/headless/Terminal.ts | 2 +- src/headless/public/Terminal.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/headless/Terminal.ts b/src/headless/Terminal.ts index 2df76c61e..e53c00fbb 100644 --- a/src/headless/Terminal.ts +++ b/src/headless/Terminal.ts @@ -69,7 +69,7 @@ export class Terminal extends CoreTerminal { return this.buffer.markers; } - public addMarker(cursorYOffset: number): IMarker | undefined { + public registerMarker(cursorYOffset: number): IMarker | undefined { // Disallow markers on the alt buffer if (this.buffer !== this.buffers.normal) { return; diff --git a/src/headless/public/Terminal.ts b/src/headless/public/Terminal.ts index 0d4e6e93b..bf673ee08 100644 --- a/src/headless/public/Terminal.ts +++ b/src/headless/public/Terminal.ts @@ -141,7 +141,7 @@ export class Terminal extends Disposable implements ITerminalApi { } public registerMarker(cursorYOffset: number = 0): IMarker | undefined { this._verifyIntegers(cursorYOffset); - return this._core.addMarker(cursorYOffset); + return this._core.registerMarker(cursorYOffset); } public addMarker(cursorYOffset: number): IMarker | undefined { return this.registerMarker(cursorYOffset); From 41dfdba2cfcbae6484d3ea23f96ffc16848ce025 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 27 May 2026 18:18:13 +0000 Subject: [PATCH 35/47] Avoid spread when merging decoration line buckets Replace existing.push(...bucket) with an indexed loop in _mergeLineBucket to avoid intermediate array allocations during buffer trim/insert/delete re-indexing. Reuse _mergeLineBucket in _handleBufferLinesTrim for consistency. Fixes #5941 Co-authored-by: Daniel Imms --- src/common/services/DecorationService.ts | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/common/services/DecorationService.ts b/src/common/services/DecorationService.ts index a582c18af..95875e54a 100644 --- a/src/common/services/DecorationService.ts +++ b/src/common/services/DecorationService.ts @@ -225,12 +225,7 @@ export class DecorationLineCache extends Disposable { if (newLine < 0) { continue; } - const existing = newMap.get(newLine); - if (existing) { - existing.push(...bucket); - } else { - newMap.set(newLine, bucket.slice()); - } + this._mergeLineBucket(newMap, newLine, bucket); } this._decorationsByLine.clear(); for (const [line, bucket] of newMap) { @@ -254,7 +249,9 @@ export class DecorationLineCache extends Disposable { private _mergeLineBucket(newMap: Map, line: number, bucket: IInternalDecoration[]): void { const existing = newMap.get(line); if (existing) { - existing.push(...bucket); + for (let i = 0, len = bucket.length; i < len; i++) { + existing.push(bucket[i]); + } } else { newMap.set(line, bucket.slice()); } From d91045dc5f31d14a7c0e3ce1fb66618a9baa75d6 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 27 May 2026 18:18:22 +0000 Subject: [PATCH 36/47] Cancel WriteBuffer innerWrite timers on dispose Guard _innerWrite and public write methods after disposal so pending setTimeout continuations cannot run against a disposed terminal. Fixes #5936 Co-authored-by: Daniel Imms --- src/common/input/WriteBuffer.test.ts | 29 +++++++++++++++ src/common/input/WriteBuffer.ts | 53 ++++++++++++++++++++++++---- 2 files changed, 75 insertions(+), 7 deletions(-) diff --git a/src/common/input/WriteBuffer.test.ts b/src/common/input/WriteBuffer.test.ts index cca6bd0be..5340f918a 100644 --- a/src/common/input/WriteBuffer.test.ts +++ b/src/common/input/WriteBuffer.test.ts @@ -140,5 +140,34 @@ describe('WriteBuffer', () => { wb.flushSync(); assert.equal(parsed, 0); }); + it('dispose cancels scheduled innerWrite', done => { + wb.write('a'); + wb.dispose(); + setTimeout(() => { + assert.deepEqual(stack, []); + done(); + }, 20); + }); + it('dispose does not fire onWriteParsed for pending writes', done => { + let parsed = 0; + wb.onWriteParsed(() => parsed++); + wb.write('a'); + wb.dispose(); + setTimeout(() => { + assert.equal(parsed, 0); + done(); + }, 20); + }); + it('write after dispose is a no-op', done => { + wb.dispose(); + wb.write('a', () => { + assert.deepEqual(stack, []); + done(); + }); + setTimeout(() => { + assert.deepEqual(stack, []); + done(); + }, 20); + }); }); }); diff --git a/src/common/input/WriteBuffer.ts b/src/common/input/WriteBuffer.ts index 7c6d5d866..279dc7693 100644 --- a/src/common/input/WriteBuffer.ts +++ b/src/common/input/WriteBuffer.ts @@ -4,11 +4,10 @@ * @license MIT */ +import { TimeoutTimer } from 'common/Async'; import { Disposable } from 'common/Lifecycle'; import { Emitter } from 'common/Event'; -declare const setTimeout: (handler: () => void, timeout?: number) => void; - const enum Constants { /** * Safety watermark to avoid memory exhaustion and browser engine crash on fast data input. @@ -42,7 +41,9 @@ export class WriteBuffer extends Disposable { private _isSyncWriting = false; private _syncCalls = 0; private _didUserInput = false; + private _isDisposed = false; + private readonly _innerWriteTimer = this._register(new TimeoutTimer()); private readonly _onWriteParsed = this._register(new Emitter()); public readonly onWriteParsed = this._onWriteParsed.event; @@ -50,6 +51,18 @@ export class WriteBuffer extends Disposable { super(); } + public override dispose(): void { + if (this._isDisposed) { + return; + } + this._isDisposed = true; + this._writeBuffer.length = 0; + this._callbacks.length = 0; + this._pendingData = 0; + this._bufferOffset = 0; + super.dispose(); + } + public handleUserInput(): void { this._didUserInput = true; } @@ -63,6 +76,9 @@ export class WriteBuffer extends Disposable { * promises to resolve. */ public flushSync(): void { + if (this._isDisposed) { + return; + } // exit early if another sync write loop is active if (this._isSyncWriting) { return; @@ -95,6 +111,9 @@ export class WriteBuffer extends Disposable { * @deprecated Unreliable, to be removed soon. */ public writeSync(data: string | Uint8Array, maxSubsequentCalls?: number): void { + if (this._isDisposed) { + return; + } // stop writeSync recursions with maxSubsequentCalls argument // This is dangerous to use as it will lose the current data chunk // and return immediately. @@ -138,6 +157,9 @@ export class WriteBuffer extends Disposable { } public write(data: string | Uint8Array, callback?: () => void): void { + if (this._isDisposed) { + return; + } if (this._pendingData > Constants.DISCARD_WATERMARK) { throw new Error('write data discarded, use flow control to avoid losing data'); } @@ -158,7 +180,7 @@ export class WriteBuffer extends Disposable { return; } - setTimeout(() => this._innerWrite()); + this._scheduleInnerWrite(); } this._pendingData += data.length; @@ -194,7 +216,17 @@ export class WriteBuffer extends Disposable { * * Note, for pure sync code `lastTime` and `promiseResult` have no meaning. */ + private _scheduleInnerWrite(lastTime: number = 0, promiseResult: boolean = true): void { + if (this._isDisposed) { + return; + } + this._innerWriteTimer.cancelAndSet(() => this._innerWrite(lastTime, promiseResult), 0); + } + protected _innerWrite(lastTime: number = 0, promiseResult: boolean = true): void { + if (this._isDisposed) { + return; + } const startTime = lastTime || performance.now(); while (this._writeBuffer.length > this._bufferOffset) { const data = this._writeBuffer[this._bufferOffset]; @@ -223,9 +255,16 @@ export class WriteBuffer extends Disposable { * responsibility to slice hard work), but we can at least schedule a screen update as we * gain control. */ - const continuation: (r: boolean) => void = (r: boolean) => performance.now() - startTime >= Constants.WRITE_TIMEOUT_MS - ? setTimeout(() => this._innerWrite(0, r)) - : this._innerWrite(startTime, r); + const continuation: (r: boolean) => void = (r: boolean) => { + if (this._isDisposed) { + return; + } + if (performance.now() - startTime >= Constants.WRITE_TIMEOUT_MS) { + this._scheduleInnerWrite(0, r); + } else { + this._innerWrite(startTime, r); + } + }; /** * Optimization considerations: @@ -272,7 +311,7 @@ export class WriteBuffer extends Disposable { this._callbacks = this._callbacks.slice(this._bufferOffset); this._bufferOffset = 0; } - setTimeout(() => this._innerWrite()); + this._scheduleInnerWrite(); } else { this._writeBuffer.length = 0; this._callbacks.length = 0; From c6e4627b45bf426cfcbbca9116d7133eee3041d3 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 27 May 2026 18:18:24 +0000 Subject: [PATCH 37/47] Hoist _getCurrentLinkId() in print() loop Avoid calling _getCurrentLinkId() twice per codepoint when a hyperlink is active by reading the link id once before the print loop. Fixes #5940 Co-authored-by: Daniel Imms --- src/common/InputHandler.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/common/InputHandler.ts b/src/common/InputHandler.ts index 33e954811..ef07aa05e 100644 --- a/src/common/InputHandler.ts +++ b/src/common/InputHandler.ts @@ -531,6 +531,8 @@ export class InputHandler extends Disposable implements IInputHandler { return; } + const linkId = this._getCurrentLinkId(); + this._dirtyRowTracker.markDirty(this._activeBuffer.y); // handle wide chars: reset start_cell-1 if we would overwrite the second cell of a wide char @@ -567,8 +569,8 @@ export class InputHandler extends Disposable implements IInputHandler { if (screenReaderMode) { this._onA11yChar.fire(stringFromCodePoint(code)); } - if (this._getCurrentLinkId()) { - this._oscLinkService.addLineToLink(this._getCurrentLinkId(), this._activeBuffer.ybase + this._activeBuffer.y); + if (linkId) { + this._oscLinkService.addLineToLink(linkId, this._activeBuffer.ybase + this._activeBuffer.y); } // goto next line if ch would overflow From e59a6c1f7b549bb4ab775a359d5dba4f748b0e57 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 27 May 2026 19:31:00 +0000 Subject: [PATCH 38/47] Reuse shared StringBuilder in BufferLine.translateToString Avoid per-call allocation when translating many lines in sequence by resetting a module-level work StringBuilder before and after each use. Co-authored-by: Daniel Imms --- src/common/buffer/BufferLine.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/common/buffer/BufferLine.ts b/src/common/buffer/BufferLine.ts index 69cde7e8f..e24a000c9 100644 --- a/src/common/buffer/BufferLine.ts +++ b/src/common/buffer/BufferLine.ts @@ -42,6 +42,7 @@ export const DEFAULT_ATTR_DATA = Object.freeze(new AttributeData()); // Work variables to avoid garbage collection let $startIndex = 0; const $workCell = new CellData(); +const $translateToStringBuilder = new StringBuilder(); export interface IBufferLineStringCacheEntry { value: string | undefined; @@ -571,12 +572,12 @@ export class BufferLine implements IBufferLine { if (outColumns) { outColumns.length = 0; } - const resultBuilder = new StringBuilder(); + $translateToStringBuilder.reset(); while (startCol < endCol) { const content = this._data[startCol * Constants.CELL_INDICIES + Cell.CONTENT]; const cp = content & Content.CODEPOINT_MASK; const chars = (content & Content.IS_COMBINED_MASK) ? this._combined[startCol] : (cp) ? stringFromCodePoint(cp) : WHITESPACE_CELL_CHAR; - resultBuilder.append(chars); + $translateToStringBuilder.append(chars); if (outColumns) { for (let i = 0; i < chars.length; ++i) { outColumns.push(startCol); @@ -587,7 +588,8 @@ export class BufferLine implements IBufferLine { if (outColumns) { outColumns.push(startCol); } - const result = resultBuilder.toString(); + const result = $translateToStringBuilder.toString(); + $translateToStringBuilder.reset(); if (isCanonicalRequest) { const cacheEntry = this._getStringCacheEntry(true)!; cacheEntry.value = result; From 7499a2181d90aeca5e473e9aa14ca6542d8c83ab Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 27 May 2026 19:34:08 +0000 Subject: [PATCH 39/47] Use DisposableStore.isDisposed instead of duplicate flag Co-authored-by: Daniel Imms --- src/common/input/WriteBuffer.ts | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/common/input/WriteBuffer.ts b/src/common/input/WriteBuffer.ts index 279dc7693..f606c8655 100644 --- a/src/common/input/WriteBuffer.ts +++ b/src/common/input/WriteBuffer.ts @@ -41,7 +41,6 @@ export class WriteBuffer extends Disposable { private _isSyncWriting = false; private _syncCalls = 0; private _didUserInput = false; - private _isDisposed = false; private readonly _innerWriteTimer = this._register(new TimeoutTimer()); private readonly _onWriteParsed = this._register(new Emitter()); @@ -52,10 +51,9 @@ export class WriteBuffer extends Disposable { } public override dispose(): void { - if (this._isDisposed) { + if (this._store.isDisposed) { return; } - this._isDisposed = true; this._writeBuffer.length = 0; this._callbacks.length = 0; this._pendingData = 0; @@ -76,7 +74,7 @@ export class WriteBuffer extends Disposable { * promises to resolve. */ public flushSync(): void { - if (this._isDisposed) { + if (this._store.isDisposed) { return; } // exit early if another sync write loop is active @@ -111,7 +109,7 @@ export class WriteBuffer extends Disposable { * @deprecated Unreliable, to be removed soon. */ public writeSync(data: string | Uint8Array, maxSubsequentCalls?: number): void { - if (this._isDisposed) { + if (this._store.isDisposed) { return; } // stop writeSync recursions with maxSubsequentCalls argument @@ -157,7 +155,7 @@ export class WriteBuffer extends Disposable { } public write(data: string | Uint8Array, callback?: () => void): void { - if (this._isDisposed) { + if (this._store.isDisposed) { return; } if (this._pendingData > Constants.DISCARD_WATERMARK) { @@ -217,14 +215,14 @@ export class WriteBuffer extends Disposable { * Note, for pure sync code `lastTime` and `promiseResult` have no meaning. */ private _scheduleInnerWrite(lastTime: number = 0, promiseResult: boolean = true): void { - if (this._isDisposed) { + if (this._store.isDisposed) { return; } this._innerWriteTimer.cancelAndSet(() => this._innerWrite(lastTime, promiseResult), 0); } protected _innerWrite(lastTime: number = 0, promiseResult: boolean = true): void { - if (this._isDisposed) { + if (this._store.isDisposed) { return; } const startTime = lastTime || performance.now(); @@ -256,7 +254,7 @@ export class WriteBuffer extends Disposable { * gain control. */ const continuation: (r: boolean) => void = (r: boolean) => { - if (this._isDisposed) { + if (this._store.isDisposed) { return; } if (performance.now() - startTime >= Constants.WRITE_TIMEOUT_MS) { From 3e0c7d18e50d89abf4c0523fc22cae6495d5b71d Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 27 May 2026 19:35:58 +0000 Subject: [PATCH 40/47] Register WriteBuffer cleanup via toDisposable in constructor Co-authored-by: Daniel Imms --- src/common/input/WriteBuffer.ts | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/src/common/input/WriteBuffer.ts b/src/common/input/WriteBuffer.ts index f606c8655..7ecab58ad 100644 --- a/src/common/input/WriteBuffer.ts +++ b/src/common/input/WriteBuffer.ts @@ -5,7 +5,7 @@ */ import { TimeoutTimer } from 'common/Async'; -import { Disposable } from 'common/Lifecycle'; +import { Disposable, toDisposable } from 'common/Lifecycle'; import { Emitter } from 'common/Event'; const enum Constants { @@ -48,17 +48,12 @@ export class WriteBuffer extends Disposable { constructor(private _action: (data: string | Uint8Array, promiseResult?: boolean) => void | Promise) { super(); - } - - public override dispose(): void { - if (this._store.isDisposed) { - return; - } - this._writeBuffer.length = 0; - this._callbacks.length = 0; - this._pendingData = 0; - this._bufferOffset = 0; - super.dispose(); + this._register(toDisposable(() => { + this._writeBuffer.length = 0; + this._callbacks.length = 0; + this._pendingData = 0; + this._bufferOffset = 0; + })); } public handleUserInput(): void { From 224c113b645b4fcac1fd9a6d98c6403311d8f726 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 27 May 2026 19:36:45 +0000 Subject: [PATCH 41/47] Move linkId declaration above use site in print loop Co-authored-by: Daniel Imms --- src/common/InputHandler.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/common/InputHandler.ts b/src/common/InputHandler.ts index ef07aa05e..fd1fdb8a1 100644 --- a/src/common/InputHandler.ts +++ b/src/common/InputHandler.ts @@ -531,8 +531,6 @@ export class InputHandler extends Disposable implements IInputHandler { return; } - const linkId = this._getCurrentLinkId(); - this._dirtyRowTracker.markDirty(this._activeBuffer.y); // handle wide chars: reset start_cell-1 if we would overwrite the second cell of a wide char @@ -569,6 +567,7 @@ export class InputHandler extends Disposable implements IInputHandler { if (screenReaderMode) { this._onA11yChar.fire(stringFromCodePoint(code)); } + const linkId = this._getCurrentLinkId(); if (linkId) { this._oscLinkService.addLineToLink(linkId, this._activeBuffer.ybase + this._activeBuffer.y); } From edb4f89dd8c86e04b5bd8289b3c66f2957603a00 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 27 May 2026 20:00:06 +0000 Subject: [PATCH 42/47] Add WriteBuffer regression tests for dispose and hot paths Co-authored-by: Daniel Imms --- src/common/input/WriteBuffer.test.ts | 45 +++++++++++++++++++++++++--- 1 file changed, 41 insertions(+), 4 deletions(-) diff --git a/src/common/input/WriteBuffer.test.ts b/src/common/input/WriteBuffer.test.ts index 5340f918a..8d8c81fc2 100644 --- a/src/common/input/WriteBuffer.test.ts +++ b/src/common/input/WriteBuffer.test.ts @@ -160,14 +160,51 @@ describe('WriteBuffer', () => { }); it('write after dispose is a no-op', done => { wb.dispose(); - wb.write('a', () => { - assert.deepEqual(stack, []); - done(); - }); + wb.write('a'); setTimeout(() => { assert.deepEqual(stack, []); done(); }, 20); }); + it('dispose is idempotent', done => { + wb.write('a'); + wb.dispose(); + wb.dispose(); + setTimeout(() => { + assert.deepEqual(stack, []); + done(); + }, 20); + }); + it('async handler continuation is skipped after dispose', done => { + let resolve!: (value: boolean) => void; + const pending = new Promise(r => { resolve = r; }); + wb = new WriteBuffer(() => pending); + wb.write('a'); + wb.dispose(); + resolve(true); + setTimeout(() => { + assert.deepEqual(stack, []); + done(); + }, 20); + }); + it('handleUserInput still processes first chunk synchronously', () => { + wb.handleUserInput(); + wb.write('a'); + assert.deepEqual(stack, ['a']); + }); + it('flushSync after dispose is a no-op', done => { + wb.write('a'); + wb.dispose(); + wb.flushSync(); + setTimeout(() => { + assert.deepEqual(stack, []); + done(); + }, 20); + }); + it('writeSync after dispose is a no-op', () => { + wb.dispose(); + wb.writeSync('a'); + assert.deepEqual(stack, []); + }); }); }); From 57b48f60480f77d39c605289c9902d08e5921103 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 27 May 2026 21:28:36 +0000 Subject: [PATCH 43/47] Allow markers on alt buffer in headless terminal Remove the alt-buffer guard in headless registerMarker so behavior matches the browser terminal. Update headless public API and typings to return IMarker instead of IMarker | undefined. Co-authored-by: Daniel Imms --- src/headless/Terminal.ts | 7 +------ src/headless/public/Terminal.test.ts | 17 +++++++++++++---- src/headless/public/Terminal.ts | 4 ++-- typings/xterm-headless.d.ts | 9 +++------ 4 files changed, 19 insertions(+), 18 deletions(-) diff --git a/src/headless/Terminal.ts b/src/headless/Terminal.ts index e53c00fbb..a348ae824 100644 --- a/src/headless/Terminal.ts +++ b/src/headless/Terminal.ts @@ -69,12 +69,7 @@ export class Terminal extends CoreTerminal { return this.buffer.markers; } - public registerMarker(cursorYOffset: number): IMarker | undefined { - // Disallow markers on the alt buffer - if (this.buffer !== this.buffers.normal) { - return; - } - + public registerMarker(cursorYOffset: number): IMarker { return this.buffer.addMarker(this.buffer.ybase + this.buffer.y + cursorYOffset); } diff --git a/src/headless/public/Terminal.test.ts b/src/headless/public/Terminal.test.ts index 0c4739744..73e2f4865 100644 --- a/src/headless/public/Terminal.test.ts +++ b/src/headless/public/Terminal.test.ts @@ -120,10 +120,10 @@ describe('Headless API Tests', function (): void { await writeSync('\n\rtest' + i); } const markers = [ - term.registerMarker(1)!, - term.registerMarker(2)!, - term.registerMarker(3)!, - term.registerMarker(4)! + term.registerMarker(1), + term.registerMarker(2), + term.registerMarker(3), + term.registerMarker(4) ]; let disposeCount = 0; for (const marker of markers) { @@ -419,6 +419,15 @@ describe('Headless API Tests', function (): void { strictEqual(term.buffer.normal.getLine(0)!.translateToString(), 'norm '); strictEqual(term.buffer.alternate.getLine(0), undefined); }); + + it('registerMarker on alternate buffer', async () => { + term = new Terminal({ cols: 5, allowProposedApi: true }); + await writeSync('\x1b[?47h'); + const marker = term.registerMarker(0); + strictEqual(term.buffer.active.type, 'alternate'); + strictEqual(term.markers.length, 1); + strictEqual(term.markers[0], marker); + }); }); describe('modes', () => { diff --git a/src/headless/public/Terminal.ts b/src/headless/public/Terminal.ts index 87eaf3976..d593dea15 100644 --- a/src/headless/public/Terminal.ts +++ b/src/headless/public/Terminal.ts @@ -130,11 +130,11 @@ export class Terminal extends Disposable implements ITerminalApi { this._verifyIntegers(columns, rows); this._core.resize(columns, rows); } - public registerMarker(cursorYOffset: number = 0): IMarker | undefined { + public registerMarker(cursorYOffset: number = 0): IMarker { this._verifyIntegers(cursorYOffset); return this._core.registerMarker(cursorYOffset); } - public addMarker(cursorYOffset: number): IMarker | undefined { + public addMarker(cursorYOffset: number): IMarker { return this.registerMarker(cursorYOffset); } public dispose(): void { diff --git a/typings/xterm-headless.d.ts b/typings/xterm-headless.d.ts index d3b62346d..a6db39b50 100644 --- a/typings/xterm-headless.d.ts +++ b/typings/xterm-headless.d.ts @@ -653,8 +653,7 @@ declare module '@xterm/headless' { readonly buffer: IBufferNamespace; /** - * Get all markers registered against the buffer. If the alt buffer is - * active this will always return []. + * Get all markers registered against the active buffer. */ readonly markers: ReadonlyArray; @@ -825,12 +824,10 @@ declare module '@xterm/headless' { resize(columns: number, rows: number): void; /** - * Adds a marker to the normal buffer and returns it. If the alt buffer is - * active, undefined is returned. + * Adds a marker to the active buffer and returns it. * @param cursorYOffset The y position offset of the marker from the cursor. - * @returns The new marker or undefined. */ - registerMarker(cursorYOffset?: number): IMarker | undefined; + registerMarker(cursorYOffset?: number): IMarker; /* * Disposes of the terminal, detaching it from the DOM and removing any From fdcd1c272092b73c85d4bf16cc439d1e3a9ff3f4 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 27 May 2026 21:46:20 +0000 Subject: [PATCH 44/47] Align xterm-headless marker JSDoc with xterm.d.ts Co-authored-by: Daniel Imms --- typings/xterm-headless.d.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/typings/xterm-headless.d.ts b/typings/xterm-headless.d.ts index a6db39b50..d51dc096d 100644 --- a/typings/xterm-headless.d.ts +++ b/typings/xterm-headless.d.ts @@ -653,7 +653,8 @@ declare module '@xterm/headless' { readonly buffer: IBufferNamespace; /** - * Get all markers registered against the active buffer. + * Get all markers registered against the buffer. If the alt buffer is + * active this will always return []. */ readonly markers: ReadonlyArray; @@ -824,8 +825,9 @@ declare module '@xterm/headless' { resize(columns: number, rows: number): void; /** - * Adds a marker to the active buffer and returns it. + * Adds a marker to the normal buffer and returns it. * @param cursorYOffset The y position offset of the marker from the cursor. + * @returns The new marker or undefined. */ registerMarker(cursorYOffset?: number): IMarker; From e89fbf2979ead7811a9d5d148692b6c6f409e187 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 27 May 2026 21:53:01 +0000 Subject: [PATCH 45/47] Bump qs and express Bumps [qs](https://github.com/ljharb/qs) to 6.15.2 and updates ancestor dependency [express](https://github.com/expressjs/express). These dependencies need to be updated together. Updates `qs` from 6.14.2 to 6.15.2 - [Changelog](https://github.com/ljharb/qs/blob/main/CHANGELOG.md) - [Commits](https://github.com/ljharb/qs/compare/v6.14.2...v6.15.2) Updates `express` from 4.22.1 to 4.22.2 - [Release notes](https://github.com/expressjs/express/releases) - [Changelog](https://github.com/expressjs/express/blob/v4.22.2/History.md) - [Commits](https://github.com/expressjs/express/compare/v4.22.1...v4.22.2) --- updated-dependencies: - dependency-name: express dependency-version: 4.22.2 dependency-type: direct:development - dependency-name: qs dependency-version: 6.15.2 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- package-lock.json | 50 +++++++++++++++++++++++++++++++---------------- 1 file changed, 33 insertions(+), 17 deletions(-) diff --git a/package-lock.json b/package-lock.json index f101299ce..32c27e469 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2594,10 +2594,11 @@ } }, "node_modules/body-parser": { - "version": "1.20.4", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.4.tgz", - "integrity": "sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==", + "version": "1.20.5", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.5.tgz", + "integrity": "sha512-3grm+/2tUOvu2cjJkvsIxrv/wVpfXQW4PsQHYm7yk4vfpu7Ekl6nEsYBoJUL6qDwZUx8wUhQ8tR2qz+ad9c9OA==", "dev": true, + "license": "MIT", "dependencies": { "bytes": "~3.1.2", "content-type": "~1.0.5", @@ -2607,7 +2608,7 @@ "http-errors": "~2.0.1", "iconv-lite": "~0.4.24", "on-finished": "~2.4.1", - "qs": "~6.14.0", + "qs": "~6.15.1", "raw-body": "~2.5.3", "type-is": "~1.6.18", "unpipe": "~1.0.0" @@ -2622,6 +2623,7 @@ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, + "license": "MIT", "dependencies": { "ms": "2.0.0" } @@ -2630,7 +2632,8 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/brace-expansion": { "version": "2.0.2", @@ -2712,6 +2715,7 @@ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -2749,6 +2753,7 @@ "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", "dev": true, + "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" @@ -3144,6 +3149,7 @@ "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -3780,14 +3786,15 @@ } }, "node_modules/express": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.22.1.tgz", - "integrity": "sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==", + "version": "4.22.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.22.2.tgz", + "integrity": "sha512-IuL+Elrou2ZvCFHs18/CIzy2Nzvo25nZ1/D2eIZlz7c+QUayAcYoiM2BthCjs+EBHVpjYjcuLDAiCWgeIX3X1Q==", "dev": true, + "license": "MIT", "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "~1.20.3", + "body-parser": "~1.20.5", "content-disposition": "~0.5.4", "content-type": "~1.0.4", "cookie": "~0.7.1", @@ -3806,7 +3813,7 @@ "parseurl": "~1.3.3", "path-to-regexp": "~0.1.12", "proxy-addr": "~2.0.7", - "qs": "~6.14.0", + "qs": "~6.15.1", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", "send": "~0.19.0", @@ -4521,6 +4528,7 @@ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", "dev": true, + "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3" }, @@ -5222,6 +5230,7 @@ "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -5706,6 +5715,7 @@ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -6128,9 +6138,9 @@ } }, "node_modules/qs": { - "version": "6.14.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz", - "integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==", + "version": "6.15.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.2.tgz", + "integrity": "sha512-Rzq0KEyX/w/tEybncDgdkZrJgVUsUMk3xjh3t5bv3S1HTAtg+uOYt72+ZfwiQwKdysThkTBdL/rTi6HDmX9Ddw==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -6166,6 +6176,7 @@ "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz", "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==", "dev": true, + "license": "MIT", "dependencies": { "bytes": "~3.1.2", "http-errors": "~2.0.1", @@ -6604,6 +6615,7 @@ "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", "dev": true, + "license": "MIT", "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3", @@ -6619,13 +6631,14 @@ } }, "node_modules/side-channel-list": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", - "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", + "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", "dev": true, + "license": "MIT", "dependencies": { "es-errors": "^1.3.0", - "object-inspect": "^1.13.3" + "object-inspect": "^1.13.4" }, "engines": { "node": ">= 0.4" @@ -6639,6 +6652,7 @@ "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", "dev": true, + "license": "MIT", "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", @@ -6657,6 +6671,7 @@ "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", "dev": true, + "license": "MIT", "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", @@ -7194,6 +7209,7 @@ "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", "dev": true, + "license": "MIT", "dependencies": { "media-typer": "0.3.0", "mime-types": "~2.1.24" From 1d569f873d52df402f2e0642c3e1aac922afb713 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 27 May 2026 23:22:32 +0000 Subject: [PATCH 46/47] Ignore .cursor/*.log files in git Co-authored-by: Daniel Imms --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 81fe7a147..aa46b451f 100644 --- a/.gitignore +++ b/.gitignore @@ -24,6 +24,7 @@ test-results/ # Tracing/debug files .cursor/nal-trace/ +.cursor/*.log # Keep bundled code out of Git dist/ From 38174907f30ca307be978cd9e3e274831f1c870b Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 28 May 2026 00:24:06 +0000 Subject: [PATCH 47/47] fix(addon-progress): stabilize WebKit integration test harness Remove unnecessary unawaited setViewportSize from ProgressAddon tests, which was the line reported in flaky CI failures. Use serial mode and scoped hooks per Playwright shared-page guidance, and guard afterAll teardown when setup fails. Fixes #5956 Co-authored-by: Daniel Imms --- .../addon-progress/test/ProgressAddon.test.ts | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/addons/addon-progress/test/ProgressAddon.test.ts b/addons/addon-progress/test/ProgressAddon.test.ts index 792c0445d..12ff7a38f 100644 --- a/addons/addon-progress/test/ProgressAddon.test.ts +++ b/addons/addon-progress/test/ProgressAddon.test.ts @@ -9,15 +9,18 @@ import { ITestContext, createTestContext, openTerminal } from '../../../test/pla let ctx: ITestContext; -test.beforeAll(async ({ browser }) => { - ctx = await createTestContext(browser); - ctx.page.setViewportSize({ width: 1024, height: 768 }); - await openTerminal(ctx); -}); -test.afterAll(async () => await ctx.page.close()); - test.describe('ProgressAddon', () => { + test.describe.configure({ mode: 'serial' }); + + test.beforeAll(async ({ browser }) => { + ctx = await createTestContext(browser); + await openTerminal(ctx); + }); + test.afterAll(async () => { + await ctx?.page?.close(); + }); + test.beforeEach(async function(): Promise { await ctx.page.evaluate(` window.progressStack = [];