diff --git a/addons/xterm-addon-serialize/benchmark/tsconfig.json b/addons/xterm-addon-serialize/benchmark/tsconfig.json index 42aaaa1b..bf5e335c 100644 --- a/addons/xterm-addon-serialize/benchmark/tsconfig.json +++ b/addons/xterm-addon-serialize/benchmark/tsconfig.json @@ -14,7 +14,7 @@ "SerializeAddon": ["../src/SerializeAddon"] } }, - "include": ["../**/*", "../../../typings/xterm.d.ts", "../../../out/**/*"], + "include": ["../**/*", "../../../typings/xterm.d.ts"], "exclude": ["../../../**/*test.ts", "../../**/*api.ts"], "references": [ { "path": "../../../src/common" }, diff --git a/addons/xterm-addon-web-links/src/WebLinksAddon.ts b/addons/xterm-addon-web-links/src/WebLinksAddon.ts index dd1c1f17..6ba211fe 100644 --- a/addons/xterm-addon-web-links/src/WebLinksAddon.ts +++ b/addons/xterm-addon-web-links/src/WebLinksAddon.ts @@ -43,6 +43,7 @@ function handleLink(event: MouseEvent, uri: string): void { interface ILinkProviderOptions { hover?(event: MouseEvent, text: string, location: IViewportRange): void; leave?(event: MouseEvent, text: string): void; + urlRegex?: RegExp; } export class WebLinksAddon implements ITerminalAddon { @@ -62,7 +63,8 @@ export class WebLinksAddon implements ITerminalAddon { if (this._useLinkProvider && 'registerLinkProvider' in this._terminal) { const options = this._options as ILinkProviderOptions; - this._linkProvider = this._terminal.registerLinkProvider(new WebLinkProvider(this._terminal, strictUrlRegex, this._handler, options)); + const regex = options.urlRegex || strictUrlRegex; + this._linkProvider = this._terminal.registerLinkProvider(new WebLinkProvider(this._terminal, regex, this._handler, options)); } else { // TODO: This should be removed eventually const options = this._options as ILinkMatcherOptions; diff --git a/addons/xterm-addon-webgl/src/atlas/WebglCharAtlas.ts b/addons/xterm-addon-webgl/src/atlas/WebglCharAtlas.ts index 609df6eb..dd95f177 100644 --- a/addons/xterm-addon-webgl/src/atlas/WebglCharAtlas.ts +++ b/addons/xterm-addon-webgl/src/atlas/WebglCharAtlas.ts @@ -14,8 +14,8 @@ import { AttributeData } from 'common/buffer/AttributeData'; import { channels, rgba } from 'browser/Color'; import { tryDrawCustomChar } from 'browser/renderer/CustomGlyphs'; -// In practice we're probably never going to exhaust a texture this large. For debugging purposes, -// however, it can be useful to set this to a really tiny value, to verify that LRU eviction works. +// For debugging purposes, it can be useful to set this to a really tiny value, +// to verify that LRU eviction works. const TEXTURE_WIDTH = 1024; const TEXTURE_HEIGHT = 1024; @@ -463,7 +463,7 @@ export class WebglCharAtlas implements IDisposable { const clippedImageData = this._clipImageData(imageData, this._workBoundingBox); // Check if there is enough room in the current row and go to next if needed - if (this._currentRowX + this._config.scaledCharWidth > TEXTURE_WIDTH) { + if (this._currentRowX + rasterizedGlyph.size.x > TEXTURE_WIDTH) { this._currentRowX = 0; this._currentRowY += this._currentRowHeight; this._currentRowHeight = 0; diff --git a/bin/extract_vtfeatures.js b/bin/extract_vtfeatures.js index 62ad9ff9..9b5ab898 100644 --- a/bin/extract_vtfeatures.js +++ b/bin/extract_vtfeatures.js @@ -11,13 +11,13 @@ const Mustache = require('mustache'); * regexp to fetch all comments * Fetches all multiline comments and single lines containing '// @vt:'. */ -const REX_COMMENTS = /^\s*?[/][*][*]([\s\S]*?)[*][/]|^\s*?\/\/ ([@]vt[:].*?)$/mug; +const REX_COMMENTS = /^\s*?\/\*\*([\S\s]*?)\*\/|^\s*?\/\/ (@vt:.*?)$/mug; /** * regexp to parse the @vt line * expected data - "@vt: "" "" "" */ -const REX_VT_LINE = /^[@]vt\:\s*(\w+|#\w+|#\w+\[.*?\])\s*(\w+)\s*(\w+)\s*"(.*?)"\s*"(.*?)"\s*"(.*?)".*$/; +const REX_VT_LINE = /^@vt:\s*(\w+|#\w+|#\w+\[.*?\])\s*(\w+)\s*(\w+)\s*"(.*?)"\s*"(.*?)"\s*"(.*?)".*$/; // known vt command types const TYPES = [ @@ -362,7 +362,7 @@ function* parseMultiLineGen(filename, s) { if (!s.includes('@vt:')) { return; } - const lines = s.split('\n').map(el => el.trim().replace(/[*]/, '').replace(/\s/, '')); + const lines = s.split('\n').map(el => el.trim().replace(/\*/, '').replace(/\s/, '')); let grabLine = false; let longDescription = []; let feature = undefined; diff --git a/bin/publish.js b/bin/publish.js index 3c729f6c..75de4b68 100644 --- a/bin/publish.js +++ b/bin/publish.js @@ -92,7 +92,7 @@ function checkAndPublishPackage(packageDir) { } function getNextBetaVersion(packageJson) { - if (!/^[0-9]+\.[0-9]+\.[0-9]+$/.exec(packageJson.version)) { + if (!/^\d+\.\d+\.\d+$/.exec(packageJson.version)) { console.error('The package.json version must be of the form x.y.z'); process.exit(1); } @@ -104,11 +104,11 @@ function getNextBetaVersion(packageJson) { return `${nextStableVersion}-${tag}.1`; } const latestPublishedVersion = publishedVersions.sort((a, b) => { - const aVersion = parseInt(a.substr(a.search(/[0-9]+$/))); - const bVersion = parseInt(b.substr(b.search(/[0-9]+$/))); + const aVersion = parseInt(a.substr(a.search(/\d+$/))); + const bVersion = parseInt(b.substr(b.search(/\d+$/))); return aVersion > bVersion ? -1 : 1; })[0]; - const latestTagVersion = parseInt(latestPublishedVersion.substr(latestPublishedVersion.search(/[0-9]+$/)), 10); + const latestTagVersion = parseInt(latestPublishedVersion.substr(latestPublishedVersion.search(/\d+$/)), 10); return `${nextStableVersion}-${tag}.${latestTagVersion + 1}`; } diff --git a/bin/test_api.js b/bin/test_api.js index f173b417..ff166727 100644 --- a/bin/test_api.js +++ b/bin/test_api.js @@ -21,7 +21,7 @@ let flagArgs = []; if (process.argv.length > 2) { const args = process.argv.slice(2); - flagArgs = args.filter(e => e.startsWith('--')).map(arg => arg.split('=')).reduce((arr, val) => arr.concat([...val], [])); + flagArgs = args.filter(e => e.startsWith('--')).map(arg => arg.split('=')).reduce((arr, val) => arr.concat(val.slice(), [])); console.info(flagArgs); // ability to inject particular test files via // yarn test [testFileA testFileB ...] @@ -44,7 +44,7 @@ const server = cp.spawn('node', ['demo/start'], { server.stdout.on('data', (data) => { // await for the server to fully start - if (data.indexOf("successfully") !== -1) { + if (data.includes("successfully")) { const run = cp.spawnSync( npmBinScript('mocha'), [...testFiles, ...flagArgs], { diff --git a/src/browser/AccessibilityManager.ts b/src/browser/AccessibilityManager.ts index 80092202..eda29c05 100644 --- a/src/browser/AccessibilityManager.ts +++ b/src/browser/AccessibilityManager.ts @@ -55,6 +55,7 @@ export class AccessibilityManager extends Disposable { this._accessibilityTreeRoot = document.createElement('div'); this._accessibilityTreeRoot.setAttribute('role', 'document'); this._accessibilityTreeRoot.classList.add('xterm-accessibility'); + this._accessibilityTreeRoot.tabIndex = 0; this._rowContainer = document.createElement('div'); this._rowContainer.setAttribute('role', 'list'); diff --git a/src/browser/Terminal.ts b/src/browser/Terminal.ts index 24dc7af9..bcb89abb 100644 --- a/src/browser/Terminal.ts +++ b/src/browser/Terminal.ts @@ -820,7 +820,7 @@ export class Terminal extends CoreTerminal implements ITerminal { // normal viewport scrolling // conditionally stop event, if the viewport still had rows to scroll within - if (!this.viewport!.onWheel(ev)) { + if (this.viewport!.onWheel(ev)) { return this.cancel(ev); } }, { passive: false })); @@ -1169,6 +1169,10 @@ export class Terminal 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 + this._unprocessedDeadKey = false; + return true; } @@ -1181,11 +1185,15 @@ export class Terminal extends CoreTerminal implements ITerminal { 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 - if (ev.data && ev.inputType === 'insertText' && !this.optionsService.options.screenReaderMode) { + if (ev.data && ev.inputType === 'insertText' && !ev.composed && !this.optionsService.options.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 + this._unprocessedDeadKey = false; + const text = ev.data; this.coreService.triggerDataEvent(text, true); diff --git a/src/browser/Viewport.ts b/src/browser/Viewport.ts index f73594bf..1dfc9e3e 100644 --- a/src/browser/Viewport.ts +++ b/src/browser/Viewport.ts @@ -220,7 +220,7 @@ export class Viewport extends Disposable implements IViewport { private _getPixelsScrolled(ev: WheelEvent): number { // Do nothing if it's not a vertical scroll event - if (ev.deltaY === 0) { + if (ev.deltaY === 0 || ev.shiftKey) { return 0; } @@ -241,7 +241,7 @@ export class Viewport extends Disposable implements IViewport { */ public getLinesScrolled(ev: WheelEvent): number { // Do nothing if it's not a vertical scroll event - if (ev.deltaY === 0) { + if (ev.deltaY === 0 || ev.shiftKey) { return 0; } diff --git a/src/browser/input/MoveToCell.ts b/src/browser/input/MoveToCell.ts index 25e1844d..82e767cd 100644 --- a/src/browser/input/MoveToCell.ts +++ b/src/browser/input/MoveToCell.ts @@ -121,7 +121,7 @@ function wrappedRowsCount(startY: number, targetY: number, bufferService: IBuffe for (let i = 0; i < Math.abs(startRow - endRow); i++) { const direction = verticalDirection(startY, targetY) === Direction.UP ? -1 : 1; const line = bufferService.buffer.lines.get(startRow + (direction * i)); - if (line && line.isWrapped) { + if (line?.isWrapped) { wrappedRows++; } } @@ -136,12 +136,12 @@ function wrappedRowsCount(startY: number, targetY: number, bufferService: IBuffe function wrappedRowsForRow(bufferService: IBufferService, currentRow: number): number { let rowCount = 0; let line = bufferService.buffer.lines.get(currentRow); - let lineWraps = line && line.isWrapped; + let lineWraps = line?.isWrapped; while (lineWraps && currentRow >= 0 && currentRow < bufferService.rows) { rowCount++; line = bufferService.buffer.lines.get(--currentRow); - lineWraps = line && line.isWrapped; + lineWraps = line?.isWrapped; } return rowCount; diff --git a/src/browser/renderer/BaseRenderLayer.ts b/src/browser/renderer/BaseRenderLayer.ts index 40b6e3f9..343ebfe2 100644 --- a/src/browser/renderer/BaseRenderLayer.ts +++ b/src/browser/renderer/BaseRenderLayer.ts @@ -325,12 +325,7 @@ export abstract class BaseRenderLayer implements IRenderLayer { this._currentGlyphIdentifier.bold = !!cell.isBold(); this._currentGlyphIdentifier.dim = !!cell.isDim(); this._currentGlyphIdentifier.italic = !!cell.isItalic(); - const atlasDidDraw = this._charAtlas && this._charAtlas.draw( - this._ctx, - this._currentGlyphIdentifier, - x * this._scaledCellWidth + this._scaledCharLeft, - y * this._scaledCellHeight + this._scaledCharTop - ); + const atlasDidDraw = this._charAtlas?.draw(this._ctx, this._currentGlyphIdentifier, x * this._scaledCellWidth + this._scaledCharLeft, y * this._scaledCellHeight + this._scaledCharTop); if (!atlasDidDraw) { this._drawUncachedChars(cell, x, y); diff --git a/src/browser/renderer/atlas/CharAtlasUtils.ts b/src/browser/renderer/atlas/CharAtlasUtils.ts index b196b373..be92727a 100644 --- a/src/browser/renderer/atlas/CharAtlasUtils.ts +++ b/src/browser/renderer/atlas/CharAtlasUtils.ts @@ -16,7 +16,7 @@ export function generateConfig(scaledCharWidth: number, scaledCharHeight: number cursor: undefined, cursorAccent: undefined, selection: undefined, - ansi: colors.ansi + ansi: [...colors.ansi] }; return { devicePixelRatio: window.devicePixelRatio, diff --git a/src/browser/services/CharacterJoinerService.ts b/src/browser/services/CharacterJoinerService.ts index ea65c29b..ca4f1984 100644 --- a/src/browser/services/CharacterJoinerService.ts +++ b/src/browser/services/CharacterJoinerService.ts @@ -176,16 +176,25 @@ export class CharacterJoinerService implements ICharacterJoinerService { // 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. - const joinedRanges: [number, number][] = this._characterJoiners[0].handler(text); + let allJoinedRanges: [number, number][] = []; + try { + allJoinedRanges = this._characterJoiners[0].handler(text); + } catch (error) { + console.error(error); + } for (let i = 1; i < this._characterJoiners.length; i++) { // We merge any overlapping ranges across the different joiners - const joinerRanges = this._characterJoiners[i].handler(text); - for (let j = 0; j < joinerRanges.length; j++) { - CharacterJoinerService._mergeRanges(joinedRanges, joinerRanges[j]); + try { + const joinerRanges = this._characterJoiners[i].handler(text); + for (let j = 0; j < joinerRanges.length; j++) { + CharacterJoinerService._mergeRanges(allJoinedRanges, joinerRanges[j]); + } + } catch (error) { + console.error(error); } } - this._stringRangesToCellRanges(joinedRanges, lineData, startCol); - return joinedRanges; + this._stringRangesToCellRanges(allJoinedRanges, lineData, startCol); + return allJoinedRanges; } /** diff --git a/src/browser/services/SelectionService.ts b/src/browser/services/SelectionService.ts index c0547755..fce53ecd 100644 --- a/src/browser/services/SelectionService.ts +++ b/src/browser/services/SelectionService.ts @@ -220,7 +220,7 @@ export class SelectionService extends Disposable implements ISelectionService { for (let i = start[1] + 1; i <= end[1] - 1; i++) { const bufferLine = buffer.lines.get(i); const lineText = buffer.translateBufferLineToString(i, true); - if (bufferLine && bufferLine.isWrapped) { + if (bufferLine?.isWrapped) { result[result.length - 1] += lineText; } else { result.push(lineText); @@ -924,7 +924,7 @@ export class SelectionService extends Disposable implements ISelectionService { if (followWrappedLinesBelow) { if (start + length === this._bufferService.cols && bufferLine.getCodePoint(this._bufferService.cols - 1) !== 32 /* ' ' */) { const nextBufferLine = buffer.lines.get(coords[1] + 1); - if (nextBufferLine && nextBufferLine.isWrapped && nextBufferLine.getCodePoint(0) !== 32 /* ' ' */) { + if (nextBufferLine?.isWrapped && nextBufferLine.getCodePoint(0) !== 32 /* ' ' */) { const nextLineWordPosition = this._getWordAt([0, coords[1] + 1], false, false, true); if (nextLineWordPosition) { length += nextLineWordPosition.length; diff --git a/src/common/input/Keyboard.ts b/src/common/input/Keyboard.ts index 20a28f61..b4b3dce4 100644 --- a/src/common/input/Keyboard.ts +++ b/src/common/input/Keyboard.ts @@ -349,7 +349,7 @@ export function evaluateKeyboardEvent( } else if ((!isMac || macOptionIsMeta) && ev.altKey && !ev.metaKey) { // On macOS this is a third level shift when !macOptionIsMeta. Use instead. const keyMapping = KEYCODE_KEY_MAPPINGS[ev.keyCode]; - const key = keyMapping && keyMapping[!ev.shiftKey ? 0 : 1]; + const key = keyMapping?.[!ev.shiftKey ? 0 : 1]; if (key) { result.key = C0.ESC + key; } else if (ev.keyCode >= 65 && ev.keyCode <= 90) { diff --git a/src/common/input/UnicodeV6.test.ts b/src/common/input/UnicodeV6.test.ts index 9a7fe805..9f89048c 100644 --- a/src/common/input/UnicodeV6.test.ts +++ b/src/common/input/UnicodeV6.test.ts @@ -162,7 +162,7 @@ it('wcwidth should match all values from the old implementation', function(): vo // we are only interested in 2 LSBs, cut off higher bits // ==> n = n & 3 e.g. 000000000000000000000000000000XX return (num: number): number => { - num = num | 0; // get asm.js like optimization under V8 + num |= 0; // get asm.js like optimization under V8 if (num < 32) { return control | 0; } diff --git a/src/common/parser/Params.ts b/src/common/parser/Params.ts index 47ee129d..7071453d 100644 --- a/src/common/parser/Params.ts +++ b/src/common/parser/Params.ts @@ -52,9 +52,9 @@ export class Params implements IParams { return params; } // skip leading sub params - for (let i = (values[0] instanceof Array) ? 1 : 0; i < values.length; ++i) { + for (let i = (Array.isArray(values[0])) ? 1 : 0; i < values.length; ++i) { const value = values[i]; - if (value instanceof Array) { + if (Array.isArray(value)) { for (let k = 0; k < value.length; ++k) { params.addSubParam(value[k]); } diff --git a/test/api/TestUtils.ts b/test/api/TestUtils.ts index 4fa98d43..2acc4d09 100644 --- a/test/api/TestUtils.ts +++ b/test/api/TestUtils.ts @@ -71,11 +71,11 @@ export function getBrowserType(): playwright.BrowserType = { - headless: process.argv.includes('--headless'), - } + headless: process.argv.includes('--headless') + }; const index = process.argv.indexOf('--executablePath'); - if(index > 0 && process.argv.length > index + 1 && typeof process.argv[index + 1] === 'string') { + if (index > 0 && process.argv.length > index + 1 && typeof process.argv[index + 1] === 'string') { options.executablePath = process.argv[index + 1]; } diff --git a/test/benchmark/tsconfig.json b/test/benchmark/tsconfig.json index cac99d90..2ebe2ebe 100644 --- a/test/benchmark/tsconfig.json +++ b/test/benchmark/tsconfig.json @@ -21,8 +21,7 @@ }, "include": [ "./**/*", - "../../typings/xterm.d.ts", - "../../out/**/*" + "../../typings/xterm.d.ts" ], "exclude": [ "../../**/*test.ts"