From 486fc714b3c647e962bf4ba82ced60f01a87ac13 Mon Sep 17 00:00:00 2001 From: Daniel Date: Sat, 26 Oct 2019 18:12:17 -0700 Subject: [PATCH 01/92] Support checkboxes in demo to toggle addons Fixes #2407 --- demo/client.ts | 88 ++++++++++++++++++++++++++++++++++++++++++------- demo/index.html | 5 +++ 2 files changed, 81 insertions(+), 12 deletions(-) diff --git a/demo/client.ts b/demo/client.ts index 7942cffe..b32d6c6a 100644 --- a/demo/client.ts +++ b/demo/client.ts @@ -39,13 +39,39 @@ export interface IWindowWithTerminal extends Window { declare let window: IWindowWithTerminal; let term; -let fitAddon: FitAddon; -let searchAddon: SearchAddon; let protocol; let socketURL; let socket; let pid; +type AddonType = 'attach' | 'fit' | 'search' | 'web-links' | 'webgl'; + +interface IDemoAddon { + name: T; + canChange: boolean; + ctor: + T extends 'attach' ? typeof AttachAddon : + T extends 'fit' ? typeof FitAddon : + T extends 'search' ? typeof SearchAddon : + T extends 'web-links' ? typeof WebLinksAddon : + typeof WebglAddon; + instance?: + T extends 'attach' ? AttachAddon : + T extends 'fit' ? FitAddon : + T extends 'search' ? SearchAddon : + T extends 'web-links' ? WebLinksAddon : + T extends 'webgl' ? WebglAddon : + never; +} + +const addons: { [T in AddonType]: IDemoAddon} = { + attach: { name: 'attach', ctor: AttachAddon, canChange: false }, + fit: { name: 'fit', ctor: FitAddon, canChange: false }, + search: { name: 'search', ctor: SearchAddon, canChange: true }, + 'web-links': { name: 'web-links', ctor: WebLinksAddon, canChange: true }, + webgl: { name: 'webgl', ctor: WebglAddon, canChange: true } +}; + const terminalContainer = document.getElementById('terminal-container'); const actionElements = { findNext: document.querySelector('#find-next'), @@ -108,11 +134,12 @@ function createTerminal(): void { // Load addons const typedTerm = term as TerminalType; - typedTerm.loadAddon(new WebLinksAddon()); - searchAddon = new SearchAddon(); - typedTerm.loadAddon(searchAddon); - fitAddon = new FitAddon(); - typedTerm.loadAddon(fitAddon); + addons['web-links'].instance = new WebLinksAddon(); + addons.search.instance = new SearchAddon(); + addons.fit.instance = new FitAddon(); + typedTerm.loadAddon(addons['web-links'].instance); + typedTerm.loadAddon(addons.search.instance); + typedTerm.loadAddon(addons.fit.instance); window.term = term; // Expose `term` to window for debugging purposes term.onResize((size: { cols: number, rows: number }) => { @@ -129,17 +156,17 @@ function createTerminal(): void { socketURL = protocol + location.hostname + ((location.port) ? (':' + location.port) : '') + '/terminals/'; term.open(terminalContainer); - fitAddon.fit(); + addons.fit.instance!.fit(); term.focus(); addDomListener(paddingElement, 'change', setPadding); addDomListener(actionElements.findNext, 'keyup', (e) => { - searchAddon.findNext(actionElements.findNext.value, getSearchOptions(e)); + addons.search.instance.findNext(actionElements.findNext.value, getSearchOptions(e)); }); addDomListener(actionElements.findPrevious, 'keyup', (e) => { - searchAddon.findPrevious(actionElements.findPrevious.value, getSearchOptions(e)); + addons.search.instance.findPrevious(actionElements.findPrevious.value, getSearchOptions(e)); }); // fit is called within a setTimeout, cols and rows need this. @@ -167,8 +194,10 @@ function createTerminal(): void { } function runRealTerminal(): void { - term.loadAddon(new AttachAddon(socket)); + addons.attach.instance = new AttachAddon(socket); + term.loadAddon(addons.attach.instance); term._initialized = true; + initAddons(term); } function runFakeTerminal(): void { @@ -177,6 +206,7 @@ function runFakeTerminal(): void { } term._initialized = true; + initAddons(term); term.prompt = () => { term.write('\r\n$ '); @@ -300,6 +330,40 @@ function initOptions(term: TerminalType): void { }); } +function initAddons(term: TerminalType): void { + const fragment = document.createDocumentFragment(); + Object.keys(addons).forEach((name: AddonType) => { + const addon = addons[name]; + const checkbox = document.createElement('input') as HTMLInputElement; + checkbox.type = 'checkbox'; + checkbox.checked = !!addon.instance; + if (!addon.canChange) { + checkbox.disabled = true; + } + checkbox.addEventListener('change', () => { + if (checkbox.checked) { + addon.instance = new addon.ctor(); + term.loadAddon(addon.instance); + } else { + addon.instance!.dispose(); + addon.instance = undefined; + } + }); + const label = document.createElement('label'); + label.classList.add('addon'); + if (!addon.canChange) { + label.title = 'This addon is needed for the demo to operate'; + } + label.appendChild(checkbox); + label.appendChild(document.createTextNode(name)); + const wrapper = document.createElement('div'); + wrapper.classList.add('addon'); + wrapper.appendChild(label); + fragment.appendChild(wrapper); + }); + document.getElementById('addons-container').appendChild(fragment); +} + function addDomListener(element: HTMLElement, type: string, handler: (...args: any[]) => any): void { element.addEventListener(type, handler); term._core.register({ dispose: () => element.removeEventListener(type, handler) }); @@ -312,5 +376,5 @@ function updateTerminalSize(): void { const height = (rows * term._core._renderService.dimensions.actualCellHeight).toString() + 'px'; terminalContainer.style.width = width; terminalContainer.style.height = height; - fitAddon.fit(); + addons.fit.instance.fit(); } diff --git a/demo/index.html b/demo/index.html index ef8f3891..83ea6815 100644 --- a/demo/index.html +++ b/demo/index.html @@ -26,6 +26,11 @@

These options can be set in the Terminal constructor or using the Terminal.setOption function.

+
+

Addons

+

Addons can be loaded and unloaded on a particular terminal to extend its functionality.

+
+

Style

From 2f18edbcac8a42522ebd692a726dee860642d01d Mon Sep 17 00:00:00 2001 From: Daniel Date: Sat, 26 Oct 2019 18:12:56 -0700 Subject: [PATCH 02/92] Remove old webgl enable button --- demo/client.ts | 1 - demo/index.html | 1 - 2 files changed, 2 deletions(-) diff --git a/demo/client.ts b/demo/client.ts index b32d6c6a..5b76700e 100644 --- a/demo/client.ts +++ b/demo/client.ts @@ -118,7 +118,6 @@ if (document.location.pathname === '/test') { } else { createTerminal(); document.getElementById('dispose').addEventListener('click', disposeRecreateButtonHandler); - document.getElementById('webgl').addEventListener('click', () => term.loadAddon(new WebglAddon())); } function createTerminal(): void { diff --git a/demo/index.html b/demo/index.html index 83ea6815..88c6bc4c 100644 --- a/demo/index.html +++ b/demo/index.html @@ -41,7 +41,6 @@

Attention: The demo is a barebones implementation and is designed for the development and evaluation of xterm.js only. Exposing the demo to the public as is would introduce security risks for the host.

- From 6c2b34a5c8b421f3b29aeddac07108ea4bcf8404 Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 30 Oct 2019 20:03:53 -0700 Subject: [PATCH 03/92] Use YarnInstaller --- azure-pipelines.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index a58f7fe0..cec85ba5 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -12,9 +12,9 @@ jobs: inputs: versionSpec: '8.x' displayName: 'Install Node.js' - - task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 + - task: YarnInstaller@3 inputs: - versionSpec: "1.9.4" + versionSpec: '1.x' displayName: 'Install Yarn' - script: | yarn @@ -70,9 +70,9 @@ jobs: inputs: versionSpec: '8.x' displayName: 'Install Node.js' - - task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 + - task: YarnInstaller@3 inputs: - versionSpec: "1.9.4" + versionSpec: '1.x' displayName: 'Install Yarn' - script: | yarn @@ -115,9 +115,9 @@ jobs: inputs: versionSpec: '8.x' displayName: 'Install Node.js' - - task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 + - task: YarnInstaller@3 inputs: - versionSpec: "1.9.4" + versionSpec: '1.x' displayName: 'Install Yarn' - script: | yarn From 79f3a7343cf71de92e18157ab6f7a90931181d9b Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 30 Oct 2019 20:07:41 -0700 Subject: [PATCH 04/92] Don't wrap for single line scripts --- azure-pipelines.yml | 39 +++++++++++++-------------------------- 1 file changed, 13 insertions(+), 26 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index cec85ba5..c6ee9ac0 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -16,14 +16,11 @@ jobs: inputs: versionSpec: '1.x' displayName: 'Install Yarn' - - script: | - yarn + - script: yarn displayName: 'Install dependencies and build' - - script: | - yarn test-unit --forbid-only + - script: yarn test-unit --forbid-only displayName: 'Unit tests' - - script: | - yarn lint + - script: yarn lint displayName: 'Lint' - job: macOS @@ -34,14 +31,11 @@ jobs: inputs: versionSpec: '8.x' displayName: 'Install Node.js' - - script: | - yarn + - script: yarn displayName: 'Install dependencies and build' - - script: | - yarn test-unit --forbid-only + - script: yarn test-unit --forbid-only displayName: 'Unit tests' - - script: | - yarn lint + - script: yarn lint displayName: 'Lint' - job: Windows @@ -52,14 +46,11 @@ jobs: inputs: versionSpec: '8.x' displayName: 'Install Node.js' - - script: | - yarn + - script: yarn displayName: 'Install dependencies and build' - - script: | - yarn test-unit --forbid-only + - script: yarn test-unit --forbid-only displayName: 'Unit tests' - - script: | - yarn lint + - script: yarn lint displayName: 'Lint' - job: Linux_IntegrationTests @@ -74,8 +65,7 @@ jobs: inputs: versionSpec: '1.x' displayName: 'Install Yarn' - - script: | - yarn + - script: yarn displayName: 'Install dependencies and build' - script: | yarn start & @@ -91,8 +81,7 @@ jobs: inputs: versionSpec: '8.x' displayName: 'Install Node.js' - - script: | - yarn + - script: yarn displayName: 'Install dependencies and build' - script: | yarn start & @@ -119,9 +108,7 @@ jobs: inputs: versionSpec: '1.x' displayName: 'Install Yarn' - - script: | - yarn + - script: yarn displayName: 'Install dependencies and build' - - script: | - NPM_AUTH_TOKEN="$(NPM_AUTH_TOKEN)" node ./bin/publish.js + - script: NPM_AUTH_TOKEN="$(NPM_AUTH_TOKEN)" node ./bin/publish.js displayName: 'Package and publish to npm' From a450bd52d7a5c12e9ce6767774c653c0b0f0547b Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 30 Oct 2019 20:18:32 -0700 Subject: [PATCH 05/92] Generate and publish coverage report to pipelines --- .gitignore | 1 + azure-pipelines.yml | 17 ++ package.json | 1 + yarn.lock | 522 +++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 530 insertions(+), 11 deletions(-) diff --git a/.gitignore b/.gitignore index 64273dda..8aea04b2 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ node_modules/ lib/ out/ out-test/ +.nyc_output/ Makefile.gyp *.Makefile *.target.gyp.mk diff --git a/azure-pipelines.yml b/azure-pipelines.yml index c6ee9ac0..cab8fa51 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -22,6 +22,23 @@ jobs: displayName: 'Unit tests' - script: yarn lint displayName: 'Lint' + - script: | + NODE_PATH=$(pwd)/out ./node_modules/.bin/nyc ./node_modules/.bin/mocha './out/*test.js' './out/**/*test.js' + ./node_modules/.bin/nyc report --reporter=cobertura + displayName: 'Coverage report' + - task: PublishCodeCoverageResults@1 + inputs: + codeCoverageTool: Cobertura + summaryFileLocation: '$(System.DefaultWorkingDirectory)/**/*coverage.xml' + reportDirectory: '$(System.DefaultWorkingDirectory)/**/coverage' + displayName: 'Publish coverage' + - task: mspremier.BuildQualityChecks.QualityChecks-task.BuildQualityChecks@6 + displayName: 'Check build quality' + inputs: + checkCoverage: true + coverageType: branches + coverageThreshold: 55 + coverageFailOption: fixed - job: macOS pool: diff --git a/package.json b/package.json index fb9592c7..fa29486a 100644 --- a/package.json +++ b/package.json @@ -44,6 +44,7 @@ "jsdom": "^11.11.0", "mocha": "^6.1.4", "node-pty": "0.7.6", + "nyc": "13", "puppeteer": "^1.15.0", "source-map-loader": "^0.2.4", "ts-loader": "^6.0.4", diff --git a/yarn.lock b/yarn.lock index 0019809a..c96c390c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9,6 +9,46 @@ dependencies: "@babel/highlight" "^7.0.0" +"@babel/code-frame@^7.5.5": + version "7.5.5" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.5.5.tgz#bc0782f6d69f7b7d49531219699b988f669a8f9d" + integrity sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw== + dependencies: + "@babel/highlight" "^7.0.0" + +"@babel/generator@^7.4.0", "@babel/generator@^7.6.3": + version "7.6.4" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.6.4.tgz#a4f8437287bf9671b07f483b76e3bb731bc97671" + integrity sha512-jsBuXkFoZxk0yWLyGI9llT9oiQ2FeTASmRFE32U+aaDTfoE92t78eroO7PTpU/OrYq38hlcDM6vbfLDaOLy+7w== + dependencies: + "@babel/types" "^7.6.3" + jsesc "^2.5.1" + lodash "^4.17.13" + source-map "^0.5.0" + +"@babel/helper-function-name@^7.1.0": + version "7.1.0" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.1.0.tgz#a0ceb01685f73355d4360c1247f582bfafc8ff53" + integrity sha512-A95XEoCpb3TO+KZzJ4S/5uW5fNe26DjBGqf1o9ucyLyCmi1dXq/B3c8iaWTfBk3VvetUxl16e8tIrd5teOCfGw== + dependencies: + "@babel/helper-get-function-arity" "^7.0.0" + "@babel/template" "^7.1.0" + "@babel/types" "^7.0.0" + +"@babel/helper-get-function-arity@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0.tgz#83572d4320e2a4657263734113c42868b64e49c3" + integrity sha512-r2DbJeg4svYvt3HOS74U4eWKsUAMRH01Z1ds1zx8KNTPtpTL5JAsdFv8BNyOpVqdFhHkkRDIg5B4AsxmkjAlmQ== + dependencies: + "@babel/types" "^7.0.0" + +"@babel/helper-split-export-declaration@^7.4.4": + version "7.4.4" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.4.4.tgz#ff94894a340be78f53f06af038b205c49d993677" + integrity sha512-Ro/XkzLf3JFITkW6b+hNxzZ1n5OQ80NvIUdmHspih1XAhtN3vPTuUFT4eQnela+2MaZ5ulH+iyP513KJrxbN7Q== + dependencies: + "@babel/types" "^7.4.4" + "@babel/highlight@^7.0.0": version "7.0.0" resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.0.0.tgz#f710c38c8d458e6dd9a201afb637fcb781ce99e4" @@ -18,6 +58,44 @@ esutils "^2.0.2" js-tokens "^4.0.0" +"@babel/parser@^7.4.3", "@babel/parser@^7.6.0", "@babel/parser@^7.6.3": + version "7.6.4" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.6.4.tgz#cb9b36a7482110282d5cb6dd424ec9262b473d81" + integrity sha512-D8RHPW5qd0Vbyo3qb+YjO5nvUVRTXFLQ/FsDxJU2Nqz4uB5EnUN0ZQSEYpvTIbRuttig1XbHWU5oMeQwQSAA+A== + +"@babel/template@^7.1.0", "@babel/template@^7.4.0": + version "7.6.0" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.6.0.tgz#7f0159c7f5012230dad64cca42ec9bdb5c9536e6" + integrity sha512-5AEH2EXD8euCk446b7edmgFdub/qfH1SN6Nii3+fyXP807QRx9Q73A2N5hNwRRslC2H9sNzaFhsPubkS4L8oNQ== + dependencies: + "@babel/code-frame" "^7.0.0" + "@babel/parser" "^7.6.0" + "@babel/types" "^7.6.0" + +"@babel/traverse@^7.4.3": + version "7.6.3" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.6.3.tgz#66d7dba146b086703c0fb10dd588b7364cec47f9" + integrity sha512-unn7P4LGsijIxaAJo/wpoU11zN+2IaClkQAxcJWBNCMS6cmVh802IyLHNkAjQ0iYnRS3nnxk5O3fuXW28IMxTw== + dependencies: + "@babel/code-frame" "^7.5.5" + "@babel/generator" "^7.6.3" + "@babel/helper-function-name" "^7.1.0" + "@babel/helper-split-export-declaration" "^7.4.4" + "@babel/parser" "^7.6.3" + "@babel/types" "^7.6.3" + debug "^4.1.0" + globals "^11.1.0" + lodash "^4.17.13" + +"@babel/types@^7.0.0", "@babel/types@^7.4.0", "@babel/types@^7.4.4", "@babel/types@^7.6.0", "@babel/types@^7.6.3": + version "7.6.3" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.6.3.tgz#3f07d96f854f98e2fbd45c64b0cb942d11e8ba09" + integrity sha512-CqbcpTxMcpuQTMhjI37ZHVgjBkysg5icREQIEZ0eG1yCNwg3oy+5AaLiOKmjsCj6nqOsa6Hf0ObjRVwokb7srA== + dependencies: + esutils "^2.0.2" + lodash "^4.17.13" + to-fast-properties "^2.0.0" + "@fimbul/bifrost@^0.11.0": version "0.11.0" resolved "https://registry.yarnpkg.com/@fimbul/bifrost/-/bifrost-0.11.0.tgz#83cacc21464198b12e3cc1c2204ae6c6d7afd158" @@ -439,11 +517,23 @@ app-root-path@^2.2.1: resolved "https://registry.yarnpkg.com/app-root-path/-/app-root-path-2.2.1.tgz#d0df4a682ee408273583d43f6f79e9892624bc9a" integrity sha512-91IFKeKk7FjfmezPKkwtaRvSpnUc4gDwPAjA1YZ9Gn0q0PPeW+vbeUsZuyDwjI7+QTHhcLen2v25fi/AmhvbJA== +append-transform@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/append-transform/-/append-transform-1.0.0.tgz#046a52ae582a228bd72f58acfbe2967c678759ab" + integrity sha512-P009oYkeHyU742iSZJzZZywj4QRJdnTWffaKuJQLablCZ1uz6/cW4yaRgcDaoQ+uwOxxnt0gRUcwfsNP2ri0gw== + dependencies: + default-require-extensions "^2.0.0" + aproba@^1.0.3, aproba@^1.1.1: version "1.2.0" resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw== +archy@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/archy/-/archy-1.0.0.tgz#f9c8c13757cc1dd7bc379ac77b2c62a5c2868c40" + integrity sha1-+cjBN1fMHde8N5rHeyxipcKGjEA= + are-we-there-yet@~1.1.2: version "1.1.5" resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz#4b35c2944f062a8bfcda66410760350fe9ddfc21" @@ -489,6 +579,11 @@ array-unique@^0.3.2: resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg= +arrify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" + integrity sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0= + asn1.js@^4.0.0: version "4.10.1" resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-4.10.1.tgz#b9c2bf5805f1e64aadeed6df3a2bfafb5a73f5a0" @@ -816,6 +911,16 @@ cache-base@^1.0.1: union-value "^1.0.0" unset-value "^1.0.0" +caching-transform@^3.0.1: + version "3.0.2" + resolved "https://registry.yarnpkg.com/caching-transform/-/caching-transform-3.0.2.tgz#601d46b91eca87687a281e71cef99791b0efca70" + integrity sha512-Mtgcv3lh3U0zRii/6qVgQODdPA4G3zhG+jtbCWj39RXuUFTMzH0vcdMtaJS1jPowd+It2Pqr6y3NJMQqOqCE2w== + dependencies: + hasha "^3.0.0" + make-dir "^2.0.0" + package-hash "^3.0.0" + write-file-atomic "^2.4.2" + camelcase@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd" @@ -1075,6 +1180,11 @@ commander@^2.20.0: resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.0.tgz#d58bb2b5c1ee8f87b0d340027e9e94e222c5a422" integrity sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ== +commander@~2.20.3: + version "2.20.3" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" + integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== + commondir@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" @@ -1134,6 +1244,13 @@ content-type@~1.0.4: resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== +convert-source-map@^1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.6.0.tgz#51b537a8c43e0f04dec1993bffcdd504e758ac20" + integrity sha512-eFu7XigvxdZ1ETfbgPBohgyQ/Z++C0eEhTor0qRwBw9unw+L0/6V8wkSuGgzdThkiS5lSpdptOQPD8Ak40a+7A== + dependencies: + safe-buffer "~5.1.1" + cookie-signature@1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" @@ -1197,6 +1314,14 @@ create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4: safe-buffer "^5.0.1" sha.js "^2.4.8" +cross-spawn@^4: + version "4.0.2" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-4.0.2.tgz#7b9247621c23adfdd3856004a823cbe397424d41" + integrity sha1-e5JHYhwjrf3ThWAEqCPL45dCTUE= + dependencies: + lru-cache "^4.0.1" + which "^1.2.9" + cross-spawn@^5.0.1: version "5.1.0" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449" @@ -1293,7 +1418,7 @@ debug@^3.1.0: dependencies: ms "2.0.0" -debug@^4.0.1, debug@^4.1.0: +debug@^4.0.1, debug@^4.1.0, debug@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw== @@ -1339,6 +1464,13 @@ deep-is@~0.1.3: resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= +default-require-extensions@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/default-require-extensions/-/default-require-extensions-2.0.0.tgz#f5f8fbb18a7d6d50b21f641f649ebb522cfe24f7" + integrity sha1-9fj7sYp9bVCyH2QfZJ67Uiz+JPc= + dependencies: + strip-bom "^3.0.0" + defaults@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.3.tgz#c656051e9817d9ff08ed881477f3fe4019f3ef7d" @@ -1536,6 +1668,13 @@ errno@^0.1.3, errno@~0.1.7: dependencies: prr "~1.0.1" +error-ex@^1.3.1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" + integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== + dependencies: + is-arrayish "^0.2.1" + es-abstract@^1.5.1: version "1.13.0" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.13.0.tgz#ac86145fdd5099d8dd49558ccba2eaf9b88e24e9" @@ -1557,6 +1696,11 @@ es-to-primitive@^1.2.0: is-date-object "^1.0.1" is-symbol "^1.0.2" +es6-error@^4.0.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/es6-error/-/es6-error-4.1.1.tgz#9e3af407459deed47e9a91f9b885a84eb05c561d" + integrity sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg== + es6-promise@^4.0.3: version "4.2.6" resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.6.tgz#b685edd8258886365ea62b57d30de28fadcd974f" @@ -1878,7 +2022,7 @@ finalhandler@~1.1.2: statuses "~1.5.0" unpipe "~1.0.0" -find-cache-dir@^2.1.0: +find-cache-dir@^2.0.0, find-cache-dir@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-2.1.0.tgz#8d0f94cd13fe43c6c7c261a0d86115ca918c05f7" integrity sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ== @@ -1921,6 +2065,14 @@ for-in@^1.0.2: resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" integrity sha1-gQaNKVqBQuwKxybG4iAMMPttXoA= +foreground-child@^1.5.6: + version "1.5.6" + resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-1.5.6.tgz#4fd71ad2dfde96789b980a5c0a295937cb2f5ce9" + integrity sha1-T9ca0t/elnibmApcCilZN8svXOk= + dependencies: + cross-spawn "^4" + signal-exit "^3.0.0" + forever-agent@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" @@ -2097,6 +2249,11 @@ global-modules-path@^2.1.0: resolved "https://registry.yarnpkg.com/global-modules-path/-/global-modules-path-2.3.0.tgz#b0e2bac6beac39745f7db5c59d26a36a0b94f7dc" integrity sha512-HchvMJNYh9dGSCy8pOQ2O8u/hoXaL+0XhnrwH0RyLiSXMMTl9W3N6KUU73+JFOg5PGjtzl6VZzUQsnrpm7Szag== +globals@^11.1.0: + version "11.12.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" + integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== + graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2: version "4.2.2" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.2.tgz#6f0952605d0140c1cfdb138ed005775b92d67b02" @@ -2107,6 +2264,17 @@ growl@1.10.5: resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e" integrity sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA== +handlebars@^4.1.2: + version "4.5.1" + resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.5.1.tgz#8a01c382c180272260d07f2d1aa3ae745715c7ba" + integrity sha512-C29UoFzHe9yM61lOsIlCE5/mQVGrnIOrOq7maQl76L7tYPCgC1og0Ajt6uWnX4ZTxBPnjw+CUvawphwCfJgUnA== + dependencies: + neo-async "^2.6.0" + optimist "^0.6.1" + source-map "^0.6.1" + optionalDependencies: + uglify-js "^3.1.4" + har-schema@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" @@ -2189,6 +2357,13 @@ hash.js@^1.0.0, hash.js@^1.0.3: inherits "^2.0.3" minimalistic-assert "^1.0.1" +hasha@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/hasha/-/hasha-3.0.0.tgz#52a32fab8569d41ca69a61ff1a214f8eb7c8bd39" + integrity sha1-UqMvq4Vp1BymmmH/GiFPjrfIvTk= + dependencies: + is-stream "^1.0.1" + he@1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" @@ -2203,6 +2378,11 @@ hmac-drbg@^1.0.0: minimalistic-assert "^1.0.0" minimalistic-crypto-utils "^1.0.1" +hosted-git-info@^2.1.4: + version "2.8.5" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.5.tgz#759cfcf2c4d156ade59b0b2dfabddc42a6b9c70c" + integrity sha512-kssjab8CvdXfcXMXVcvsXum4Hwdq9XGtRD3TteMEvEbq0LXyiNQr6AprqKqfeaDXze7SxWvRxdpwE6ku7ikLkg== + html-encoding-sniffer@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz#e70d84b94da53aa375e11fe3a351be6642ca46f8" @@ -2387,6 +2567,11 @@ is-accessor-descriptor@^1.0.0: dependencies: kind-of "^6.0.0" +is-arrayish@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= + is-arrayish@^0.3.1: version "0.3.2" resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.3.2.tgz#4574a2ae56f7ab206896fb431eaeed066fdf8f03" @@ -2525,7 +2710,7 @@ is-regex@^1.0.4: dependencies: has "^1.0.1" -is-stream@^1.1.0: +is-stream@^1.0.1, is-stream@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= @@ -2579,6 +2764,58 @@ isstream@~0.1.2: resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= +istanbul-lib-coverage@^2.0.3, istanbul-lib-coverage@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz#675f0ab69503fad4b1d849f736baaca803344f49" + integrity sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA== + +istanbul-lib-hook@^2.0.3: + version "2.0.7" + resolved "https://registry.yarnpkg.com/istanbul-lib-hook/-/istanbul-lib-hook-2.0.7.tgz#c95695f383d4f8f60df1f04252a9550e15b5b133" + integrity sha512-vrRztU9VRRFDyC+aklfLoeXyNdTfga2EI3udDGn4cZ6fpSXpHLV9X6CHvfoMCPtggg8zvDDmC4b9xfu0z6/llA== + dependencies: + append-transform "^1.0.0" + +istanbul-lib-instrument@^3.1.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-3.3.0.tgz#a5f63d91f0bbc0c3e479ef4c5de027335ec6d630" + integrity sha512-5nnIN4vo5xQZHdXno/YDXJ0G+I3dAm4XgzfSVTPLQpj/zAV2dV6Juy0yaf10/zrJOJeHoN3fraFe+XRq2bFVZA== + dependencies: + "@babel/generator" "^7.4.0" + "@babel/parser" "^7.4.3" + "@babel/template" "^7.4.0" + "@babel/traverse" "^7.4.3" + "@babel/types" "^7.4.0" + istanbul-lib-coverage "^2.0.5" + semver "^6.0.0" + +istanbul-lib-report@^2.0.4: + version "2.0.8" + resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-2.0.8.tgz#5a8113cd746d43c4889eba36ab10e7d50c9b4f33" + integrity sha512-fHBeG573EIihhAblwgxrSenp0Dby6tJMFR/HvlerBsrCTD5bkUuoNtn3gVh29ZCS824cGGBPn7Sg7cNk+2xUsQ== + dependencies: + istanbul-lib-coverage "^2.0.5" + make-dir "^2.1.0" + supports-color "^6.1.0" + +istanbul-lib-source-maps@^3.0.2: + version "3.0.6" + resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.6.tgz#284997c48211752ec486253da97e3879defba8c8" + integrity sha512-R47KzMtDJH6X4/YW9XTx+jrLnZnscW4VpNN+1PViSYTejLVPWv7oov+Duf8YQSPyVRUvueQqz1TcsC6mooZTXw== + dependencies: + debug "^4.1.1" + istanbul-lib-coverage "^2.0.5" + make-dir "^2.1.0" + rimraf "^2.6.3" + source-map "^0.6.1" + +istanbul-reports@^2.1.1: + version "2.2.6" + resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-2.2.6.tgz#7b4f2660d82b29303a8fe6091f8ca4bf058da1af" + integrity sha512-SKi4rnMyLBKe0Jy2uUdx28h8oG7ph2PPuQPvIAh31d+Ci+lSiEu4C+h3oBPuJ9+mPKhOyW0M8gY4U5NM1WLeXA== + dependencies: + handlebars "^4.1.2" + javascript-natural-sort@0.7.1: version "0.7.1" resolved "https://registry.yarnpkg.com/javascript-natural-sort/-/javascript-natural-sort-0.7.1.tgz#f9e2303d4507f6d74355a73664d1440fb5a0ef59" @@ -2634,7 +2871,12 @@ jsdom@^11.11.0: ws "^4.0.0" xml-name-validator "^3.0.0" -json-parse-better-errors@^1.0.2: +jsesc@^2.5.1: + version "2.5.2" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" + integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== + +json-parse-better-errors@^1.0.1, json-parse-better-errors@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw== @@ -2739,6 +2981,16 @@ levn@~0.3.0: prelude-ls "~1.1.2" type-check "~0.3.2" +load-json-file@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-4.0.0.tgz#2f5f45ab91e33216234fd53adab668eb4ec0993b" + integrity sha1-L19Fq5HjMhYjT9U62rZo607AmTs= + dependencies: + graceful-fs "^4.1.2" + parse-json "^4.0.0" + pify "^3.0.0" + strip-bom "^3.0.0" + loader-runner@^2.4.0: version "2.4.0" resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.4.0.tgz#ed47066bfe534d7e84c4c7b9998c2a75607d9357" @@ -2778,6 +3030,11 @@ locate-path@^3.0.0: p-locate "^3.0.0" path-exists "^3.0.0" +lodash.flattendeep@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz#fb030917f86a3134e5bc9bec0d69e0013ddfedb2" + integrity sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI= + lodash.sortby@^4.7.0: version "4.7.0" resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" @@ -2788,6 +3045,11 @@ lodash@^4.13.1, lodash@^4.17.10, lodash@^4.17.11: resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.14.tgz#9ce487ae66c96254fe20b599f21b6816028078ba" integrity sha512-mmKYbW3GLuJeX+iGP+Y7Gp1AiGHGbXHCOh/jZmrawMmsE7MS4znI3RL2FsjbqOyMayHInjOeykW7PEajUk1/xw== +lodash@^4.17.13: + version "4.17.15" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" + integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== + log-symbols@2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-2.2.0.tgz#5740e1c5d6f0dfda4ad9323b5332107ef6b4c40a" @@ -2821,7 +3083,14 @@ lru-cache@^5.1.1: dependencies: yallist "^3.0.2" -make-dir@^2.0.0: +make-dir@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.3.0.tgz#79c1033b80515bd6d24ec9933e860ca75ee27f0c" + integrity sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ== + dependencies: + pify "^3.0.0" + +make-dir@^2.0.0, make-dir@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5" integrity sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA== @@ -2910,6 +3179,13 @@ merge-descriptors@1.0.1: resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E= +merge-source-map@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/merge-source-map/-/merge-source-map-1.1.0.tgz#2fdde7e6020939f70906a68f2d7ae685e4c8c646" + integrity sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw== + dependencies: + source-map "^0.6.1" + methods@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" @@ -3021,6 +3297,11 @@ minimist@^1.2.0: resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ= +minimist@~0.0.1: + version "0.0.10" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.10.tgz#de3f98543dbf96082be48ad1a0c7cda836301dcf" + integrity sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8= + minipass@^2.2.1, minipass@^2.3.5: version "2.5.0" resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.5.0.tgz#dddb1d001976978158a05badfcbef4a771612857" @@ -3164,7 +3445,7 @@ negotiator@0.6.2: resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw== -neo-async@^2.5.0, neo-async@^2.6.1: +neo-async@^2.5.0, neo-async@^2.6.0, neo-async@^2.6.1: version "2.6.1" resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.1.tgz#ac27ada66167fa8849a6addd837f6b189ad2081c" integrity sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw== @@ -3242,6 +3523,16 @@ nopt@^4.0.1: abbrev "1" osenv "^0.1.4" +normalize-package-data@^2.3.2: + version "2.5.0" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" + integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== + dependencies: + hosted-git-info "^2.1.4" + resolve "^1.10.0" + semver "2 || 3 || 4 || 5" + validate-npm-package-license "^3.0.1" + normalize-path@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" @@ -3294,6 +3585,36 @@ nwsapi@^2.0.0: resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.0.4.tgz#dc79040a5f77b97716dc79565fc7fc3ef7d50570" integrity sha512-Zt6HRR6RcJkuj5/N9zeE7FN6YitRW//hK2wTOwX274IBphbY3Zf5+yn5mZ9v/SzAOTMjQNxZf9KkmPLWn0cV4g== +nyc@13: + version "13.3.0" + resolved "https://registry.yarnpkg.com/nyc/-/nyc-13.3.0.tgz#da4dbe91a9c8b9ead3f4f3344c76f353e3c78c75" + integrity sha512-P+FwIuro2aFG6B0Esd9ZDWUd51uZrAEoGutqZxzrVmYl3qSfkLgcQpBPBjtDFsUQLFY1dvTQJPOyeqr8S9GF8w== + dependencies: + archy "^1.0.0" + arrify "^1.0.1" + caching-transform "^3.0.1" + convert-source-map "^1.6.0" + find-cache-dir "^2.0.0" + find-up "^3.0.0" + foreground-child "^1.5.6" + glob "^7.1.3" + istanbul-lib-coverage "^2.0.3" + istanbul-lib-hook "^2.0.3" + istanbul-lib-instrument "^3.1.0" + istanbul-lib-report "^2.0.4" + istanbul-lib-source-maps "^3.0.2" + istanbul-reports "^2.1.1" + make-dir "^1.3.0" + merge-source-map "^1.1.0" + resolve-from "^4.0.0" + rimraf "^2.6.3" + signal-exit "^3.0.2" + spawn-wrap "^1.4.2" + test-exclude "^5.1.0" + uuid "^3.3.2" + yargs "^12.0.5" + yargs-parser "^11.1.1" + oauth-sign@~0.8.2: version "0.8.2" resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43" @@ -3376,6 +3697,14 @@ onetime@^2.0.0: dependencies: mimic-fn "^1.0.0" +optimist@^0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.6.1.tgz#da3ea74686fa21a19a111c326e90eb15a0196686" + integrity sha1-2j6nRob6IaGaERwybpDrFaAZZoY= + dependencies: + minimist "~0.0.1" + wordwrap "~0.0.2" + optionator@^0.8.1: version "0.8.2" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.2.tgz#364c5e409d3f4d6301d6c0b4c05bba50180aeb64" @@ -3393,7 +3722,7 @@ os-browserify@^0.3.0: resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27" integrity sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc= -os-homedir@^1.0.0: +os-homedir@^1.0.0, os-homedir@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" integrity sha1-/7xJiDNuDoM94MFox+8VISGqf7M= @@ -3482,6 +3811,16 @@ p-try@^2.0.0: resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== +package-hash@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/package-hash/-/package-hash-3.0.0.tgz#50183f2d36c9e3e528ea0a8605dff57ce976f88e" + integrity sha512-lOtmukMDVvtkL84rJHI7dpTYq+0rli8N2wlnqUcBuDWCfVhRUfOmnR9SsoHFMLpACvEV60dX7rd0rFaYDZI+FA== + dependencies: + graceful-fs "^4.1.15" + hasha "^3.0.0" + lodash.flattendeep "^4.4.0" + release-zalgo "^1.0.0" + pako@~1.0.5: version "1.0.10" resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.10.tgz#4328badb5086a426aa90f541977d4955da5c9732" @@ -3508,6 +3847,14 @@ parse-asn1@^5.0.0: pbkdf2 "^3.0.3" safe-buffer "^5.1.1" +parse-json@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0" + integrity sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA= + dependencies: + error-ex "^1.3.1" + json-parse-better-errors "^1.0.1" + parse5@4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/parse5/-/parse5-4.0.0.tgz#6d78656e3da8d78b4ec0b906f7c08ef1dfe3f608" @@ -3560,11 +3907,23 @@ path-parse@^1.0.5: resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.5.tgz#3c1adf871ea9cd6c9431b6ea2bd74a0ff055c4c1" integrity sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME= +path-parse@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" + integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== + path-to-regexp@0.1.7: version "0.1.7" resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w= +path-type@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f" + integrity sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg== + dependencies: + pify "^3.0.0" + pbkdf2@^3.0.3: version "3.0.17" resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.0.17.tgz#976c206530617b14ebb32114239f7b09336e93a6" @@ -3591,6 +3950,11 @@ picomatch@^2.0.5: resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.0.7.tgz#514169d8c7cd0bdbeecc8a2609e34a7163de69f6" integrity sha512-oLHIdio3tZ0qH76NybpeneBhYVj0QFTfXEFTc/B3zKQspYfYYkWYgFsmzo+4kvId/bQRcNkVeguI3y+CD22BtA== +pify@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" + integrity sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY= + pify@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" @@ -3813,6 +4177,23 @@ rc@^1.2.7: minimist "^1.2.0" strip-json-comments "~2.0.1" +read-pkg-up@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-4.0.0.tgz#1b221c6088ba7799601c808f91161c66e58f8978" + integrity sha512-6etQSH7nJGsK0RbG/2TeDzZFa8shjQ1um+SwQQ5cwKy0dhSXdOncEhb1CPpvQG4h7FyOV6EB6YlV0yJvZQNAkA== + dependencies: + find-up "^3.0.0" + read-pkg "^3.0.0" + +read-pkg@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-3.0.0.tgz#9cbc686978fee65d16c00e2b19c237fcf6e38389" + integrity sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k= + dependencies: + load-json-file "^4.0.0" + normalize-package-data "^2.3.2" + path-type "^3.0.0" + "readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.6, readable-stream@~2.3.6: version "2.3.6" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf" @@ -3857,6 +4238,13 @@ regex-not@^1.0.0, regex-not@^1.0.2: extend-shallow "^3.0.2" safe-regex "^1.1.0" +release-zalgo@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/release-zalgo/-/release-zalgo-1.0.0.tgz#09700b7e5074329739330e535c5a90fb67851730" + integrity sha1-CXALflB0Mpc5Mw5TXFqQ+2eFFzA= + dependencies: + es6-error "^4.0.1" + remove-trailing-separator@^1.0.1: version "1.1.0" resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" @@ -3941,6 +4329,11 @@ resolve-from@^3.0.0: resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748" integrity sha1-six699nWiBvItuZTM17rywoYh0g= +resolve-from@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" + integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== + resolve-url@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" @@ -3951,6 +4344,13 @@ resolve@1.1.7: resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" integrity sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs= +resolve@^1.10.0: + version "1.12.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.12.0.tgz#3fc644a35c84a48554609ff26ec52b66fa577df6" + integrity sha512-B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w== + dependencies: + path-parse "^1.0.6" + resolve@^1.3.2: version "1.8.1" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.8.1.tgz#82f1ec19a423ac1fbd080b0bab06ba36e84a7a26" @@ -3971,7 +4371,7 @@ ret@~0.1.10: resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== -rimraf@^2.5.4, rimraf@^2.6.1, rimraf@^2.6.3: +rimraf@^2.5.4, rimraf@^2.6.1, rimraf@^2.6.2, rimraf@^2.6.3: version "2.7.1" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== @@ -4048,7 +4448,7 @@ seed-random@2.2.0: resolved "https://registry.yarnpkg.com/seed-random/-/seed-random-2.2.0.tgz#2a9b19e250a817099231a5b99a4daf80b7fbed54" integrity sha1-KpsZ4lCoFwmSMaW5mk2vgLf77VQ= -semver@^5.3.0, semver@^5.6.0: +"semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.6.0: version "5.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== @@ -4233,7 +4633,7 @@ source-map-url@^0.4.0: resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3" integrity sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM= -source-map@^0.5.6: +source-map@^0.5.0, source-map@^0.5.6: version "0.5.7" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= @@ -4243,6 +4643,44 @@ source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1: resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== +spawn-wrap@^1.4.2: + version "1.4.3" + resolved "https://registry.yarnpkg.com/spawn-wrap/-/spawn-wrap-1.4.3.tgz#81b7670e170cca247d80bf5faf0cfb713bdcf848" + integrity sha512-IgB8md0QW/+tWqcavuFgKYR/qIRvJkRLPJDFaoXtLLUaVcCDK0+HeFTkmQHj3eprcYhc+gOl0aEA1w7qZlYezw== + dependencies: + foreground-child "^1.5.6" + mkdirp "^0.5.0" + os-homedir "^1.0.1" + rimraf "^2.6.2" + signal-exit "^3.0.2" + which "^1.3.0" + +spdx-correct@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.0.tgz#fb83e504445268f154b074e218c87c003cd31df4" + integrity sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q== + dependencies: + spdx-expression-parse "^3.0.0" + spdx-license-ids "^3.0.0" + +spdx-exceptions@^2.1.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz#2ea450aee74f2a89bfb94519c07fcd6f41322977" + integrity sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA== + +spdx-expression-parse@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz#99e119b7a5da00e05491c9fa338b7904823b41d0" + integrity sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg== + dependencies: + spdx-exceptions "^2.1.0" + spdx-license-ids "^3.0.0" + +spdx-license-ids@^3.0.0: + version "3.0.5" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz#3694b5804567a458d3c8045842a6358632f62654" + integrity sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q== + split-string@^3.0.1, split-string@^3.0.2: version "3.1.0" resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" @@ -4401,6 +4839,11 @@ strip-ansi@^5.1.0: dependencies: ansi-regex "^4.1.0" +strip-bom@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" + integrity sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM= + strip-eof@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" @@ -4432,6 +4875,13 @@ supports-color@^5.4.0: dependencies: has-flag "^3.0.0" +supports-color@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.1.0.tgz#0764abc69c63d5ac842dd4867e8d025e880df8f3" + integrity sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ== + dependencies: + has-flag "^3.0.0" + symbol-tree@^3.2.2: version "3.2.2" resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.2.tgz#ae27db38f660a7ae2e1c3b7d1bc290819b8519e6" @@ -4479,6 +4929,16 @@ terser@^4.1.2: source-map "~0.6.1" source-map-support "~0.5.12" +test-exclude@^5.1.0: + version "5.2.3" + resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-5.2.3.tgz#c3d3e1e311eb7ee405e092dac10aefd09091eac0" + integrity sha512-M+oxtseCFO3EDtAaGH7iiej3CBkzXqFMbzqYAACdzKui4eZA+pq3tZEwChvOdNfa7xxy8BfbmgJSIr43cC/+2g== + dependencies: + glob "^7.1.3" + minimatch "^3.0.4" + read-pkg-up "^4.0.0" + require-main-filename "^2.0.0" + text-hex@1.0.x: version "1.0.0" resolved "https://registry.yarnpkg.com/text-hex/-/text-hex-1.0.0.tgz#69dc9c1b17446ee79a92bf5b884bb4b9127506f5" @@ -4521,6 +4981,11 @@ to-arraybuffer@^1.0.0: resolved "https://registry.yarnpkg.com/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz#7d229b1fcc637e466ca081180836a7aabff83f43" integrity sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M= +to-fast-properties@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" + integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4= + to-object-path@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af" @@ -4729,6 +5194,14 @@ typescript@^3.5.1: resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.5.1.tgz#ba72a6a600b2158139c5dd8850f700e231464202" integrity sha512-64HkdiRv1yYZsSe4xC1WVgamNigVYjlssIoaH2HcZF0+ijsk5YK2g0G34w9wJkze8+5ow4STd22AynfO6ZYYLw== +uglify-js@^3.1.4: + version "3.6.5" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.6.5.tgz#b0ee796d2ae7e25672e04f65629b997cd4b30bd6" + integrity sha512-7L3W+Npia1OCr5Blp4/Vw83tK1mu5gnoIURtT1fUVfQ3Kf8WStWV6NJz0fdoBJZls0KlweruRTLVe6XLafmy5g== + dependencies: + commander "~2.20.3" + source-map "~0.6.1" + union-value@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847" @@ -4830,11 +5303,24 @@ uuid@^3.1.0: resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131" integrity sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA== +uuid@^3.3.2: + version "3.3.3" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.3.tgz#4568f0216e78760ee1dbf3a4d2cf53e224112866" + integrity sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ== + v8-compile-cache@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.0.2.tgz#a428b28bb26790734c4fc8bc9fa106fccebf6a6c" integrity sha512-1wFuMUIM16MDJRCrpbpuEPTUGmM5QMUg0cr3KFwra2XgOgFcPGDQHDh3CszSCD2Zewc/dh/pamNEW8CbfDebUw== +validate-npm-package-license@^3.0.1: + version "3.0.4" + resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" + integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== + dependencies: + spdx-correct "^3.0.0" + spdx-expression-parse "^3.0.0" + vary@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" @@ -4962,7 +5448,7 @@ which-module@^2.0.0: resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= -which@1.3.1, which@^1.2.9: +which@1.3.1, which@^1.2.9, which@^1.3.0: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== @@ -4999,6 +5485,11 @@ winston@^3.2.1: triple-beam "^1.3.0" winston-transport "^4.3.0" +wordwrap@~0.0.2: + version "0.0.3" + resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107" + integrity sha1-o9XabNXAvAAI03I0u68b7WMFkQc= + wordwrap@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" @@ -5024,6 +5515,15 @@ wrappy@1: resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= +write-file-atomic@^2.4.2: + version "2.4.3" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-2.4.3.tgz#1fd2e9ae1df3e75b8d8c367443c692d4ca81f481" + integrity sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ== + dependencies: + graceful-fs "^4.1.11" + imurmurhash "^0.1.4" + signal-exit "^3.0.2" + ws@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/ws/-/ws-4.1.0.tgz#a979b5d7d4da68bf54efe0408967c324869a7289" From 39c0fdb54ad530466ed8bd72a75c387a7bde6fdf Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 30 Oct 2019 20:31:40 -0700 Subject: [PATCH 06/92] Remove report directory --- azure-pipelines.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index cab8fa51..8c35196a 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -30,7 +30,6 @@ jobs: inputs: codeCoverageTool: Cobertura summaryFileLocation: '$(System.DefaultWorkingDirectory)/**/*coverage.xml' - reportDirectory: '$(System.DefaultWorkingDirectory)/**/coverage' displayName: 'Publish coverage' - task: mspremier.BuildQualityChecks.QualityChecks-task.BuildQualityChecks@6 displayName: 'Check build quality' From 08b10ba65160aae9af802a2c6fea1d694ff5c64f Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 30 Oct 2019 21:13:09 -0700 Subject: [PATCH 07/92] Add note about unit test coverage in contributing --- CONTRIBUTING.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e8102a2e..d5efe9d2 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -52,3 +52,7 @@ By contributing code to xterm.js you: holder has explicitly granted the right to use it like this, through a compatible open source license or through a direct agreement with you.) + +### Test coverage + +One area that always needs attention is improving out unit test coverage, you can view the code coverage report on [Azure Pipelines](https://dev.azure.com/xtermjs/xterm.js/_build/latest?definitionId=3) by clicking the Code Coverage tab. From fbb9ef9f7f5f9b5cfa7e4fbfa419f0d0bbeabe54 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Thu, 31 Oct 2019 11:59:04 -0700 Subject: [PATCH 08/92] Change to tracking lines in code coverage --- azure-pipelines.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 8c35196a..177f581f 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -35,8 +35,8 @@ jobs: displayName: 'Check build quality' inputs: checkCoverage: true - coverageType: branches - coverageThreshold: 55 + coverageType: lines + coverageThreshold: 60 coverageFailOption: fixed - job: macOS From 1f4780a71ae4b5532a9597a569a819184de0c9ad Mon Sep 17 00:00:00 2001 From: Geraldo Neto Date: Sat, 2 Nov 2019 18:58:04 -0300 Subject: [PATCH 09/92] #2444: Fix wrapped lines search --- addons/xterm-addon-search/src/SearchAddon.ts | 57 +++++++++++++++----- 1 file changed, 43 insertions(+), 14 deletions(-) diff --git a/addons/xterm-addon-search/src/SearchAddon.ts b/addons/xterm-addon-search/src/SearchAddon.ts index 5893f79f..9d553d76 100644 --- a/addons/xterm-addon-search/src/SearchAddon.ts +++ b/addons/xterm-addon-search/src/SearchAddon.ts @@ -12,6 +12,11 @@ export interface ISearchOptions { incremental?: boolean; } +export interface ISearchPosition { + startCol: number; + startRow: number; +} + export interface ISearchResult { term: string; col: number; @@ -71,17 +76,23 @@ export class SearchAddon implements ITerminalAddon { this._initLinesCache(); + const searchPosition: ISearchPosition = { + startRow, + startCol + }; + // Search startRow - let result = this._findInLine(term, startRow, startCol, searchOptions); + let result = this._findInLine(term, searchPosition, searchOptions); // Search from startRow + 1 to end if (!result) { for (let y = startRow + 1; y < this._terminal.buffer.baseY + this._terminal.rows; y++) { - + searchPosition.startRow = y; + searchPosition.startCol = 0; // If the current line is wrapped line, increase index of column to ignore the previous scan // Otherwise, reset beginning column index to zero with set new unwrapped line index - result = this._findInLine(term, y, 0, searchOptions); + result = this._findInLine(term, searchPosition, searchOptions); if (result) { break; } @@ -90,7 +101,9 @@ export class SearchAddon implements ITerminalAddon { // If we hit the bottom and didn't search from the very top wrap back up if (!result && startRow !== 0) { for (let y = 0; y < startRow; y++) { - result = this._findInLine(term, y, 0, searchOptions); + searchPosition.startRow = y; + searchPosition.startCol = 0; + result = this._findInLine(term, searchPosition, searchOptions); if (result) { break; } @@ -131,21 +144,26 @@ export class SearchAddon implements ITerminalAddon { } this._initLinesCache(); + const searchPosition: ISearchPosition = { + startRow, + startCol + }; if (incremental) { - result = this._findInLine(term, startRow, startCol, searchOptions, false); + result = this._findInLine(term, searchPosition, searchOptions, false); if (!(result && result.row === startRow && result.col === startCol)) { - result = this._findInLine(term, startRow, startCol, searchOptions, true); + result = this._findInLine(term, searchPosition, searchOptions, true); } } else { - result = this._findInLine(term, startRow, startCol, searchOptions, isReverseSearch); + result = this._findInLine(term, searchPosition, searchOptions, isReverseSearch); } // Search from startRow - 1 to top if (!result) { - startCol = this._terminal.cols; + searchPosition.startCol = Math.max(searchPosition.startCol, this._terminal.cols); for (let y = startRow - 1; y >= 0; y--) { - result = this._findInLine(term, y, startCol, searchOptions, isReverseSearch); + searchPosition.startRow = y; + result = this._findInLine(term, searchPosition, searchOptions, isReverseSearch); if (result) { break; } @@ -154,7 +172,8 @@ export class SearchAddon implements ITerminalAddon { // If we hit the top and didn't search from the very bottom wrap back down if (!result && startRow !== (this._terminal.buffer.baseY + this._terminal.rows)) { for (let y = (this._terminal.buffer.baseY + this._terminal.rows); y > startRow; y--) { - result = this._findInLine(term, y, startCol, searchOptions, isReverseSearch); + searchPosition.startRow = y; + result = this._findInLine(term, searchPosition, searchOptions, isReverseSearch); if (result) { break; } @@ -213,18 +232,28 @@ export class SearchAddon implements ITerminalAddon { * started on an earlier line then it is skipped since it will be properly searched when the terminal line that the * text starts on is searched. * @param term The search term. - * @param row The line to start the search from. - * @param col The column to start the search from. + * @param {ISearchPosition} position The position to start the search. * @param searchOptions Search options. * @return The search result if it was found. */ - protected _findInLine(term: string, row: number, col: number, searchOptions: ISearchOptions = {}, isReverseSearch: boolean = false): ISearchResult | undefined { + protected _findInLine(term: string, searchPosition: ISearchPosition, searchOptions: ISearchOptions = {}, isReverseSearch: boolean = false): ISearchResult | undefined { const terminal = this._terminal!; + let row = searchPosition.startRow; + const col = searchPosition.startCol; // Ignore wrapped lines, only consider on unwrapped line (first row of command string). const firstLine = terminal.buffer.getLine(row); if (firstLine && firstLine.isWrapped) { - return; + if (isReverseSearch) { + searchPosition.startCol += terminal.cols; + } + else { + // 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 += terminal.cols; + return this._findInLine(term, searchPosition, searchOptions); + } } let stringLine = this._linesCache ? this._linesCache[row] : void 0; if (stringLine === void 0) { From 9d50d106e38e900d54155e1f1f24cc3bbd50ec4f Mon Sep 17 00:00:00 2001 From: Geraldo Neto Date: Sat, 2 Nov 2019 19:21:58 -0300 Subject: [PATCH 10/92] 2444: Return after calculating startCol in reverse search --- addons/xterm-addon-search/src/SearchAddon.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/addons/xterm-addon-search/src/SearchAddon.ts b/addons/xterm-addon-search/src/SearchAddon.ts index 9d553d76..3db1d6e1 100644 --- a/addons/xterm-addon-search/src/SearchAddon.ts +++ b/addons/xterm-addon-search/src/SearchAddon.ts @@ -246,6 +246,7 @@ export class SearchAddon implements ITerminalAddon { if (firstLine && firstLine.isWrapped) { if (isReverseSearch) { searchPosition.startCol += terminal.cols; + return; } else { // This will iterate until we find the line start. From 9358718b5ff767a427503c66eeaa4dff423747a9 Mon Sep 17 00:00:00 2001 From: Geraldo Neto Date: Sat, 2 Nov 2019 19:33:21 -0300 Subject: [PATCH 11/92] 2444: Fix no-else-return lint --- addons/xterm-addon-search/src/SearchAddon.ts | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/addons/xterm-addon-search/src/SearchAddon.ts b/addons/xterm-addon-search/src/SearchAddon.ts index b869209f..67f1ad6e 100644 --- a/addons/xterm-addon-search/src/SearchAddon.ts +++ b/addons/xterm-addon-search/src/SearchAddon.ts @@ -255,13 +255,12 @@ export class SearchAddon implements ITerminalAddon { searchPosition.startCol += terminal.cols; return; } - else { - // 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 += terminal.cols; - return this._findInLine(term, searchPosition, searchOptions); - } + + // 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 += terminal.cols; + return this._findInLine(term, searchPosition, searchOptions); } let stringLine = this._linesCache ? this._linesCache[row] : void 0; if (stringLine === void 0) { From 1afffba1d8bed670401fbef2391d2fb4de4c1c78 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Mon, 4 Nov 2019 11:00:19 -0800 Subject: [PATCH 12/92] Only refresh rows that changed during parsing Fixes #2534 --- src/InputHandler.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/InputHandler.ts b/src/InputHandler.ts index 7b6ea1fe..74208482 100644 --- a/src/InputHandler.ts +++ b/src/InputHandler.ts @@ -347,6 +347,9 @@ export class InputHandler extends Disposable implements IInputHandler { } } + // Clear the dirty row service so we know which lines changed as a result of parsing + this._dirtyRowService.clearRange(); + // process big data in smaller chunks if (data.length > MAX_PARSEBUFFER_LENGTH) { for (let i = 0; i < data.length; i += MAX_PARSEBUFFER_LENGTH) { @@ -367,6 +370,8 @@ export class InputHandler extends Disposable implements IInputHandler { if (buffer.x !== cursorStartX || buffer.y !== cursorStartY) { this._onCursorMove.fire(); } + + // Refresh any dirty rows accumulated as part of parsing this._terminal.refresh(this._dirtyRowService.start, this._dirtyRowService.end); } From 267fbc7f5ee63a9318b3f6d048b61c91685608c0 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Tue, 5 Nov 2019 10:54:02 -0800 Subject: [PATCH 13/92] typescript@3.7 and fix errors --- package.json | 2 +- src/browser/Clipboard.ts | 12 ++++++------ yarn.lock | 8 ++++---- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/package.json b/package.json index fa29486a..0150569d 100644 --- a/package.json +++ b/package.json @@ -50,7 +50,7 @@ "ts-loader": "^6.0.4", "tslint": "^5.18.0", "tslint-consistent-codestyle": "^1.13.0", - "typescript": "3.6", + "typescript": "3.7", "utf8": "^3.0.0", "webpack": "^4.35.3", "webpack-cli": "^3.1.0", diff --git a/src/browser/Clipboard.ts b/src/browser/Clipboard.ts index 594e4429..a7c48bfb 100644 --- a/src/browser/Clipboard.ts +++ b/src/browser/Clipboard.ts @@ -82,12 +82,12 @@ export function moveTextAreaUnderMouseCursor(ev: MouseEvent, textarea: HTMLTextA // Reset the terminal textarea's styling // Timeout needs to be long enough for click event to be handled. setTimeout(() => { - textarea.style.position = null; - textarea.style.width = null; - textarea.style.height = null; - textarea.style.left = null; - textarea.style.top = null; - textarea.style.zIndex = null; + textarea.style.position = ''; + textarea.style.width = ''; + textarea.style.height = ''; + textarea.style.left = ''; + textarea.style.top = ''; + textarea.style.zIndex = ''; }, 200); } diff --git a/yarn.lock b/yarn.lock index c96c390c..cb02cf91 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5184,10 +5184,10 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= -typescript@3.6: - version "3.6.3" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.6.3.tgz#fea942fabb20f7e1ca7164ff626f1a9f3f70b4da" - integrity sha512-N7bceJL1CtRQ2RiG0AQME13ksR7DiuQh/QehubYcghzv20tnh+MQnQIuJddTmsbqYj+dztchykemz0zFzlvdQw== +typescript@3.7: + version "3.7.2" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.7.2.tgz#27e489b95fa5909445e9fef5ee48d81697ad18fb" + integrity sha512-ml7V7JfiN2Xwvcer+XAf2csGO1bPBdRbFCkYBczNZggrBZ9c7G3riSUeJmqEU5uOtXNPMhE3n+R4FA/3YOAWOQ== typescript@^3.5.1: version "3.5.1" From 843d5ff53ca966b809fe7371fb95464f79cc3b88 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Tue, 5 Nov 2019 11:09:47 -0800 Subject: [PATCH 14/92] Adopt optional chaining --- src/Terminal.ts | 82 +++++++----------------- src/browser/MouseZoneManager.ts | 8 +-- src/browser/renderer/BaseRenderLayer.ts | 4 +- src/browser/services/SelectionService.ts | 4 +- 4 files changed, 27 insertions(+), 71 deletions(-) diff --git a/src/Terminal.ts b/src/Terminal.ts index 3e7e55ca..537ecb74 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -240,18 +240,12 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp return; } super.dispose(); - if (this._windowsMode) { - this._windowsMode.dispose(); - this._windowsMode = undefined; - } - if (this._renderService) { - this._renderService.dispose(); - } + this._windowsMode?.dispose(); + this._windowsMode = undefined; + this._renderService?.dispose(); this._customKeyEventHandler = null; this.write = () => {}; - if (this.element && this.element.parentNode) { - this.element.parentNode.removeChild(this.element); - } + this.element?.parentNode?.removeChild(this.element); } private _setup(): void { @@ -336,12 +330,8 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp case 'fontFamily': case 'fontSize': // When the font changes the size of the cells may change which requires a renderer clear - if (this._renderService) { - this._renderService.clear(); - } - if (this._charSizeService) { - this._charSizeService.measure(); - } + this._renderService?.clear(); + this._charSizeService?.measure(); break; case 'drawBoldTextInBrightColors': case 'letterSpacing': @@ -363,9 +353,7 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp break; case 'scrollback': this.buffers.resize(this.cols, this.rows); - if (this.viewport) { - this.viewport.syncScrollArea(); - } + this.viewport?.syncScrollArea(); break; case 'screenReaderMode': if (this.optionsService.options.screenReaderMode) { @@ -373,10 +361,8 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp this._accessibilityManager = new AccessibilityManager(this, this._renderService); } } else { - if (this._accessibilityManager) { - this._accessibilityManager.dispose(); - this._accessibilityManager = null; - } + this._accessibilityManager?.dispose(); + this._accessibilityManager = null; } break; case 'tabStopWidth': this.buffers.setupTabStops(); break; @@ -389,10 +375,8 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp this._windowsMode = applyWindowsMode(this); } } else { - if (this._windowsMode) { - this._windowsMode.dispose(); - this._windowsMode = undefined; - } + this._windowsMode?.dispose(); + this._windowsMode = undefined; } break; } @@ -665,15 +649,9 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp */ private _setTheme(theme: ITheme): void { this._theme = theme; - if (this._colorManager) { - this._colorManager.setTheme(theme); - } - if (this._renderService) { - this._renderService.setColors(this._colorManager.colors); - } - if (this.viewport) { - this.viewport.onThemeChange(this._colorManager.colors); - } + this._colorManager?.setTheme(theme); + this._renderService?.setColors(this._colorManager.colors); + this.viewport?.onThemeChange(this._colorManager.colors); } /** @@ -940,9 +918,7 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp * @param end The row to end at (between start and this.rows - 1). */ public refresh(start: number, end: number): void { - if (this._renderService) { - this._renderService.refreshRows(start, end); - } + this._renderService?.refreshRows(start, end); } /** @@ -951,9 +927,7 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp * @param end The row to end at (between start and this.rows - 1). */ private _queueLinkification(start: number, end: number): void { - if (this.linkifier) { - this.linkifier.linkifyRows(start, end); - } + this.linkifier?.linkifyRows(start, end); } /** @@ -1233,24 +1207,18 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp * Clears the current terminal selection. */ public clearSelection(): void { - if (this._selectionService) { - this._selectionService.clearSelection(); - } + this._selectionService?.clearSelection(); } /** * Selects all text within the terminal. */ public selectAll(): void { - if (this._selectionService) { - this._selectionService.selectAll(); - } + this._selectionService?.selectAll(); } public selectLines(start: number, end: number): void { - if (this._selectionService) { - this._selectionService.selectLines(start, end); - } + this._selectionService?.selectLines(start, end); } /** @@ -1457,9 +1425,7 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp this._bufferService.resize(x, y); this.buffers.setupTabStops(this.cols); - if (this._charSizeService) { - this._charSizeService.measure(); - } + this._charSizeService?.measure(); // Sync the scroll area to make sure scroll events don't fire and scroll the viewport to an // invalid location @@ -1551,9 +1517,7 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp this._bufferService.reset(); this._coreService.reset(); this._coreMouseService.reset(); - if (this._selectionService) { - this._selectionService.reset(); - } + this._selectionService?.reset(); // reattach this._customKeyEventHandler = customKeyEventHandler; @@ -1563,9 +1527,7 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp // do a full screen refresh this.refresh(0, this.rows - 1); - if (this.viewport) { - this.viewport.syncScrollArea(); - } + this.viewport?.syncScrollArea(); } // TODO: Remove cancel function and cancelEvents option diff --git a/src/browser/MouseZoneManager.ts b/src/browser/MouseZoneManager.ts index 7eb7c5f8..d697c133 100644 --- a/src/browser/MouseZoneManager.ts +++ b/src/browser/MouseZoneManager.ts @@ -175,11 +175,9 @@ export class MouseZoneManager extends Disposable implements IMouseZoneManager { // Find the active zone, prevent event propagation if found to prevent other // components from handling the mouse event. const zone = this._findZoneEventAt(e); - if (zone) { - if (zone.willLinkActivate(e)) { - e.preventDefault(); - e.stopImmediatePropagation(); - } + if (zone?.willLinkActivate(e)) { + e.preventDefault(); + e.stopImmediatePropagation(); } } diff --git a/src/browser/renderer/BaseRenderLayer.ts b/src/browser/renderer/BaseRenderLayer.ts index e9ad5b3a..a33bef28 100644 --- a/src/browser/renderer/BaseRenderLayer.ts +++ b/src/browser/renderer/BaseRenderLayer.ts @@ -60,9 +60,7 @@ export abstract class BaseRenderLayer implements IRenderLayer { public dispose(): void { this._container.removeChild(this._canvas); - if (this._charAtlas) { - this._charAtlas.dispose(); - } + this._charAtlas?.dispose(); } private _initCanvas(): void { diff --git a/src/browser/services/SelectionService.ts b/src/browser/services/SelectionService.ts index 3631bbb7..4eb6550c 100644 --- a/src/browser/services/SelectionService.ts +++ b/src/browser/services/SelectionService.ts @@ -688,9 +688,7 @@ export class SelectionService 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. - if (this._trimListener) { - this._trimListener.dispose(); - } + this._trimListener.dispose(); this._trimListener = e.activeBuffer.lines.onTrim(amount => this._onTrim(amount)); } From 3dca46619c8ad4d6ca1879e24208e349ef26db62 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Tue, 5 Nov 2019 11:30:11 -0800 Subject: [PATCH 15/92] Skip puppeteer download on unit test runs Fixes #2539 --- azure-pipelines.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 177f581f..2326e981 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -7,6 +7,8 @@ jobs: - job: Linux pool: vmImage: 'ubuntu-16.04' + variables: + PUPPETEER_SKIP_CHROMIUM_DOWNLOAD: 1 steps: - task: NodeTool@0 inputs: @@ -42,6 +44,8 @@ jobs: - job: macOS pool: vmImage: 'xcode9-macos10.13' + variables: + PUPPETEER_SKIP_CHROMIUM_DOWNLOAD: 1 steps: - task: NodeTool@0 inputs: @@ -57,6 +61,8 @@ jobs: - job: Windows pool: vmImage: 'vs2017-win2016' + variables: + PUPPETEER_SKIP_CHROMIUM_DOWNLOAD: 1 steps: - task: NodeTool@0 inputs: From 70500e31ac4da4468af267c2e3a0113ba4151d00 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Tue, 5 Nov 2019 11:36:54 -0800 Subject: [PATCH 16/92] Add yarn cache caching --- azure-pipelines.yml | 42 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 36 insertions(+), 6 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 2326e981..247395b3 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -18,7 +18,12 @@ jobs: inputs: versionSpec: '1.x' displayName: 'Install Yarn' - - script: yarn + - task: CacheBeta@1 + inputs: + key: yarn | $(Agent.OS) | yarn.lock + path: $(YARN_CACHE_FOLDER) + displayName: Cache Yarn packages + - script: yarn --frozen-lockfile displayName: 'Install dependencies and build' - script: yarn test-unit --forbid-only displayName: 'Unit tests' @@ -51,7 +56,12 @@ jobs: inputs: versionSpec: '8.x' displayName: 'Install Node.js' - - script: yarn + - task: CacheBeta@1 + inputs: + key: yarn | $(Agent.OS) | yarn.lock + path: $(YARN_CACHE_FOLDER) + displayName: Cache Yarn packages + - script: yarn --frozen-lockfile displayName: 'Install dependencies and build' - script: yarn test-unit --forbid-only displayName: 'Unit tests' @@ -68,7 +78,12 @@ jobs: inputs: versionSpec: '8.x' displayName: 'Install Node.js' - - script: yarn + - task: CacheBeta@1 + inputs: + key: yarn | $(Agent.OS) | yarn.lock + path: $(YARN_CACHE_FOLDER) + displayName: Cache Yarn packages + - script: yarn --frozen-lockfile displayName: 'Install dependencies and build' - script: yarn test-unit --forbid-only displayName: 'Unit tests' @@ -87,7 +102,12 @@ jobs: inputs: versionSpec: '1.x' displayName: 'Install Yarn' - - script: yarn + - task: CacheBeta@1 + inputs: + key: yarn | $(Agent.OS) | yarn.lock + path: $(YARN_CACHE_FOLDER) + displayName: Cache Yarn packages + - script: yarn --frozen-lockfile displayName: 'Install dependencies and build' - script: | yarn start & @@ -103,7 +123,12 @@ jobs: inputs: versionSpec: '8.x' displayName: 'Install Node.js' - - script: yarn + - task: CacheBeta@1 + inputs: + key: yarn | $(Agent.OS) | yarn.lock + path: $(YARN_CACHE_FOLDER) + displayName: Cache Yarn packages + - script: yarn --frozen-lockfile displayName: 'Install dependencies and build' - script: | yarn start & @@ -130,7 +155,12 @@ jobs: inputs: versionSpec: '1.x' displayName: 'Install Yarn' - - script: yarn + - task: CacheBeta@1 + inputs: + key: yarn | $(Agent.OS) | yarn.lock + path: $(YARN_CACHE_FOLDER) + displayName: Cache Yarn packages + - script: yarn --frozen-lockfile displayName: 'Install dependencies and build' - script: NPM_AUTH_TOKEN="$(NPM_AUTH_TOKEN)" node ./bin/publish.js displayName: 'Package and publish to npm' From 86264d47635a8c360e7fecad10fa3fcfb2af58ee Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Tue, 5 Nov 2019 11:42:35 -0800 Subject: [PATCH 17/92] Specify yarn cache folder --- azure-pipelines.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 247395b3..0998781f 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -9,6 +9,7 @@ jobs: vmImage: 'ubuntu-16.04' variables: PUPPETEER_SKIP_CHROMIUM_DOWNLOAD: 1 + YARN_CACHE_FOLDER: .yarn-cache steps: - task: NodeTool@0 inputs: @@ -51,6 +52,7 @@ jobs: vmImage: 'xcode9-macos10.13' variables: PUPPETEER_SKIP_CHROMIUM_DOWNLOAD: 1 + YARN_CACHE_FOLDER: .yarn-cache steps: - task: NodeTool@0 inputs: @@ -73,6 +75,7 @@ jobs: vmImage: 'vs2017-win2016' variables: PUPPETEER_SKIP_CHROMIUM_DOWNLOAD: 1 + YARN_CACHE_FOLDER: .yarn-cache steps: - task: NodeTool@0 inputs: @@ -93,6 +96,8 @@ jobs: - job: Linux_IntegrationTests pool: vmImage: 'ubuntu-16.04' + variables: + YARN_CACHE_FOLDER: .yarn-cache steps: - task: NodeTool@0 inputs: @@ -118,6 +123,8 @@ jobs: - job: macOS_IntegrationTests pool: vmImage: 'xcode9-macos10.13' + variables: + YARN_CACHE_FOLDER: .yarn-cache steps: - task: NodeTool@0 inputs: From 5411f60f44c71a4ecf9b3a9e125bf3afa66f700e Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Tue, 5 Nov 2019 11:54:32 -0800 Subject: [PATCH 18/92] Try cache node_modules --- azure-pipelines.yml | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 0998781f..9ccc8393 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -9,7 +9,6 @@ jobs: vmImage: 'ubuntu-16.04' variables: PUPPETEER_SKIP_CHROMIUM_DOWNLOAD: 1 - YARN_CACHE_FOLDER: .yarn-cache steps: - task: NodeTool@0 inputs: @@ -22,7 +21,7 @@ jobs: - task: CacheBeta@1 inputs: key: yarn | $(Agent.OS) | yarn.lock - path: $(YARN_CACHE_FOLDER) + path: node_modules displayName: Cache Yarn packages - script: yarn --frozen-lockfile displayName: 'Install dependencies and build' @@ -52,7 +51,6 @@ jobs: vmImage: 'xcode9-macos10.13' variables: PUPPETEER_SKIP_CHROMIUM_DOWNLOAD: 1 - YARN_CACHE_FOLDER: .yarn-cache steps: - task: NodeTool@0 inputs: @@ -61,7 +59,7 @@ jobs: - task: CacheBeta@1 inputs: key: yarn | $(Agent.OS) | yarn.lock - path: $(YARN_CACHE_FOLDER) + path: node_modules displayName: Cache Yarn packages - script: yarn --frozen-lockfile displayName: 'Install dependencies and build' @@ -75,7 +73,6 @@ jobs: vmImage: 'vs2017-win2016' variables: PUPPETEER_SKIP_CHROMIUM_DOWNLOAD: 1 - YARN_CACHE_FOLDER: .yarn-cache steps: - task: NodeTool@0 inputs: @@ -84,7 +81,7 @@ jobs: - task: CacheBeta@1 inputs: key: yarn | $(Agent.OS) | yarn.lock - path: $(YARN_CACHE_FOLDER) + path: node_modules displayName: Cache Yarn packages - script: yarn --frozen-lockfile displayName: 'Install dependencies and build' @@ -97,7 +94,6 @@ jobs: pool: vmImage: 'ubuntu-16.04' variables: - YARN_CACHE_FOLDER: .yarn-cache steps: - task: NodeTool@0 inputs: @@ -110,7 +106,7 @@ jobs: - task: CacheBeta@1 inputs: key: yarn | $(Agent.OS) | yarn.lock - path: $(YARN_CACHE_FOLDER) + path: node_modules displayName: Cache Yarn packages - script: yarn --frozen-lockfile displayName: 'Install dependencies and build' @@ -124,7 +120,6 @@ jobs: pool: vmImage: 'xcode9-macos10.13' variables: - YARN_CACHE_FOLDER: .yarn-cache steps: - task: NodeTool@0 inputs: @@ -133,7 +128,7 @@ jobs: - task: CacheBeta@1 inputs: key: yarn | $(Agent.OS) | yarn.lock - path: $(YARN_CACHE_FOLDER) + path: node_modules displayName: Cache Yarn packages - script: yarn --frozen-lockfile displayName: 'Install dependencies and build' @@ -165,7 +160,7 @@ jobs: - task: CacheBeta@1 inputs: key: yarn | $(Agent.OS) | yarn.lock - path: $(YARN_CACHE_FOLDER) + path: node_modules displayName: Cache Yarn packages - script: yarn --frozen-lockfile displayName: 'Install dependencies and build' From 2960d5df53fd7626e121fdf1fe951bb93d75bf2d Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Tue, 5 Nov 2019 11:55:06 -0800 Subject: [PATCH 19/92] New cache key --- azure-pipelines.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 9ccc8393..4eb35079 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -20,7 +20,7 @@ jobs: displayName: 'Install Yarn' - task: CacheBeta@1 inputs: - key: yarn | $(Agent.OS) | yarn.lock + key: yarn1 | $(Agent.OS) | yarn.lock path: node_modules displayName: Cache Yarn packages - script: yarn --frozen-lockfile @@ -58,7 +58,7 @@ jobs: displayName: 'Install Node.js' - task: CacheBeta@1 inputs: - key: yarn | $(Agent.OS) | yarn.lock + key: yarn1 | $(Agent.OS) | yarn.lock path: node_modules displayName: Cache Yarn packages - script: yarn --frozen-lockfile @@ -80,7 +80,7 @@ jobs: displayName: 'Install Node.js' - task: CacheBeta@1 inputs: - key: yarn | $(Agent.OS) | yarn.lock + key: yarn1 | $(Agent.OS) | yarn.lock path: node_modules displayName: Cache Yarn packages - script: yarn --frozen-lockfile @@ -105,7 +105,7 @@ jobs: displayName: 'Install Yarn' - task: CacheBeta@1 inputs: - key: yarn | $(Agent.OS) | yarn.lock + key: yarn1 | $(Agent.OS) | yarn.lock path: node_modules displayName: Cache Yarn packages - script: yarn --frozen-lockfile @@ -127,7 +127,7 @@ jobs: displayName: 'Install Node.js' - task: CacheBeta@1 inputs: - key: yarn | $(Agent.OS) | yarn.lock + key: yarn1 | $(Agent.OS) | yarn.lock path: node_modules displayName: Cache Yarn packages - script: yarn --frozen-lockfile @@ -159,7 +159,7 @@ jobs: displayName: 'Install Yarn' - task: CacheBeta@1 inputs: - key: yarn | $(Agent.OS) | yarn.lock + key: yarn1 | $(Agent.OS) | yarn.lock path: node_modules displayName: Cache Yarn packages - script: yarn --frozen-lockfile From d2164584d53dfc59362f0e5702b45a6f56554488 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Tue, 5 Nov 2019 11:56:44 -0800 Subject: [PATCH 20/92] Fix error in yml --- azure-pipelines.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 4eb35079..9117b885 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -93,7 +93,6 @@ jobs: - job: Linux_IntegrationTests pool: vmImage: 'ubuntu-16.04' - variables: steps: - task: NodeTool@0 inputs: @@ -119,7 +118,6 @@ jobs: - job: macOS_IntegrationTests pool: vmImage: 'xcode9-macos10.13' - variables: steps: - task: NodeTool@0 inputs: From c974b273d40c6e2f595539bcc2fa07152ce51fa3 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Tue, 5 Nov 2019 12:41:03 -0800 Subject: [PATCH 21/92] Install puppeteer explicitly --- azure-pipelines.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 9117b885..ff13e616 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -109,6 +109,8 @@ jobs: displayName: Cache Yarn packages - script: yarn --frozen-lockfile displayName: 'Install dependencies and build' + - script: yarn add puppeteer + displayName: 'Install puppeteer' - script: | yarn start & sleep 10 @@ -130,6 +132,8 @@ jobs: displayName: Cache Yarn packages - script: yarn --frozen-lockfile displayName: 'Install dependencies and build' + - script: yarn add puppeteer + displayName: 'Install puppeteer' - script: | yarn start & sleep 10 From 0630baaf0295d4100c5d40e87463070c3fe316d3 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Tue, 5 Nov 2019 12:50:37 -0800 Subject: [PATCH 22/92] Don't cache on integration tests --- azure-pipelines.yml | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index ff13e616..aaa2b6ea 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -20,7 +20,7 @@ jobs: displayName: 'Install Yarn' - task: CacheBeta@1 inputs: - key: yarn1 | $(Agent.OS) | yarn.lock + key: yarn2 | $(Agent.OS) | yarn.lock path: node_modules displayName: Cache Yarn packages - script: yarn --frozen-lockfile @@ -58,7 +58,7 @@ jobs: displayName: 'Install Node.js' - task: CacheBeta@1 inputs: - key: yarn1 | $(Agent.OS) | yarn.lock + key: yarn2 | $(Agent.OS) | yarn.lock path: node_modules displayName: Cache Yarn packages - script: yarn --frozen-lockfile @@ -80,7 +80,7 @@ jobs: displayName: 'Install Node.js' - task: CacheBeta@1 inputs: - key: yarn1 | $(Agent.OS) | yarn.lock + key: yarn2 | $(Agent.OS) | yarn.lock path: node_modules displayName: Cache Yarn packages - script: yarn --frozen-lockfile @@ -102,11 +102,6 @@ jobs: inputs: versionSpec: '1.x' displayName: 'Install Yarn' - - task: CacheBeta@1 - inputs: - key: yarn1 | $(Agent.OS) | yarn.lock - path: node_modules - displayName: Cache Yarn packages - script: yarn --frozen-lockfile displayName: 'Install dependencies and build' - script: yarn add puppeteer @@ -125,11 +120,6 @@ jobs: inputs: versionSpec: '8.x' displayName: 'Install Node.js' - - task: CacheBeta@1 - inputs: - key: yarn1 | $(Agent.OS) | yarn.lock - path: node_modules - displayName: Cache Yarn packages - script: yarn --frozen-lockfile displayName: 'Install dependencies and build' - script: yarn add puppeteer @@ -161,7 +151,7 @@ jobs: displayName: 'Install Yarn' - task: CacheBeta@1 inputs: - key: yarn1 | $(Agent.OS) | yarn.lock + key: yarn2 | $(Agent.OS) | yarn.lock path: node_modules displayName: Cache Yarn packages - script: yarn --frozen-lockfile From fc0568074ffb7577680c6646725ce2053a145ada Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Wed, 6 Nov 2019 10:37:00 -0800 Subject: [PATCH 23/92] Cache node modules with puppeteer --- azure-pipelines.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index aaa2b6ea..b4a6d940 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -98,6 +98,11 @@ jobs: inputs: versionSpec: '8.x' displayName: 'Install Node.js' + - task: CacheBeta@1 + inputs: + key: yarn_puppeteer | $(Agent.OS) | yarn.lock + path: node_modules + displayName: Cache Yarn packages - task: YarnInstaller@3 inputs: versionSpec: '1.x' @@ -120,6 +125,11 @@ jobs: inputs: versionSpec: '8.x' displayName: 'Install Node.js' + - task: CacheBeta@1 + inputs: + key: yarn_puppeteer | $(Agent.OS) | yarn.lock + path: node_modules + displayName: Cache Yarn packages - script: yarn --frozen-lockfile displayName: 'Install dependencies and build' - script: yarn add puppeteer From d4dbdac73f6bd20764ee2e1626c0f9a4265502c6 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Wed, 6 Nov 2019 10:53:42 -0800 Subject: [PATCH 24/92] Don't cache integration test modules --- azure-pipelines.yml | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index b4a6d940..7ddb534f 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -22,7 +22,7 @@ jobs: inputs: key: yarn2 | $(Agent.OS) | yarn.lock path: node_modules - displayName: Cache Yarn packages + displayName: Cache node modules - script: yarn --frozen-lockfile displayName: 'Install dependencies and build' - script: yarn test-unit --forbid-only @@ -60,7 +60,7 @@ jobs: inputs: key: yarn2 | $(Agent.OS) | yarn.lock path: node_modules - displayName: Cache Yarn packages + displayName: Cache node modules - script: yarn --frozen-lockfile displayName: 'Install dependencies and build' - script: yarn test-unit --forbid-only @@ -82,7 +82,7 @@ jobs: inputs: key: yarn2 | $(Agent.OS) | yarn.lock path: node_modules - displayName: Cache Yarn packages + displayName: Cache node modules - script: yarn --frozen-lockfile displayName: 'Install dependencies and build' - script: yarn test-unit --forbid-only @@ -102,7 +102,7 @@ jobs: inputs: key: yarn_puppeteer | $(Agent.OS) | yarn.lock path: node_modules - displayName: Cache Yarn packages + displayName: Cache node modules - task: YarnInstaller@3 inputs: versionSpec: '1.x' @@ -125,11 +125,6 @@ jobs: inputs: versionSpec: '8.x' displayName: 'Install Node.js' - - task: CacheBeta@1 - inputs: - key: yarn_puppeteer | $(Agent.OS) | yarn.lock - path: node_modules - displayName: Cache Yarn packages - script: yarn --frozen-lockfile displayName: 'Install dependencies and build' - script: yarn add puppeteer @@ -159,11 +154,6 @@ jobs: inputs: versionSpec: '1.x' displayName: 'Install Yarn' - - task: CacheBeta@1 - inputs: - key: yarn2 | $(Agent.OS) | yarn.lock - path: node_modules - displayName: Cache Yarn packages - script: yarn --frozen-lockfile displayName: 'Install dependencies and build' - script: NPM_AUTH_TOKEN="$(NPM_AUTH_TOKEN)" node ./bin/publish.js From d079775690a5e93f4d06971c7cc151912a7e569b Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Wed, 6 Nov 2019 11:04:37 -0800 Subject: [PATCH 25/92] Merge coverage check into regular unit test check Fixes #2542 --- azure-pipelines.yml | 17 +++-------------- bin/test.js | 21 ++++++++++++++++++++- package.json | 1 + 3 files changed, 24 insertions(+), 15 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 7ddb534f..415f1fb2 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -25,26 +25,15 @@ jobs: displayName: Cache node modules - script: yarn --frozen-lockfile displayName: 'Install dependencies and build' - - script: yarn test-unit --forbid-only + - script: yarn test-coverage --forbid-only displayName: 'Unit tests' - script: yarn lint displayName: 'Lint' - - script: | - NODE_PATH=$(pwd)/out ./node_modules/.bin/nyc ./node_modules/.bin/mocha './out/*test.js' './out/**/*test.js' - ./node_modules/.bin/nyc report --reporter=cobertura - displayName: 'Coverage report' - task: PublishCodeCoverageResults@1 inputs: codeCoverageTool: Cobertura summaryFileLocation: '$(System.DefaultWorkingDirectory)/**/*coverage.xml' displayName: 'Publish coverage' - - task: mspremier.BuildQualityChecks.QualityChecks-task.BuildQualityChecks@6 - displayName: 'Check build quality' - inputs: - checkCoverage: true - coverageType: lines - coverageThreshold: 60 - coverageFailOption: fixed - job: macOS pool: @@ -63,7 +52,7 @@ jobs: displayName: Cache node modules - script: yarn --frozen-lockfile displayName: 'Install dependencies and build' - - script: yarn test-unit --forbid-only + - script: yarn test-coverage --forbid-only displayName: 'Unit tests' - script: yarn lint displayName: 'Lint' @@ -85,7 +74,7 @@ jobs: displayName: Cache node modules - script: yarn --frozen-lockfile displayName: 'Install dependencies and build' - - script: yarn test-unit --forbid-only + - script: yarn test-coverage --forbid-only displayName: 'Unit tests' - script: yarn lint displayName: 'Lint' diff --git a/bin/test.js b/bin/test.js index 3b08d4a1..2b15e907 100644 --- a/bin/test.js +++ b/bin/test.js @@ -28,6 +28,26 @@ if (process.argv.length > 2) { } } +const checkCoverage = flagArgs.indexOf('--coverage') >= 0; + +if (checkCoverage) { + flagArgs.splice(flagArgs.indexOf('--coverage'), 1); + const executable = path.resolve(__dirname, '../node_modules/.bin/nyc'); + const args = ['--check-coverage', '--lines=100', path.resolve(__dirname, '../node_modules/.bin/mocha'), ...testFiles, ...flagArgs]; + console.info('executable', executable); + console.info('args', args); + const run = cp.spawnSync( + executable, + args, + { + cwd: path.resolve(__dirname, '..'), + env, + stdio: 'inherit' + } + ); + process.exit(run.status); +} + const run = cp.spawnSync( path.resolve(__dirname, '../node_modules/.bin/mocha'), [...testFiles, ...flagArgs], @@ -37,5 +57,4 @@ const run = cp.spawnSync( stdio: 'inherit' } ); - process.exit(run.status); diff --git a/package.json b/package.json index 0150569d..c33c48bd 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "posttest": "npm run lint", "test-api": "mocha \"**/*.api.js\"", "test-unit": "node ./bin/test.js", + "test-coverage": "node ./bin/test.js --coverage", "build": "tsc -b ./tsconfig.all.json", "prepare": "npm run setup", "setup": "npm run build", From 030a8bda80359623fd4a80cce7ee2c8d5dd2508f Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Wed, 6 Nov 2019 11:08:39 -0800 Subject: [PATCH 26/92] Set coverage lines threshold to 60% --- bin/test.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/bin/test.js b/bin/test.js index 2b15e907..94b4ac7b 100644 --- a/bin/test.js +++ b/bin/test.js @@ -6,6 +6,8 @@ const cp = require('child_process'); const path = require('path'); +const COVERAGE_LINES_THRESHOLD = 60; + // Add `out` to the NODE_PATH so absolute paths can be resolved. const env = { ...process.env }; env.NODE_PATH = path.resolve(__dirname, '../out'); @@ -33,7 +35,7 @@ const checkCoverage = flagArgs.indexOf('--coverage') >= 0; if (checkCoverage) { flagArgs.splice(flagArgs.indexOf('--coverage'), 1); const executable = path.resolve(__dirname, '../node_modules/.bin/nyc'); - const args = ['--check-coverage', '--lines=100', path.resolve(__dirname, '../node_modules/.bin/mocha'), ...testFiles, ...flagArgs]; + const args = ['--check-coverage', `--lines=${COVERAGE_LINES_THRESHOLD}`, path.resolve(__dirname, '../node_modules/.bin/mocha'), ...testFiles, ...flagArgs]; console.info('executable', executable); console.info('args', args); const run = cp.spawnSync( From 48b20e32005d98a905c463c1acf9acc0bd9fd6b7 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Wed, 6 Nov 2019 11:08:47 -0800 Subject: [PATCH 27/92] Don't run through nyc on mac and Windows --- azure-pipelines.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 415f1fb2..a4f6ede8 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -52,7 +52,7 @@ jobs: displayName: Cache node modules - script: yarn --frozen-lockfile displayName: 'Install dependencies and build' - - script: yarn test-coverage --forbid-only + - script: yarn test-unit --forbid-only displayName: 'Unit tests' - script: yarn lint displayName: 'Lint' @@ -74,7 +74,7 @@ jobs: displayName: Cache node modules - script: yarn --frozen-lockfile displayName: 'Install dependencies and build' - - script: yarn test-coverage --forbid-only + - script: yarn test-unit --forbid-only displayName: 'Unit tests' - script: yarn lint displayName: 'Lint' From fe2f1ea4df1b0b426057944af9d5d352bbd96fc7 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Wed, 6 Nov 2019 11:20:52 -0800 Subject: [PATCH 28/92] Get Windows unit tests running again Fixes #2543 --- bin/test.js | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/bin/test.js b/bin/test.js index 94b4ac7b..bd1784fb 100644 --- a/bin/test.js +++ b/bin/test.js @@ -34,8 +34,8 @@ const checkCoverage = flagArgs.indexOf('--coverage') >= 0; if (checkCoverage) { flagArgs.splice(flagArgs.indexOf('--coverage'), 1); - const executable = path.resolve(__dirname, '../node_modules/.bin/nyc'); - const args = ['--check-coverage', `--lines=${COVERAGE_LINES_THRESHOLD}`, path.resolve(__dirname, '../node_modules/.bin/mocha'), ...testFiles, ...flagArgs]; + const executable = npmBinScript('nyc'); + const args = ['--check-coverage', `--lines=${COVERAGE_LINES_THRESHOLD}`, npmBinScript('mocha'), ...testFiles, ...flagArgs]; console.info('executable', executable); console.info('args', args); const run = cp.spawnSync( @@ -51,7 +51,7 @@ if (checkCoverage) { } const run = cp.spawnSync( - path.resolve(__dirname, '../node_modules/.bin/mocha'), + npmBinScript('mocha'), [...testFiles, ...flagArgs], { cwd: path.resolve(__dirname, '..'), @@ -59,4 +59,9 @@ const run = cp.spawnSync( stdio: 'inherit' } ); + +function npmBinScript(script) { + return path.resolve(__dirname, `../node_modules/.bin/` + (process.platform === 'win32' ? `${script}.cmd` : script)); +} + process.exit(run.status); From 31afe5b30545da59a965ff69ba756c54bbe02308 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Wed, 6 Nov 2019 11:23:42 -0800 Subject: [PATCH 29/92] test-coverage -> test-unit-coverage --- azure-pipelines.yml | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index a4f6ede8..1ee66ddb 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -25,7 +25,7 @@ jobs: displayName: Cache node modules - script: yarn --frozen-lockfile displayName: 'Install dependencies and build' - - script: yarn test-coverage --forbid-only + - script: yarn test-unit-coverage --forbid-only displayName: 'Unit tests' - script: yarn lint displayName: 'Lint' diff --git a/package.json b/package.json index c33c48bd..7ed71741 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "posttest": "npm run lint", "test-api": "mocha \"**/*.api.js\"", "test-unit": "node ./bin/test.js", - "test-coverage": "node ./bin/test.js --coverage", + "test-unit-coverage": "node ./bin/test.js --coverage", "build": "tsc -b ./tsconfig.all.json", "prepare": "npm run setup", "setup": "npm run build", From 08782949648f064620a1cc646d7199cd70842c36 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Wed, 6 Nov 2019 11:24:34 -0800 Subject: [PATCH 30/92] Skip Terminal2 tests on Windows --- src/Terminal2.test.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/Terminal2.test.ts b/src/Terminal2.test.ts index efb2a446..f7046240 100644 --- a/src/Terminal2.test.ts +++ b/src/Terminal2.test.ts @@ -34,7 +34,6 @@ if (os.platform() === 'darwin') { // filter skipFilenames const FILES = TESTFILES.filter(value => SKIP_FILES.indexOf(value.split('/').slice(-1)[0]) === -1); - describe('Escape Sequence Files', function(): void { this.timeout(1000); @@ -44,6 +43,9 @@ describe('Escape Sequence Files', function(): void { let customHandler: IDisposable | undefined; before(() => { + if (process.platform === 'win32') { + return; + } ptyTerm = (pty as any).open({cols: COLS, rows: ROWS}); slaveEnd = ptyTerm._slave; term = new Terminal({cols: COLS, rows: ROWS}); @@ -51,12 +53,15 @@ describe('Escape Sequence Files', function(): void { }); after(() => { + if (process.platform === 'win32') { + return; + } ptyTerm._master.end(); ptyTerm._master.destroy(); }); FILES.forEach(filename => { - it(filename.split('/').slice(-1)[0], async () => { + (process.platform === 'win32' ? it.skip : it)(filename.split('/').slice(-1)[0], async () => { // reset terminal and handler if (customHandler) { customHandler.dispose(); From 94ce037eb8d750839ea8ddaa87e712ab042a2f58 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Thu, 7 Nov 2019 13:00:43 -0800 Subject: [PATCH 31/92] Add integration tests for regression in #2444 --- addons/xterm-addon-search/fixtures/issue-2444 | 70 ++++++++++++++++ .../xterm-addon-search/src/SearchAddon.api.ts | 84 ++++++++++++++++++- 2 files changed, 153 insertions(+), 1 deletion(-) create mode 100644 addons/xterm-addon-search/fixtures/issue-2444 diff --git a/addons/xterm-addon-search/fixtures/issue-2444 b/addons/xterm-addon-search/fixtures/issue-2444 new file mode 100644 index 00000000..cb7f7878 --- /dev/null +++ b/addons/xterm-addon-search/fixtures/issue-2444 @@ -0,0 +1,70 @@ +/usr/bin/cmake -E cmake_progress_report /home/yan/ssd/searchtrunk/tmp/release/CMakeFiles +make[3]: Entering directory `/home/yan/ssd/searchtrunk/tmp/release' +make[3]: Nothing to be done for `libsrc/wrap_curl/CMakeFiles/wrap_curl.dir/build'. +make[3]: Leaving directory `/home/yan/ssd/searchtrunk/tmp/release' +[ 0%] /usr/bin/cmake -E cmake_progress_report /home/yan/ssd/searchtrunk/tmp/release/CMakeFiles +[ 0%] [ 0%] /usr/bin/cmake -E cmake_progress_report /home/yan/ssd/searchtrunk/tmp/release/CMakeFiles +Built target config_utils +[ 0%] make[3]: Entering directory `/home/yan/ssd/searchtrunk/tmp/release' +/usr/bin/cmake -E cmake_progress_report /home/yan/ssd/searchtrunk/tmp/release/CMakeFiles +Built target omega +make[3]: Entering directory `/home/yan/ssd/searchtrunk/tmp/release' +cd /home/yan/ssd/searchtrunk/tmp/release && /usr/bin/cmake -E cmake_depends "Unix Makefiles" /home/yan/ssd/searchtrunk /home/yan/ssd/searchtrunk/projects/protogen/cpp /home/yan/ssd/searchtrunk/tmp/release /home/yan/ssd/searchtrunk/tmp/release/projects/protogen/cpp /home/yan/ssd/searchtrunk/tmp/release/projects/protogen/cpp/CMakeFiles/protogen.dir/DependInfo.cmake --color= +/usr/bin/cmake -E cmake_progress_report /home/yan/ssd/searchtrunk/tmp/release/CMakeFiles +Built target morpheus +make[3]: Leaving directory `/home/yan/ssd/searchtrunk/tmp/release' +make -f libsrc/base/CMakeFiles/base.dir/build.make libsrc/base/CMakeFiles/base.dir/build +make[3]: Entering directory `/home/yan/ssd/searchtrunk/tmp/release' +make[3]: Nothing to be done for `libsrc/coroutine/CMakeFiles/coroutine.dir/build'. +Built target wrap_curl +make[3]: Leaving directory `/home/yan/ssd/searchtrunk/tmp/release' +/usr/bin/cmake -E cmake_progress_report /home/yan/ssd/searchtrunk/tmp/release/CMakeFiles +/usr/bin/cmake -E cmake_progress_report /home/yan/ssd/searchtrunk/tmp/release/CMakeFiles +[ 0%] [ 0%] [ 0%] [ 0%] Building CXX object libsrc/image_crop/CMakeFiles/image_crop.dir/src/crop_context.cpp.o +cd /home/yan/ssd/searchtrunk/tmp/release/libsrc/image_crop && /opt/rh/devtoolset-7/root/usr/bin/c++ -DGOGO_USE_LIBEVENT2=1 -DGOGO_USE_OPENSSL_SNI=1 -DHAVE_BOOST -DHAVE_GLIBC -DHAVE_MALLOC_TRIM -DHAVE_PREAD -DHAVE_READAHEAD -D_FILE_OFFSET_BITS=64 -D_STAT_LEMM -pipe -Wall -Wextra -Werror=multichar -Wno-deprecated -Wno-unused-parameter -pthread -fPIC -Woverloaded-virtual -Wnon-virtual-dtor -Werror -std=gnu++11 -Wno-unknown-pragmas -DBOOST_FILESYSTEM_VERSION=3 -DBOOST_NO_CXX11_HDR_CODECVT -DMAGICKCORE_QUANTUM_DEPTH=8 -DMAGICKCORE_HDRI_ENABLE=0 -Wno-unused-local-typedefs -D_GLIBCXX_USE_CXX11_ABI=0 -std=gnu++14 -DCXXHASH128_EXIST -static-libstdc++ -Wno-implicit-fallthrough -Wno-stringop-overflow -O3 -DNDEBUG -DBOOST_UBLAS_NDEBUG -g0 -march=core2 -mssse3 -msse4.1 -I/home/yan/ssd/searchtrunk/tmp/release/__inc -I/home/yan/ssd/searchtrunk/libsrc/image_crop/src -I/home/yan/ssd/searchtrunk/include -isystem /home/yan/ssd/searchtrunk/tmp/release/contrib/boost_1_69_0/include -isystem /home/yan/ssd/searchtrunk/tmp/release/contrib/icu-49.1.2/include -isystem /home/yan/ssd/searchtrunk/tmp/release/contrib/protobuf-3.4.0/include -I/home/yan/ssd/searchtrunk/tmp/release -isystem /home/yan/ssd/searchtrunk/tmp/release/contrib/opencv-3.0.0m2/include -isystem /home/yan/ssd/searchtrunk/tmp/release/contrib/dlib-19.7p1/include -Wno-array-bounds -o CMakeFiles/image_crop.dir/src/crop_context.cpp.o -c /home/yan/ssd/searchtrunk/libsrc/image_crop/src/crop_context.cpp +Building CXX object libsrc/image_crop/CMakeFiles/image_crop.dir/src/detector.cpp.o +Building CXX object libsrc/image_crop/CMakeFiles/image_crop.dir/src/image_crop.cpp.o +cd /home/yan/ssd/searchtrunk/tmp/release/libsrc/image_crop && /opt/rh/devtoolset-7/root/usr/bin/c++ -DGOGO_USE_LIBEVENT2=1 -DGOGO_USE_OPENSSL_SNI=1 -DHAVE_BOOST -DHAVE_GLIBC -DHAVE_MALLOC_TRIM -DHAVE_PREAD -DHAVE_READAHEAD -D_FILE_OFFSET_BITS=64 -D_STAT_LEMM -pipe -Wall -Wextra -Werror=multichar -Wno-deprecated -Wno-unused-parameter -pthread -fPIC -Woverloaded-virtual -Wnon-virtual-dtor -Werror -std=gnu++11 -Wno-unknown-pragmas -DBOOST_FILESYSTEM_VERSION=3 -DBOOST_NO_CXX11_HDR_CODECVT -DMAGICKCORE_QUANTUM_DEPTH=8 -DMAGICKCORE_HDRI_ENABLE=0 -Wno-unused-local-typedefs -D_GLIBCXX_USE_CXX11_ABI=0 -std=gnu++14 -DCXXHASH128_EXIST -static-libstdc++ -Wno-implicit-fallthrough -Wno-stringop-overflow -O3 -DNDEBUG -DBOOST_UBLAS_NDEBUG -g0 -march=core2 -mssse3 -msse4.1 -I/home/yan/ssd/searchtrunk/tmp/release/__inc -I/home/yan/ssd/searchtrunk/libsrc/image_crop/src -I/home/yan/ssd/searchtrunk/include -isystem /home/yan/ssd/searchtrunk/tmp/release/contrib/boost_1_69_0/include -isystem /home/yan/ssd/searchtrunk/tmp/release/contrib/icu-49.1.2/include -isystem /home/yan/ssd/searchtrunk/tmp/release/contrib/protobuf-3.4.0/include -I/home/yan/ssd/searchtrunk/tmp/release -isystem /home/yan/ssd/searchtrunk/tmp/release/contrib/opencv-3.0.0m2/include -isystem /home/yan/ssd/searchtrunk/tmp/release/contrib/dlib-19.7p1/include -Wno-array-bounds -o CMakeFiles/image_crop.dir/src/detector.cpp.o -c /home/yan/ssd/searchtrunk/libsrc/image_crop/src/detector.cpp +Built target coroutine +cd /home/yan/ssd/searchtrunk/tmp/release/libsrc/image_crop && /opt/rh/devtoolset-7/root/usr/bin/c++ -DGOGO_USE_LIBEVENT2=1 -DGOGO_USE_OPENSSL_SNI=1 -DHAVE_BOOST -DHAVE_GLIBC -DHAVE_MALLOC_TRIM -DHAVE_PREAD -DHAVE_READAHEAD -D_FILE_OFFSET_BITS=64 -D_STAT_LEMM -pipe -Wall -Wextra -Werror=multichar -Wno-deprecated -Wno-unused-parameter -pthread -fPIC -Woverloaded-virtual -Wnon-virtual-dtor -Werror -std=gnu++11 -Wno-unknown-pragmas -DBOOST_FILESYSTEM_VERSION=3 -DBOOST_NO_CXX11_HDR_CODECVT -DMAGICKCORE_QUANTUM_DEPTH=8 -DMAGICKCORE_HDRI_ENABLE=0 -Wno-unused-local-typedefs -D_GLIBCXX_USE_CXX11_ABI=0 -std=gnu++14 -DCXXHASH128_EXIST -static-libstdc++ -Wno-implicit-fallthrough -Wno-stringop-overflow -O3 -DNDEBUG -DBOOST_UBLAS_NDEBUG -g0 -march=core2 -mssse3 -msse4.1 -I/home/yan/ssd/searchtrunk/tmp/release/__inc -I/home/yan/ssd/searchtrunk/libsrc/image_crop/src -I/home/yan/ssd/searchtrunk/include -isystem /home/yan/ssd/searchtrunk/tmp/release/contrib/boost_1_69_0/include -isystem /home/yan/ssd/searchtrunk/tmp/release/contrib/icu-49.1.2/include -isystem /home/yan/ssd/searchtrunk/tmp/release/contrib/protobuf-3.4.0/include -I/home/yan/ssd/searchtrunk/tmp/release -isystem /home/yan/ssd/searchtrunk/tmp/release/contrib/opencv-3.0.0m2/include -isystem /home/yan/ssd/searchtrunk/tmp/release/contrib/dlib-19.7p1/include -Wno-array-bounds -o CMakeFiles/image_crop.dir/src/image_crop.cpp.o -c /home/yan/ssd/searchtrunk/libsrc/image_crop/src/image_crop.cpp +make[3]: Entering directory `/home/yan/ssd/searchtrunk/tmp/release' +make[3]: Nothing to be done for `libsrc/base/CMakeFiles/base.dir/build'. +make[3]: Leaving directory `/home/yan/ssd/searchtrunk/tmp/release' +/usr/bin/cmake -E cmake_progress_report /home/yan/ssd/searchtrunk/tmp/release/CMakeFiles +make[3]: Leaving directory `/home/yan/ssd/searchtrunk/tmp/release' +make -f projects/protogen/cpp/CMakeFiles/protogen.dir/build.make projects/protogen/cpp/CMakeFiles/protogen.dir/build +[ 0%] Built target base +In file included from /home/yan/ssd/searchtrunk/include/image_crop/detector.hpp:3:0, + from /home/yan/ssd/searchtrunk/libsrc/image_crop/src/detector.cpp:2: +/home/yan/ssd/searchtrunk/include/image_crop/crop_context.hpp:6:10: fatal error: opencv2/dnn.hpp: No such file or directory + #include + ^~~~~~~~~~~~~~~~~ +compilation terminated. +make[3]: *** [libsrc/image_crop/CMakeFiles/image_crop.dir/src/detector.cpp.o] Error 1 +make[3]: *** Waiting for unfinished jobs.... +In file included from /home/yan/ssd/searchtrunk/include/image_crop/image_crop.hpp:3:0, + from /home/yan/ssd/searchtrunk/libsrc/image_crop/src/image_crop.cpp:1: +/home/yan/ssd/searchtrunk/include/image_crop/crop_context.hpp:6:10: fatal error: opencv2/dnn.hpp: No such file or directory + #include + ^~~~~~~~~~~~~~~~~ +compilation terminated. +make[3]: *** [libsrc/image_crop/CMakeFiles/image_crop.dir/src/image_crop.cpp.o] Error 1 +In file included from /home/yan/ssd/searchtrunk/libsrc/image_crop/src/crop_context.cpp:1:0: +/home/yan/ssd/searchtrunk/include/image_crop/crop_context.hpp:6:10: fatal error: opencv2/dnn.hpp: No such file or directory + #include + ^~~~~~~~~~~~~~~~~ +compilation terminated. +make[3]: *** [libsrc/image_crop/CMakeFiles/image_crop.dir/src/crop_context.cpp.o] Error 1 +make[3]: Leaving directory `/home/yan/ssd/searchtrunk/tmp/release' +make[2]: *** [libsrc/image_crop/CMakeFiles/image_crop.dir/all] Error 2 +make[2]: *** Waiting for unfinished jobs.... +make[3]: Entering directory `/home/yan/ssd/searchtrunk/tmp/release' +make[3]: Nothing to be done for `projects/protogen/cpp/CMakeFiles/protogen.dir/build'. +make[3]: Leaving directory `/home/yan/ssd/searchtrunk/tmp/release' +/usr/bin/cmake -E cmake_progress_report /home/yan/ssd/searchtrunk/tmp/release/CMakeFiles 63 64 65 66 +[ 25%] Built target protogen +make[2]: Leaving directory `/home/yan/ssd/searchtrunk/tmp/release' +make[1]: *** [all] Error 2 +make[1]: Leaving directory `/home/yan/ssd/searchtrunk/tmp/release/projects/projectX' +make: *** [install] Error 2 +yan@yPC:~/ssd/searchtrunk/projects/projectX$ diff --git a/addons/xterm-addon-search/src/SearchAddon.api.ts b/addons/xterm-addon-search/src/SearchAddon.api.ts index e3520116..ff195f5e 100644 --- a/addons/xterm-addon-search/src/SearchAddon.api.ts +++ b/addons/xterm-addon-search/src/SearchAddon.api.ts @@ -6,6 +6,8 @@ import * as puppeteer from 'puppeteer'; import { assert } from 'chai'; import { ITerminalOptions } from 'xterm'; +import { readFile } from 'fs'; +import { resolve } from 'path'; const APP = 'http://127.0.0.1:3000/test'; @@ -91,7 +93,7 @@ describe('Search Tests', function (): void { selectionPosition = await page.evaluate(`window.term.getSelectionPosition()`); assert.deepEqual(line.substring(selectionPosition.startColumn, selectionPosition.endColumn), 'package.jsonc'); }); - it ('Simple Regex', async () => { + it('Simple Regex', async () => { await writeSync('abc123defABCD'); await page.evaluate(`window.search.findNext('[a-z]+', {regex: true})`); assert.deepEqual(await page.evaluate(`window.term.getSelection()`), 'abc'); @@ -106,6 +108,86 @@ describe('Search Tests', function (): void { assert.deepEqual(await page.evaluate(`window.search.findNext('abc')`), true); assert.deepEqual(await page.evaluate(`window.term.getSelection()`), 'abc'); }); + + describe('Regression tests', () => { + describe('#2444 wrapped line content not being found', () => { + let fixture: string; + before(async () => { + const rawFixture = await new Promise(r => readFile(resolve(__dirname, '../fixtures/issue-2444'), (err, data) => r(data))); + fixture = rawFixture.toString() + .replace(/\n/g, '\\n\\r') + .replace(/'/g, '\\\''); + }); + it('should find all occurrences using findNext', async () => { + await writeSync(fixture); + assert.deepEqual(await page.evaluate(`window.search.findNext('opencv')`), true); + let selectionPosition = await page.evaluate(`window.term.getSelectionPosition()`); + assert.deepEqual(selectionPosition, { startColumn: 24, startRow: 53, endColumn: 30, endRow: 53 }); + assert.deepEqual(await page.evaluate(`window.search.findNext('opencv')`), true); + selectionPosition = await page.evaluate(`window.term.getSelectionPosition()`); + assert.deepEqual(selectionPosition, { startColumn: 24, startRow: 76, endColumn: 30, endRow: 76 }); + assert.deepEqual(await page.evaluate(`window.search.findNext('opencv')`), true); + selectionPosition = await page.evaluate(`window.term.getSelectionPosition()`); + assert.deepEqual(selectionPosition, { startColumn: 24, startRow: 96, endColumn: 30, endRow: 96 }); + assert.deepEqual(await page.evaluate(`window.search.findNext('opencv')`), true); + selectionPosition = await page.evaluate(`window.term.getSelectionPosition()`); + assert.deepEqual(selectionPosition, { startColumn: 1, startRow: 114, endColumn: 7, endRow: 114 }); + assert.deepEqual(await page.evaluate(`window.search.findNext('opencv')`), true); + selectionPosition = await page.evaluate(`window.term.getSelectionPosition()`); + assert.deepEqual(selectionPosition, { startColumn: 11, startRow: 115, endColumn: 17, endRow: 115 }); + assert.deepEqual(await page.evaluate(`window.search.findNext('opencv')`), true); + selectionPosition = await page.evaluate(`window.term.getSelectionPosition()`); + assert.deepEqual(selectionPosition, { startColumn: 1, startRow: 126, endColumn: 7, endRow: 126 }); + assert.deepEqual(await page.evaluate(`window.search.findNext('opencv')`), true); + selectionPosition = await page.evaluate(`window.term.getSelectionPosition()`); + assert.deepEqual(selectionPosition, { startColumn: 11, startRow: 127, endColumn: 17, endRow: 127 }); + assert.deepEqual(await page.evaluate(`window.search.findNext('opencv')`), true); + selectionPosition = await page.evaluate(`window.term.getSelectionPosition()`); + assert.deepEqual(selectionPosition, { startColumn: 1, startRow: 135, endColumn: 7, endRow: 135 }); + assert.deepEqual(await page.evaluate(`window.search.findNext('opencv')`), true); + selectionPosition = await page.evaluate(`window.term.getSelectionPosition()`); + assert.deepEqual(selectionPosition, { startColumn: 11, startRow: 136, endColumn: 17, endRow: 136 }); + // Wrap around to first result + assert.deepEqual(await page.evaluate(`window.search.findNext('opencv')`), true); + selectionPosition = await page.evaluate(`window.term.getSelectionPosition()`); + assert.deepEqual(selectionPosition, { startColumn: 24, startRow: 53, endColumn: 30, endRow: 53 }); + }); + it('should find all occurrences using findPrevious', async () => { + await writeSync(fixture); + assert.deepEqual(await page.evaluate(`window.search.findPrevious('opencv')`), true); + let selectionPosition = await page.evaluate(`window.term.getSelectionPosition()`); + assert.deepEqual(selectionPosition, { startColumn: 11, startRow: 136, endColumn: 17, endRow: 136 }); + assert.deepEqual(await page.evaluate(`window.search.findPrevious('opencv')`), true); + selectionPosition = await page.evaluate(`window.term.getSelectionPosition()`); + assert.deepEqual(selectionPosition, { startColumn: 1, startRow: 135, endColumn: 7, endRow: 135 }); + assert.deepEqual(await page.evaluate(`window.search.findPrevious('opencv')`), true); + selectionPosition = await page.evaluate(`window.term.getSelectionPosition()`); + assert.deepEqual(selectionPosition, { startColumn: 11, startRow: 127, endColumn: 17, endRow: 127 }); + assert.deepEqual(await page.evaluate(`window.search.findPrevious('opencv')`), true); + selectionPosition = await page.evaluate(`window.term.getSelectionPosition()`); + assert.deepEqual(selectionPosition, { startColumn: 1, startRow: 126, endColumn: 7, endRow: 126 }); + assert.deepEqual(await page.evaluate(`window.search.findPrevious('opencv')`), true); + selectionPosition = await page.evaluate(`window.term.getSelectionPosition()`); + assert.deepEqual(selectionPosition, { startColumn: 11, startRow: 115, endColumn: 17, endRow: 115 }); + assert.deepEqual(await page.evaluate(`window.search.findPrevious('opencv')`), true); + selectionPosition = await page.evaluate(`window.term.getSelectionPosition()`); + assert.deepEqual(selectionPosition, { startColumn: 1, startRow: 114, endColumn: 7, endRow: 114 }); + assert.deepEqual(await page.evaluate(`window.search.findPrevious('opencv')`), true); + selectionPosition = await page.evaluate(`window.term.getSelectionPosition()`); + assert.deepEqual(selectionPosition, { startColumn: 24, startRow: 96, endColumn: 30, endRow: 96 }); + assert.deepEqual(await page.evaluate(`window.search.findPrevious('opencv')`), true); + selectionPosition = await page.evaluate(`window.term.getSelectionPosition()`); + assert.deepEqual(selectionPosition, { startColumn: 24, startRow: 76, endColumn: 30, endRow: 76 }); + assert.deepEqual(await page.evaluate(`window.search.findPrevious('opencv')`), true); + selectionPosition = await page.evaluate(`window.term.getSelectionPosition()`); + assert.deepEqual(selectionPosition, { startColumn: 24, startRow: 53, endColumn: 30, endRow: 53 }); + // Wrap around to first result + assert.deepEqual(await page.evaluate(`window.search.findPrevious('opencv')`), true); + selectionPosition = await page.evaluate(`window.term.getSelectionPosition()`); + assert.deepEqual(selectionPosition, { startColumn: 11, startRow: 136, endColumn: 17, endRow: 136 }); + }); + }); + }); }); async function openTerminal(options: ITerminalOptions = {}): Promise { From fce7a67c8e73b9a7eba8767e5c1205f7dbf5bf5b Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Thu, 7 Nov 2019 13:39:42 -0800 Subject: [PATCH 32/92] Remove type jsdoc --- addons/xterm-addon-search/src/SearchAddon.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/xterm-addon-search/src/SearchAddon.ts b/addons/xterm-addon-search/src/SearchAddon.ts index 67f1ad6e..db2968e6 100644 --- a/addons/xterm-addon-search/src/SearchAddon.ts +++ b/addons/xterm-addon-search/src/SearchAddon.ts @@ -239,7 +239,7 @@ export class SearchAddon implements ITerminalAddon { * started on an earlier line then it is skipped since it will be properly searched when the terminal line that the * text starts on is searched. * @param term The search term. - * @param {ISearchPosition} position The position to start the search. + * @param position The position to start the search. * @param searchOptions Search options. * @return The search result if it was found. */ From 1d63be0506e70cd9ab36c4ea8f9f6478a09a8422 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Thu, 7 Nov 2019 13:51:51 -0800 Subject: [PATCH 33/92] Reduce duplicate in pipeline, cache in release job --- azure-pipelines.yml | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 1ee66ddb..4daa0961 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -87,19 +87,12 @@ jobs: inputs: versionSpec: '8.x' displayName: 'Install Node.js' - - task: CacheBeta@1 - inputs: - key: yarn_puppeteer | $(Agent.OS) | yarn.lock - path: node_modules - displayName: Cache node modules - task: YarnInstaller@3 inputs: versionSpec: '1.x' displayName: 'Install Yarn' - script: yarn --frozen-lockfile displayName: 'Install dependencies and build' - - script: yarn add puppeteer - displayName: 'Install puppeteer' - script: | yarn start & sleep 10 @@ -116,8 +109,6 @@ jobs: displayName: 'Install Node.js' - script: yarn --frozen-lockfile displayName: 'Install dependencies and build' - - script: yarn add puppeteer - displayName: 'Install puppeteer' - script: | yarn start & sleep 10 @@ -143,6 +134,10 @@ jobs: inputs: versionSpec: '1.x' displayName: 'Install Yarn' + - task: CacheBeta@1 + inputs: + key: yarn2 | $(Agent.OS) | yarn.lock + path: node_modules - script: yarn --frozen-lockfile displayName: 'Install dependencies and build' - script: NPM_AUTH_TOKEN="$(NPM_AUTH_TOKEN)" node ./bin/publish.js From 7c39217392e498442fdf64e3cac1ec6489e13136 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Thu, 7 Nov 2019 17:35:22 -0800 Subject: [PATCH 34/92] Break DomRenderer dependency on ITerminal Part of #1507 --- src/Terminal.ts | 2 +- src/renderer/dom/DomRenderer.ts | 102 ++++++++++++++++---------------- 2 files changed, 53 insertions(+), 51 deletions(-) diff --git a/src/Terminal.ts b/src/Terminal.ts index 537ecb74..5e2fa04e 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -638,7 +638,7 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp private _createRenderer(): IRenderer { switch (this.options.rendererType) { case 'canvas': return new Renderer(this._colorManager.colors, this, this._bufferService, this._charSizeService, this.optionsService); - case 'dom': return new DomRenderer(this, this._colorManager.colors, this._charSizeService, this.optionsService); + case 'dom': return new DomRenderer(this._colorManager.colors, this.element, this.screenElement, this._viewportElement, this.linkifier, this._charSizeService, this.optionsService, this._bufferService, this._renderService); default: throw new Error(`Unrecognized rendererType "${this.options.rendererType}"`); } } diff --git a/src/renderer/dom/DomRenderer.ts b/src/renderer/dom/DomRenderer.ts index ef927f20..1beaadcd 100644 --- a/src/renderer/dom/DomRenderer.ts +++ b/src/renderer/dom/DomRenderer.ts @@ -4,13 +4,12 @@ */ import { IRenderer, IRenderDimensions, CharacterJoinerHandler } from 'browser/renderer/Types'; -import { ITerminal } from '../../Types'; import { BOLD_CLASS, ITALIC_CLASS, CURSOR_CLASS, CURSOR_STYLE_BLOCK_CLASS, CURSOR_BLINK_CLASS, CURSOR_STYLE_BAR_CLASS, CURSOR_STYLE_UNDERLINE_CLASS, DomRendererRowFactory } from 'browser/renderer/dom/DomRendererRowFactory'; import { INVERTED_DEFAULT_COLOR } from 'browser/renderer/atlas/Constants'; import { Disposable } from 'common/Lifecycle'; -import { IColorSet, ILinkifierEvent } from 'browser/Types'; -import { ICharSizeService } from 'browser/services/Services'; -import { IOptionsService } from 'common/services/Services'; +import { IColorSet, ILinkifierEvent, ILinkifier } from 'browser/Types'; +import { ICharSizeService, IRenderService } from 'browser/services/Services'; +import { IOptionsService, IBufferService } from 'common/services/Services'; const TERMINAL_CLASS_PREFIX = 'xterm-dom-renderer-owner-'; const ROW_CONTAINER_CLASS = 'xterm-rows'; @@ -39,10 +38,15 @@ export class DomRenderer extends Disposable implements IRenderer { public dimensions: IRenderDimensions; constructor( - private _terminal: ITerminal, private _colors: IColorSet, - private _charSizeService: ICharSizeService, - private _optionsService: IOptionsService + private readonly _element: HTMLElement, + private readonly _screenElement: HTMLElement, + private readonly _viewportElement: HTMLElement, + private readonly _linkifier: ILinkifier, + private readonly _charSizeService: ICharSizeService, + private readonly _optionsService: IOptionsService, + private readonly _bufferService: IBufferService, + private readonly _renderService: IRenderService ) { super(); @@ -50,7 +54,7 @@ export class DomRenderer extends Disposable implements IRenderer { this._rowContainer.classList.add(ROW_CONTAINER_CLASS); this._rowContainer.style.lineHeight = 'normal'; this._rowContainer.setAttribute('aria-hidden', 'true'); - this._refreshRowElements(this._terminal.cols, this._terminal.rows); + this._refreshRowElements(this._bufferService.cols, this._bufferService.rows); this._selectionContainer = document.createElement('div'); this._selectionContainer.classList.add(SELECTION_CLASS); this._selectionContainer.setAttribute('aria-hidden', 'true'); @@ -74,36 +78,36 @@ export class DomRenderer extends Disposable implements IRenderer { this._rowFactory = new DomRendererRowFactory(document, this._optionsService); - this._terminal.element.classList.add(TERMINAL_CLASS_PREFIX + this._terminalClass); - this._terminal.screenElement.appendChild(this._rowContainer); - this._terminal.screenElement.appendChild(this._selectionContainer); + this._element.classList.add(TERMINAL_CLASS_PREFIX + this._terminalClass); + this._screenElement.appendChild(this._rowContainer); + this._screenElement.appendChild(this._selectionContainer); - this._terminal.linkifier.onLinkHover(e => this._onLinkHover(e)); - this._terminal.linkifier.onLinkLeave(e => this._onLinkLeave(e)); + this._linkifier.onLinkHover(e => this._onLinkHover(e)); + this._linkifier.onLinkLeave(e => this._onLinkLeave(e)); } public dispose(): void { - this._terminal.element.classList.remove(TERMINAL_CLASS_PREFIX + this._terminalClass); - this._terminal.screenElement.removeChild(this._rowContainer); - this._terminal.screenElement.removeChild(this._selectionContainer); - this._terminal.screenElement.removeChild(this._themeStyleElement); - this._terminal.screenElement.removeChild(this._dimensionsStyleElement); + this._element.classList.remove(TERMINAL_CLASS_PREFIX + this._terminalClass); + this._screenElement.removeChild(this._rowContainer); + this._screenElement.removeChild(this._selectionContainer); + this._screenElement.removeChild(this._themeStyleElement); + this._screenElement.removeChild(this._dimensionsStyleElement); super.dispose(); } private _updateDimensions(): void { this.dimensions.scaledCharWidth = this._charSizeService.width * window.devicePixelRatio; this.dimensions.scaledCharHeight = Math.ceil(this._charSizeService.height * window.devicePixelRatio); - this.dimensions.scaledCellWidth = this.dimensions.scaledCharWidth + Math.round(this._terminal.options.letterSpacing); - this.dimensions.scaledCellHeight = Math.floor(this.dimensions.scaledCharHeight * this._terminal.options.lineHeight); + this.dimensions.scaledCellWidth = this.dimensions.scaledCharWidth + Math.round(this._optionsService.options.letterSpacing); + this.dimensions.scaledCellHeight = Math.floor(this.dimensions.scaledCharHeight * this._optionsService.options.lineHeight); this.dimensions.scaledCharLeft = 0; this.dimensions.scaledCharTop = 0; - this.dimensions.scaledCanvasWidth = this.dimensions.scaledCellWidth * this._terminal.cols; - this.dimensions.scaledCanvasHeight = this.dimensions.scaledCellHeight * this._terminal.rows; + this.dimensions.scaledCanvasWidth = this.dimensions.scaledCellWidth * this._bufferService.cols; + this.dimensions.scaledCanvasHeight = this.dimensions.scaledCellHeight * this._bufferService.rows; this.dimensions.canvasWidth = Math.round(this.dimensions.scaledCanvasWidth / window.devicePixelRatio); this.dimensions.canvasHeight = Math.round(this.dimensions.scaledCanvasHeight / window.devicePixelRatio); - this.dimensions.actualCellWidth = this.dimensions.canvasWidth / this._terminal.cols; - this.dimensions.actualCellHeight = this.dimensions.canvasHeight / this._terminal.rows; + this.dimensions.actualCellWidth = this.dimensions.canvasWidth / this._bufferService.cols; + this.dimensions.actualCellHeight = this.dimensions.canvasHeight / this._bufferService.rows; this._rowElements.forEach(element => { element.style.width = `${this.dimensions.canvasWidth}px`; @@ -115,7 +119,7 @@ export class DomRenderer extends Disposable implements IRenderer { if (!this._dimensionsStyleElement) { this._dimensionsStyleElement = document.createElement('style'); - this._terminal.screenElement.appendChild(this._dimensionsStyleElement); + this._screenElement.appendChild(this._dimensionsStyleElement); } const styles = @@ -128,9 +132,9 @@ export class DomRenderer extends Disposable implements IRenderer { this._dimensionsStyleElement.innerHTML = styles; - this._selectionContainer.style.height = (this._terminal)._viewportElement.style.height; - this._terminal.screenElement.style.width = `${this.dimensions.canvasWidth}px`; - this._terminal.screenElement.style.height = `${this.dimensions.canvasHeight}px`; + this._selectionContainer.style.height = this._viewportElement.style.height; + this._screenElement.style.width = `${this.dimensions.canvasWidth}px`; + this._screenElement.style.height = `${this.dimensions.canvasHeight}px`; } public setColors(colors: IColorSet): void { @@ -141,7 +145,7 @@ export class DomRenderer extends Disposable implements IRenderer { private _injectCss(): void { if (!this._themeStyleElement) { this._themeStyleElement = document.createElement('style'); - this._terminal.screenElement.appendChild(this._themeStyleElement); + this._screenElement.appendChild(this._themeStyleElement); } // Base CSS @@ -149,16 +153,16 @@ export class DomRenderer extends Disposable implements IRenderer { `${this._terminalSelector} .${ROW_CONTAINER_CLASS} {` + ` color: ${this._colors.foreground.css};` + ` background-color: ${this._colors.background.css};` + - ` font-family: ${this._terminal.options.fontFamily};` + - ` font-size: ${this._terminal.options.fontSize}px;` + + ` font-family: ${this._optionsService.options.fontFamily};` + + ` font-size: ${this._optionsService.options.fontSize}px;` + `}`; // Text styles styles += `${this._terminalSelector} span:not(.${BOLD_CLASS}) {` + - ` font-weight: ${this._terminal.options.fontWeight};` + + ` font-weight: ${this._optionsService.options.fontWeight};` + `}` + `${this._terminalSelector} span.${BOLD_CLASS} {` + - ` font-weight: ${this._terminal.options.fontWeightBold};` + + ` font-weight: ${this._optionsService.options.fontWeightBold};` + `}` + `${this._terminalSelector} span.${ITALIC_CLASS} {` + ` font-style: italic;` + @@ -275,13 +279,13 @@ export class DomRenderer extends Disposable implements IRenderer { } // Translate from buffer position to viewport position - const viewportStartRow = start[1] - this._terminal.buffer.ydisp; - const viewportEndRow = end[1] - this._terminal.buffer.ydisp; + const viewportStartRow = start[1] - this._bufferService.buffer.ydisp; + const viewportEndRow = end[1] - this._bufferService.buffer.ydisp; const viewportCappedStartRow = Math.max(viewportStartRow, 0); - const viewportCappedEndRow = Math.min(viewportEndRow, this._terminal.rows - 1); + const viewportCappedEndRow = Math.min(viewportEndRow, this._bufferService.rows - 1); // No need to draw the selection - if (viewportCappedStartRow >= this._terminal.rows || viewportCappedEndRow < 0) { + if (viewportCappedStartRow >= this._bufferService.rows || viewportCappedEndRow < 0) { return; } @@ -295,15 +299,15 @@ export class DomRenderer extends Disposable implements IRenderer { } else { // Draw first row const startCol = viewportStartRow === viewportCappedStartRow ? start[0] : 0; - const endCol = viewportCappedStartRow === viewportCappedEndRow ? end[0] : this._terminal.cols; + const endCol = viewportCappedStartRow === viewportCappedEndRow ? end[0] : this._bufferService.cols; documentFragment.appendChild(this._createSelectionElement(viewportCappedStartRow, startCol, endCol)); // Draw middle rows const middleRowsCount = viewportCappedEndRow - viewportCappedStartRow - 1; - documentFragment.appendChild(this._createSelectionElement(viewportCappedStartRow + 1, 0, this._terminal.cols, middleRowsCount)); + documentFragment.appendChild(this._createSelectionElement(viewportCappedStartRow + 1, 0, this._bufferService.cols, middleRowsCount)); // Draw final row if (viewportCappedStartRow !== viewportCappedEndRow) { // Only draw viewportEndRow if it's not the same as viewporttartRow - const endCol = viewportEndRow === viewportCappedEndRow ? end[0] : this._terminal.cols; + const endCol = viewportEndRow === viewportCappedEndRow ? end[0] : this._bufferService.cols; documentFragment.appendChild(this._createSelectionElement(viewportCappedEndRow, 0, endCol)); } } @@ -333,7 +337,7 @@ export class DomRenderer extends Disposable implements IRenderer { // Force a refresh this._updateDimensions(); this._injectCss(); - this._terminal.refresh(0, this._terminal.rows - 1); + this._renderService.refreshRows(0, this._bufferService.rows - 1); } public clear(): void { @@ -341,20 +345,18 @@ export class DomRenderer extends Disposable implements IRenderer { } public renderRows(start: number, end: number): void { - const terminal = this._terminal; - - const cursorAbsoluteY = terminal.buffer.ybase + terminal.buffer.y; - const cursorX = this._terminal.buffer.x; - const cursorBlink = this._terminal.options.cursorBlink; + const cursorAbsoluteY = this._bufferService.buffer.ybase + this._bufferService.buffer.y; + const cursorX = this._bufferService.buffer.x; + const cursorBlink = this._optionsService.options.cursorBlink; for (let y = start; y <= end; y++) { const rowElement = this._rowElements[y]; rowElement.innerHTML = ''; - const row = y + terminal.buffer.ydisp; - const lineData = terminal.buffer.lines.get(row); - const cursorStyle = terminal.options.cursorStyle; - rowElement.appendChild(this._rowFactory.createRow(lineData, row === cursorAbsoluteY, cursorStyle, cursorX, cursorBlink, this.dimensions.actualCellWidth, terminal.cols)); + const row = y + this._bufferService.buffer.ydisp; + const lineData = this._bufferService.buffer.lines.get(row); + const cursorStyle = this._optionsService.options.cursorStyle; + rowElement.appendChild(this._rowFactory.createRow(lineData, row === cursorAbsoluteY, cursorStyle, cursorX, cursorBlink, this.dimensions.actualCellWidth, this._bufferService.cols)); } } From 82e18d7aaa4cb9219bcce0acd1de6298635bbf88 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Thu, 7 Nov 2019 17:41:29 -0800 Subject: [PATCH 35/92] Move DomRenderer to browser, fix strict errors --- src/Terminal.ts | 4 +- src/{ => browser}/renderer/dom/DomRenderer.ts | 40 +++++++++---------- 2 files changed, 22 insertions(+), 22 deletions(-) rename src/{ => browser}/renderer/dom/DomRenderer.ts (94%) diff --git a/src/Terminal.ts b/src/Terminal.ts index 5e2fa04e..6626b966 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -38,7 +38,7 @@ import { SoundService } from 'browser/services/SoundService'; import { MouseZoneManager } from 'browser/MouseZoneManager'; import { AccessibilityManager } from './AccessibilityManager'; import { ITheme, IMarker, IDisposable, ISelectionPosition } from 'xterm'; -import { DomRenderer } from './renderer/dom/DomRenderer'; +import { DomRenderer } from 'browser/renderer/dom/DomRenderer'; import { IKeyboardEvent, KeyboardResultType, ICharset, IBufferLine, IAttributeData, CoreMouseEventType, CoreMouseButton, CoreMouseAction } from 'common/Types'; import { evaluateKeyboardEvent } from 'common/input/Keyboard'; import { EventEmitter, IEvent } from 'common/EventEmitter'; @@ -638,7 +638,7 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp private _createRenderer(): IRenderer { switch (this.options.rendererType) { case 'canvas': return new Renderer(this._colorManager.colors, this, this._bufferService, this._charSizeService, this.optionsService); - case 'dom': return new DomRenderer(this._colorManager.colors, this.element, this.screenElement, this._viewportElement, this.linkifier, this._charSizeService, this.optionsService, this._bufferService, this._renderService); + case 'dom': return this._instantiationService.createInstance(DomRenderer, this._colorManager.colors, this.element, this.screenElement, this._viewportElement, this.linkifier); default: throw new Error(`Unrecognized rendererType "${this.options.rendererType}"`); } } diff --git a/src/renderer/dom/DomRenderer.ts b/src/browser/renderer/dom/DomRenderer.ts similarity index 94% rename from src/renderer/dom/DomRenderer.ts rename to src/browser/renderer/dom/DomRenderer.ts index 1beaadcd..b512aa70 100644 --- a/src/renderer/dom/DomRenderer.ts +++ b/src/browser/renderer/dom/DomRenderer.ts @@ -29,8 +29,8 @@ export class DomRenderer extends Disposable implements IRenderer { private _rowFactory: DomRendererRowFactory; private _terminalClass: number = nextTerminalId++; - private _themeStyleElement: HTMLStyleElement; - private _dimensionsStyleElement: HTMLStyleElement; + private _themeStyleElement!: HTMLStyleElement; + private _dimensionsStyleElement!: HTMLStyleElement; private _rowContainer: HTMLElement; private _rowElements: HTMLElement[] = []; private _selectionContainer: HTMLElement; @@ -43,10 +43,10 @@ export class DomRenderer extends Disposable implements IRenderer { private readonly _screenElement: HTMLElement, private readonly _viewportElement: HTMLElement, private readonly _linkifier: ILinkifier, - private readonly _charSizeService: ICharSizeService, - private readonly _optionsService: IOptionsService, - private readonly _bufferService: IBufferService, - private readonly _renderService: IRenderService + @ICharSizeService private readonly _charSizeService: ICharSizeService, + @IOptionsService private readonly _optionsService: IOptionsService, + @IBufferService private readonly _bufferService: IBufferService, + @IRenderService private readonly _renderService: IRenderService ) { super(); @@ -60,18 +60,18 @@ export class DomRenderer extends Disposable implements IRenderer { this._selectionContainer.setAttribute('aria-hidden', 'true'); this.dimensions = { - scaledCharWidth: null, - scaledCharHeight: null, - scaledCellWidth: null, - scaledCellHeight: null, - scaledCharLeft: null, - scaledCharTop: null, - scaledCanvasWidth: null, - scaledCanvasHeight: null, - canvasWidth: null, - canvasHeight: null, - actualCellWidth: null, - actualCellHeight: null + scaledCharWidth: 0, + scaledCharHeight: 0, + scaledCellWidth: 0, + scaledCellHeight: 0, + scaledCharLeft: 0, + scaledCharTop: 0, + scaledCanvasWidth: 0, + scaledCanvasHeight: 0, + canvasWidth: 0, + canvasHeight: 0, + actualCellWidth: 0, + actualCellHeight: 0 }; this._updateDimensions(); this._injectCss(); @@ -246,7 +246,7 @@ export class DomRenderer extends Disposable implements IRenderer { } // Remove excess elements while (this._rowElements.length > rows) { - this._rowContainer.removeChild(this._rowElements.pop()); + this._rowContainer.removeChild(this._rowElements.pop()!); } } @@ -356,7 +356,7 @@ export class DomRenderer extends Disposable implements IRenderer { const row = y + this._bufferService.buffer.ydisp; const lineData = this._bufferService.buffer.lines.get(row); const cursorStyle = this._optionsService.options.cursorStyle; - rowElement.appendChild(this._rowFactory.createRow(lineData, row === cursorAbsoluteY, cursorStyle, cursorX, cursorBlink, this.dimensions.actualCellWidth, this._bufferService.cols)); + rowElement.appendChild(this._rowFactory.createRow(lineData!, row === cursorAbsoluteY, cursorStyle, cursorX, cursorBlink, this.dimensions.actualCellWidth, this._bufferService.cols)); } } From 509d0e701945c7313dae4d8ef7cfdfc2c91487a9 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Thu, 7 Nov 2019 17:44:03 -0800 Subject: [PATCH 36/92] Remove some ITerminal usage from Renderer --- src/renderer/Renderer.ts | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/renderer/Renderer.ts b/src/renderer/Renderer.ts index d3977af3..5bc9d004 100644 --- a/src/renderer/Renderer.ts +++ b/src/renderer/Renderer.ts @@ -30,19 +30,19 @@ export class Renderer extends Disposable implements IRenderer { constructor( private _colors: IColorSet, private readonly _terminal: ITerminal, - readonly bufferService: IBufferService, + private readonly _bufferService: IBufferService, private readonly _charSizeService: ICharSizeService, - readonly optionsService: IOptionsService + private readonly _optionsService: IOptionsService ) { super(); - const allowTransparency = this._terminal.options.allowTransparency; - this._characterJoinerRegistry = new CharacterJoinerRegistry(bufferService); + const allowTransparency = this._optionsService.options.allowTransparency; + this._characterJoinerRegistry = new CharacterJoinerRegistry(this._bufferService); this._renderLayers = [ - new TextRenderLayer(this._terminal.screenElement, 0, this._colors, this._characterJoinerRegistry, allowTransparency, this._id, bufferService, optionsService), - new SelectionRenderLayer(this._terminal.screenElement, 1, this._colors, this._id, bufferService, optionsService), - new LinkRenderLayer(this._terminal.screenElement, 2, this._colors, this._id, this._terminal.linkifier, bufferService, optionsService), - new CursorRenderLayer(this._terminal.screenElement, 3, this._colors, this._terminal, this._id, bufferService, optionsService) + new TextRenderLayer(this._terminal.screenElement, 0, this._colors, this._characterJoinerRegistry, allowTransparency, this._id, this._bufferService, _optionsService), + new SelectionRenderLayer(this._terminal.screenElement, 1, this._colors, this._id, this._bufferService, _optionsService), + new LinkRenderLayer(this._terminal.screenElement, 2, this._colors, this._id, this._terminal.linkifier, this._bufferService, _optionsService), + new CursorRenderLayer(this._terminal.screenElement, 3, this._colors, this._terminal, this._id, this._bufferService, _optionsService) ]; this.dimensions = { scaledCharWidth: null, @@ -74,7 +74,7 @@ export class Renderer extends Disposable implements IRenderer { // and the terminal needs to refreshed if (this._devicePixelRatio !== window.devicePixelRatio) { this._devicePixelRatio = window.devicePixelRatio; - this.onResize(this._terminal.cols, this._terminal.rows); + this.onResize(this._bufferService.cols, this._bufferService.rows); } } @@ -101,7 +101,7 @@ export class Renderer extends Disposable implements IRenderer { } public onCharSizeChanged(): void { - this.onResize(this._terminal.cols, this._terminal.rows); + this.onResize(this._bufferService.cols, this._bufferService.rows); } public onBlur(): void { @@ -163,23 +163,23 @@ export class Renderer extends Disposable implements IRenderer { // will be floored because since lineHeight can never be lower then 1, there // is a guarentee that the scaled line height will always be larger than // scaled char height. - this.dimensions.scaledCellHeight = Math.floor(this.dimensions.scaledCharHeight * this._terminal.options.lineHeight); + this.dimensions.scaledCellHeight = Math.floor(this.dimensions.scaledCharHeight * this._optionsService.options.lineHeight); // Calculate the y coordinate within a cell that text should draw from in // order to draw in the center of a cell. - this.dimensions.scaledCharTop = this._terminal.options.lineHeight === 1 ? 0 : Math.round((this.dimensions.scaledCellHeight - this.dimensions.scaledCharHeight) / 2); + this.dimensions.scaledCharTop = this._optionsService.options.lineHeight === 1 ? 0 : Math.round((this.dimensions.scaledCellHeight - this.dimensions.scaledCharHeight) / 2); // Calculate the scaled cell width, taking the letterSpacing into account. - this.dimensions.scaledCellWidth = this.dimensions.scaledCharWidth + Math.round(this._terminal.options.letterSpacing); + this.dimensions.scaledCellWidth = this.dimensions.scaledCharWidth + Math.round(this._optionsService.options.letterSpacing); // Calculate the x coordinate with a cell that text should draw from in // order to draw in the center of a cell. - this.dimensions.scaledCharLeft = Math.floor(this._terminal.options.letterSpacing / 2); + this.dimensions.scaledCharLeft = Math.floor(this._optionsService.options.letterSpacing / 2); // Recalculate the canvas dimensions; scaled* define the actual number of // pixel in the canvas - this.dimensions.scaledCanvasHeight = this._terminal.rows * this.dimensions.scaledCellHeight; - this.dimensions.scaledCanvasWidth = this._terminal.cols * this.dimensions.scaledCellWidth; + this.dimensions.scaledCanvasHeight = this._bufferService.rows * this.dimensions.scaledCellHeight; + this.dimensions.scaledCanvasWidth = this._bufferService.cols * this.dimensions.scaledCellWidth; // The the size of the canvas on the page. It's very important that this // rounds to nearest integer and not ceils as browsers often set @@ -194,8 +194,8 @@ export class Renderer extends Disposable implements IRenderer { // account window.devicePixelRatio. ICharSizeService.width/height by itself // is insufficient when the page is not at 100% zoom level as it's measured // in CSS pixels, but the actual char size on the canvas can differ. - this.dimensions.actualCellHeight = this.dimensions.canvasHeight / this._terminal.rows; - this.dimensions.actualCellWidth = this.dimensions.canvasWidth / this._terminal.cols; + this.dimensions.actualCellHeight = this.dimensions.canvasHeight / this._bufferService.rows; + this.dimensions.actualCellWidth = this.dimensions.canvasWidth / this._bufferService.cols; } public registerCharacterJoiner(handler: CharacterJoinerHandler): number { From 1e1038b2c7787ed0e050d8286d3e4fff7a7b61c8 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Thu, 7 Nov 2019 18:09:13 -0800 Subject: [PATCH 37/92] Remove renderer circular dep on renderservice Also fix super.dispose not being called correctly on RenderService --- addons/xterm-addon-webgl/src/WebglRenderer.ts | 12 ++++++++---- .../src/renderLayer/CursorRenderLayer.ts | 16 +++++++++++----- src/TestUtils.test.ts | 3 ++- src/browser/renderer/Types.d.ts | 8 ++++++++ src/browser/renderer/dom/DomRenderer.ts | 13 ++++++++----- src/browser/services/RenderService.ts | 5 +++++ src/renderer/CursorRenderLayer.ts | 10 ++++++---- src/renderer/Renderer.ts | 8 ++++++-- 8 files changed, 54 insertions(+), 21 deletions(-) diff --git a/addons/xterm-addon-webgl/src/WebglRenderer.ts b/addons/xterm-addon-webgl/src/WebglRenderer.ts index 3f8163ba..1f28d589 100644 --- a/addons/xterm-addon-webgl/src/WebglRenderer.ts +++ b/addons/xterm-addon-webgl/src/WebglRenderer.ts @@ -15,13 +15,14 @@ import { INVERTED_DEFAULT_COLOR } from 'browser/renderer/atlas/Constants'; import { RenderModel, COMBINED_CHAR_BIT_MASK } from './RenderModel'; import { Disposable } from 'common/Lifecycle'; import { DEFAULT_COLOR, CHAR_DATA_CHAR_INDEX, CHAR_DATA_CODE_INDEX, NULL_CELL_CODE } from 'common/buffer/Constants'; -import { Terminal } from 'xterm'; +import { Terminal, IEvent } from 'xterm'; import { getLuminance } from './ColorUtils'; import { IRenderLayer } from './renderLayer/Types'; -import { IRenderDimensions, IRenderer } from 'browser/renderer/Types'; +import { IRenderDimensions, IRenderer, IRequestRefreshRowsEvent } from 'browser/renderer/Types'; import { IColorSet } from 'browser/Types'; import { FLAGS } from './Constants'; import { getCompatAttr } from './CharDataCompat'; +import { EventEmitter } from 'common/EventEmitter'; export const INDICIES_PER_CELL = 4; @@ -41,6 +42,9 @@ export class WebglRenderer extends Disposable implements IRenderer { private _core: ITerminal; + private _onRequestRefreshRows = new EventEmitter(); + public get onRequestRefreshRows(): IEvent { return this._onRequestRefreshRows.event; } + constructor( private _terminal: Terminal, private _colors: IColorSet, @@ -54,7 +58,7 @@ export class WebglRenderer extends Disposable implements IRenderer { this._renderLayers = [ new LinkRenderLayer(this._core.screenElement, 2, this._colors, this._core), - new CursorRenderLayer(this._core.screenElement, 3, this._colors) + new CursorRenderLayer(this._core.screenElement, 3, this._colors, this._onRequestRefreshRows) ]; this.dimensions = { scaledCharWidth: 0, @@ -186,7 +190,7 @@ export class WebglRenderer extends Disposable implements IRenderer { this._glyphRenderer.updateSelection(this._model, columnSelectMode); // TODO: #2102 Should this move to RenderCoordinator? - this._core.refresh(0, this._terminal.rows - 1); + this._onRequestRefreshRows.fire({ start: 0, end: this._terminal.rows - 1 }); } public onCursorMove(): void { diff --git a/addons/xterm-addon-webgl/src/renderLayer/CursorRenderLayer.ts b/addons/xterm-addon-webgl/src/renderLayer/CursorRenderLayer.ts index 8a67526e..c7c68ff6 100644 --- a/addons/xterm-addon-webgl/src/renderLayer/CursorRenderLayer.ts +++ b/addons/xterm-addon-webgl/src/renderLayer/CursorRenderLayer.ts @@ -8,7 +8,8 @@ import { BaseRenderLayer } from './BaseRenderLayer'; import { ICellData } from 'common/Types'; import { CellData } from 'common/buffer/CellData'; import { IColorSet } from 'browser/Types'; -import { IRenderDimensions } from 'browser/renderer/Types'; +import { IRenderDimensions, IRequestRefreshRowsEvent } from 'browser/renderer/Types'; +import { IEventEmitter } from 'common/EventEmitter'; interface ICursorState { x: number; @@ -29,7 +30,12 @@ export class CursorRenderLayer extends BaseRenderLayer { private _cursorBlinkStateManager: CursorBlinkStateManager | undefined; private _cell: ICellData = new CellData(); - constructor(container: HTMLElement, zIndex: number, colors: IColorSet) { + constructor( + container: HTMLElement, + zIndex: number, + colors: IColorSet, + private _onRequestRefreshRowsEvent: IEventEmitter + ) { super(container, 'cursor', zIndex, true, colors); this._state = { x: 0, @@ -70,14 +76,14 @@ export class CursorRenderLayer extends BaseRenderLayer { if (this._cursorBlinkStateManager) { this._cursorBlinkStateManager.pause(); } - terminal.refresh(terminal.buffer.cursorY, terminal.buffer.cursorY); + this._onRequestRefreshRowsEvent.fire({ start: terminal.buffer.cursorY, end: terminal.buffer.cursorY }); } public onFocus(terminal: Terminal): void { if (this._cursorBlinkStateManager) { this._cursorBlinkStateManager.resume(terminal); } else { - terminal.refresh(terminal.buffer.cursorY, terminal.buffer.cursorY); + this._onRequestRefreshRowsEvent.fire({ start: terminal.buffer.cursorY, end: terminal.buffer.cursorY }); } } @@ -95,7 +101,7 @@ export class CursorRenderLayer extends BaseRenderLayer { } // Request a refresh from the terminal as management of rendering is being // moved back to the terminal - terminal.refresh(terminal.buffer.cursorY, terminal.buffer.cursorY); + this._onRequestRefreshRowsEvent.fire({ start: terminal.buffer.cursorY, end: terminal.buffer.cursorY }); } public onCursorMove(terminal: Terminal): void { diff --git a/src/TestUtils.test.ts b/src/TestUtils.test.ts index a70f751c..e7c48381 100644 --- a/src/TestUtils.test.ts +++ b/src/TestUtils.test.ts @@ -3,7 +3,7 @@ * @license MIT */ -import { IRenderer, IRenderDimensions, CharacterJoinerHandler } from 'browser/renderer/Types'; +import { IRenderer, IRenderDimensions, CharacterJoinerHandler, IRequestRefreshRowsEvent } from 'browser/renderer/Types'; import { IInputHandlingTerminal, ICompositionHelper, ITerminal, IBrowser, ITerminalOptions } from './Types'; import { IBuffer, IBufferStringIterator, IBufferSet } from 'common/buffer/Types'; import { IBufferLine, ICellData, IAttributeData, ICircularList, XtermListener, ICharset, CoreMouseEventType } from 'common/Types'; @@ -362,6 +362,7 @@ export class MockBuffer implements IBuffer { } export class MockRenderer implements IRenderer { + onRequestRefreshRows: IEvent; onCanvasResize: IEvent<{ width: number; height: number; }>; onRender: IEvent<{ start: number; end: number; }>; dispose(): void { diff --git a/src/browser/renderer/Types.d.ts b/src/browser/renderer/Types.d.ts index 7f84dfd4..b4c321a1 100644 --- a/src/browser/renderer/Types.d.ts +++ b/src/browser/renderer/Types.d.ts @@ -5,6 +5,7 @@ import { IDisposable } from 'common/Types'; import { IColorSet } from 'browser/Types'; +import { IEvent } from 'common/EventEmitter'; export type CharacterJoinerHandler = (text: string) => [number, number][]; @@ -23,6 +24,11 @@ export interface IRenderDimensions { actualCellHeight: number; } +export interface IRequestRefreshRowsEvent { + start: number; + end: number; +} + /** * Note that IRenderer implementations should emit the refresh event after * rendering rows to the screen. @@ -30,6 +36,8 @@ export interface IRenderDimensions { export interface IRenderer extends IDisposable { readonly dimensions: IRenderDimensions; + readonly onRequestRefreshRows: IEvent; + dispose(): void; setColors(colors: IColorSet): void; onDevicePixelRatioChange(): void; diff --git a/src/browser/renderer/dom/DomRenderer.ts b/src/browser/renderer/dom/DomRenderer.ts index b512aa70..3d544a90 100644 --- a/src/browser/renderer/dom/DomRenderer.ts +++ b/src/browser/renderer/dom/DomRenderer.ts @@ -3,13 +3,14 @@ * @license MIT */ -import { IRenderer, IRenderDimensions, CharacterJoinerHandler } from 'browser/renderer/Types'; +import { IRenderer, IRenderDimensions, CharacterJoinerHandler, IRequestRefreshRowsEvent } from 'browser/renderer/Types'; import { BOLD_CLASS, ITALIC_CLASS, CURSOR_CLASS, CURSOR_STYLE_BLOCK_CLASS, CURSOR_BLINK_CLASS, CURSOR_STYLE_BAR_CLASS, CURSOR_STYLE_UNDERLINE_CLASS, DomRendererRowFactory } from 'browser/renderer/dom/DomRendererRowFactory'; import { INVERTED_DEFAULT_COLOR } from 'browser/renderer/atlas/Constants'; import { Disposable } from 'common/Lifecycle'; import { IColorSet, ILinkifierEvent, ILinkifier } from 'browser/Types'; -import { ICharSizeService, IRenderService } from 'browser/services/Services'; +import { ICharSizeService } from 'browser/services/Services'; import { IOptionsService, IBufferService } from 'common/services/Services'; +import { EventEmitter, IEvent } from 'common/EventEmitter'; const TERMINAL_CLASS_PREFIX = 'xterm-dom-renderer-owner-'; const ROW_CONTAINER_CLASS = 'xterm-rows'; @@ -37,6 +38,9 @@ export class DomRenderer extends Disposable implements IRenderer { public dimensions: IRenderDimensions; + private _onRequestRefreshRows = new EventEmitter(); + public get onRequestRefreshRows(): IEvent { return this._onRequestRefreshRows.event; } + constructor( private _colors: IColorSet, private readonly _element: HTMLElement, @@ -45,8 +49,7 @@ export class DomRenderer extends Disposable implements IRenderer { private readonly _linkifier: ILinkifier, @ICharSizeService private readonly _charSizeService: ICharSizeService, @IOptionsService private readonly _optionsService: IOptionsService, - @IBufferService private readonly _bufferService: IBufferService, - @IRenderService private readonly _renderService: IRenderService + @IBufferService private readonly _bufferService: IBufferService ) { super(); @@ -337,7 +340,7 @@ export class DomRenderer extends Disposable implements IRenderer { // Force a refresh this._updateDimensions(); this._injectCss(); - this._renderService.refreshRows(0, this._bufferService.rows - 1); + this._onRequestRefreshRows.fire({ start: 0, end: this._bufferService.rows - 1 }); } public clear(): void { diff --git a/src/browser/services/RenderService.ts b/src/browser/services/RenderService.ts index 63084f54..b28c9971 100644 --- a/src/browser/services/RenderService.ts +++ b/src/browser/services/RenderService.ts @@ -51,6 +51,9 @@ export class RenderService extends Disposable implements IRenderService { this.register(optionsService.onOptionChange(() => this._renderer.onOptionsChanged())); this.register(charSizeService.onCharSizeChange(() => this.onCharSizeChanged())); + // No need to register this as renderer is explicitly disposed in RenderService.dispose + this._renderer.onRequestRefreshRows(e => this.refreshRows(e.start, e.end)); + // dprchange should handle this case, we need this as well for browsers that don't support the // matchMedia query. this.register(addDisposableDomListener(window, 'resize', () => this.onDevicePixelRatioChange())); @@ -105,12 +108,14 @@ export class RenderService extends Disposable implements IRenderService { public dispose(): void { this._renderer.dispose(); + super.dispose(); } public setRenderer(renderer: IRenderer): void { // TODO: RenderService should be the only one to dispose the renderer this._renderer.dispose(); this._renderer = renderer; + this._renderer.onRequestRefreshRows(e => this.refreshRows(e.start, e.end)); this.refreshRows(0, this._rowCount - 1); } diff --git a/src/renderer/CursorRenderLayer.ts b/src/renderer/CursorRenderLayer.ts index 1f2791f1..d2278983 100644 --- a/src/renderer/CursorRenderLayer.ts +++ b/src/renderer/CursorRenderLayer.ts @@ -3,13 +3,14 @@ * @license MIT */ -import { IRenderDimensions } from 'browser/renderer/Types'; +import { IRenderDimensions, IRequestRefreshRowsEvent } from 'browser/renderer/Types'; import { BaseRenderLayer } from '../browser/renderer/BaseRenderLayer'; import { ITerminal } from '../Types'; import { ICellData } from 'common/Types'; import { CellData } from 'common/buffer/CellData'; import { IColorSet } from 'browser/Types'; import { IBufferService, IOptionsService } from 'common/services/Services'; +import { IEventEmitter } from 'common/EventEmitter'; interface ICursorState { x: number; @@ -36,6 +37,7 @@ export class CursorRenderLayer extends BaseRenderLayer { colors: IColorSet, private _terminal: ITerminal, rendererId: number, + private _onRequestRefreshRowsEvent: IEventEmitter, readonly bufferService: IBufferService, readonly optionsService: IOptionsService ) { @@ -80,14 +82,14 @@ export class CursorRenderLayer extends BaseRenderLayer { if (this._cursorBlinkStateManager) { this._cursorBlinkStateManager.pause(); } - this._terminal.refresh(this._bufferService.buffer.y, this._bufferService.buffer.y); + this._onRequestRefreshRowsEvent.fire({ start: this._bufferService.buffer.y, end: this._bufferService.buffer.y }); } public onFocus(): void { if (this._cursorBlinkStateManager) { this._cursorBlinkStateManager.resume(); } else { - this._terminal.refresh(this._bufferService.buffer.y, this._bufferService.buffer.y); + this._onRequestRefreshRowsEvent.fire({ start: this._bufferService.buffer.y, end: this._bufferService.buffer.y }); } } @@ -106,7 +108,7 @@ export class CursorRenderLayer extends BaseRenderLayer { } // Request a refresh from the terminal as management of rendering is being // moved back to the terminal - this._terminal.refresh(this._bufferService.buffer.y, this._bufferService.buffer.y); + this._onRequestRefreshRowsEvent.fire({ start: this._bufferService.buffer.y, end: this._bufferService.buffer.y }); } public onCursorMove(): void { diff --git a/src/renderer/Renderer.ts b/src/renderer/Renderer.ts index 5bc9d004..45abbb82 100644 --- a/src/renderer/Renderer.ts +++ b/src/renderer/Renderer.ts @@ -6,7 +6,7 @@ import { TextRenderLayer } from '../browser/renderer/TextRenderLayer'; import { SelectionRenderLayer } from '../browser/renderer/SelectionRenderLayer'; import { CursorRenderLayer } from './CursorRenderLayer'; -import { IRenderLayer, IRenderer, IRenderDimensions, CharacterJoinerHandler, ICharacterJoinerRegistry } from 'browser/renderer/Types'; +import { IRenderLayer, IRenderer, IRenderDimensions, CharacterJoinerHandler, ICharacterJoinerRegistry, IRequestRefreshRowsEvent } from 'browser/renderer/Types'; import { ITerminal } from '../Types'; import { LinkRenderLayer } from '../browser/renderer/LinkRenderLayer'; import { CharacterJoinerRegistry } from 'browser/renderer/CharacterJoinerRegistry'; @@ -15,6 +15,7 @@ import { IColorSet } from 'browser/Types'; import { ICharSizeService } from 'browser/services/Services'; import { IBufferService, IOptionsService } from 'common/services/Services'; import { removeTerminalFromCache } from 'browser/renderer/atlas/CharAtlasCache'; +import { EventEmitter, IEvent } from 'common/EventEmitter'; let nextRendererId = 1; @@ -27,6 +28,9 @@ export class Renderer extends Disposable implements IRenderer { public dimensions: IRenderDimensions; + private _onRequestRefreshRows = new EventEmitter(); + public get onRequestRefreshRows(): IEvent { return this._onRequestRefreshRows.event; } + constructor( private _colors: IColorSet, private readonly _terminal: ITerminal, @@ -42,7 +46,7 @@ export class Renderer extends Disposable implements IRenderer { new TextRenderLayer(this._terminal.screenElement, 0, this._colors, this._characterJoinerRegistry, allowTransparency, this._id, this._bufferService, _optionsService), new SelectionRenderLayer(this._terminal.screenElement, 1, this._colors, this._id, this._bufferService, _optionsService), new LinkRenderLayer(this._terminal.screenElement, 2, this._colors, this._id, this._terminal.linkifier, this._bufferService, _optionsService), - new CursorRenderLayer(this._terminal.screenElement, 3, this._colors, this._terminal, this._id, this._bufferService, _optionsService) + new CursorRenderLayer(this._terminal.screenElement, 3, this._colors, this._terminal, this._id, this._onRequestRefreshRows, this._bufferService, _optionsService) ]; this.dimensions = { scaledCharWidth: null, From 9d7935f8bdce02f669ffa757f1e2413ef6c3e1fe Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Thu, 7 Nov 2019 18:21:06 -0800 Subject: [PATCH 38/92] Move cursorState into core service --- src/Terminal.test.ts | 11 --------- src/Terminal.ts | 11 +++------ src/Types.d.ts | 1 - src/common/TestUtils.test.ts | 3 +++ src/common/services/CoreService.test.ts | 32 +++++++++++++++++++++++++ src/common/services/CoreService.ts | 3 +++ src/common/services/Services.ts | 7 ++++++ src/renderer/CursorRenderLayer.ts | 7 +++--- src/renderer/Renderer.ts | 7 +++--- 9 files changed, 56 insertions(+), 26 deletions(-) create mode 100644 src/common/services/CoreService.test.ts diff --git a/src/Terminal.test.ts b/src/Terminal.test.ts index 667bf076..b55a516b 100644 --- a/src/Terminal.test.ts +++ b/src/Terminal.test.ts @@ -163,17 +163,6 @@ describe('Terminal', () => { }); }); - describe('reset', () => { - it('should not affect cursorState', () => { - term.cursorState = 1; - term.reset(); - assert.equal(term.cursorState, 1); - term.cursorState = 0; - term.reset(); - assert.equal(term.cursorState, 0); - }); - }); - describe('clear', () => { it('should clear a buffer equal to rows', () => { const promptLine = term.buffer.lines.get(term.buffer.ybase + term.buffer.y); diff --git a/src/Terminal.ts b/src/Terminal.ts index 6626b966..f8f2c946 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -90,8 +90,6 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp // TODO: We should remove options once components adopt optionsService public get options(): ITerminalOptions { return this.optionsService.options; } - // TODO: This can be changed to an enum or boolean, 0 and 1 seem to be the only options - public cursorState: number; public cursorHidden: boolean; private _customKeyEventHandler: CustomKeyEventHandler; @@ -251,7 +249,6 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp private _setup(): void { this._parent = document ? document.body : null; - this.cursorState = 0; this.cursorHidden = false; this._customKeyEventHandler = null; @@ -637,7 +634,7 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp private _createRenderer(): IRenderer { switch (this.options.rendererType) { - case 'canvas': return new Renderer(this._colorManager.colors, this, this._bufferService, this._charSizeService, this.optionsService); + case 'canvas': return new Renderer(this._colorManager.colors, this, this._bufferService, this._charSizeService, this.optionsService, this._coreService); case 'dom': return this._instantiationService.createInstance(DomRenderer, this._colorManager.colors, this.element, this.screenElement, this._viewportElement, this.linkifier); default: throw new Error(`Unrecognized rendererType "${this.options.rendererType}"`); } @@ -945,8 +942,8 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp * Display the cursor element */ public showCursor(): void { - if (!this.cursorState) { - this.cursorState = 1; + if (!this._coreService.isCursorInitialized) { + this._coreService.isCursorInitialized = true; this.refresh(this.buffer.y, this.buffer.y); } } @@ -1510,7 +1507,6 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp this.options.cols = this.cols; const customKeyEventHandler = this._customKeyEventHandler; const inputHandler = this._inputHandler; - const cursorState = this.cursorState; const userScrolling = this._userScrolling; this._setup(); @@ -1522,7 +1518,6 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp // reattach this._customKeyEventHandler = customKeyEventHandler; this._inputHandler = inputHandler; - this.cursorState = cursorState; this._userScrolling = userScrolling; // do a full screen refresh diff --git a/src/Types.d.ts b/src/Types.d.ts index be1019e5..11362058 100644 --- a/src/Types.d.ts +++ b/src/Types.d.ts @@ -155,7 +155,6 @@ export interface ITerminal extends IPublicTerminal, IElementAccessor, IBufferAcc screenElement: HTMLElement; browser: IBrowser; cursorHidden: boolean; - cursorState: number; buffer: IBuffer; buffers: IBufferSet; isFocused: boolean; diff --git a/src/common/TestUtils.test.ts b/src/common/TestUtils.test.ts index f2721bbe..7d9c3aa6 100644 --- a/src/common/TestUtils.test.ts +++ b/src/common/TestUtils.test.ts @@ -43,6 +43,9 @@ export class MockCoreMouseService implements ICoreMouseService { } export class MockCoreService implements ICoreService { + isCursorInitialized: boolean = false; + isCursorHidden: boolean = false; + isFocused: boolean = false; serviceBrand: any; decPrivateModes: IDecPrivateModes = {} as any; onData: IEvent = new EventEmitter().event; diff --git a/src/common/services/CoreService.test.ts b/src/common/services/CoreService.test.ts new file mode 100644 index 00000000..00be4953 --- /dev/null +++ b/src/common/services/CoreService.test.ts @@ -0,0 +1,32 @@ +/** + * Copyright (c) 2019 The xterm.js authors. All rights reserved. + * @license MIT + */ + +import { ICoreService } from 'common/services/Services'; +import { CoreService } from 'common/services/CoreService'; +import { MockBufferService, MockLogService, MockOptionsService } from 'common/TestUtils.test'; +import { assert } from 'chai'; + +describe('CoreService', () => { + let coreService: ICoreService; + + beforeEach(() => { + coreService = new CoreService( + () => {}, + new MockBufferService(80, 30), + new MockLogService(), + new MockOptionsService()); + }); + + describe('reset', () => { + it('should not affect isCursorInitialized', () => { + coreService.isCursorInitialized = true; + coreService.reset(); + assert.equal(coreService.isCursorInitialized, true); + coreService.isCursorInitialized = false; + coreService.reset(); + assert.equal(coreService.isCursorInitialized, false); + }); + }); +}); diff --git a/src/common/services/CoreService.ts b/src/common/services/CoreService.ts index 0e0ba609..a235e539 100644 --- a/src/common/services/CoreService.ts +++ b/src/common/services/CoreService.ts @@ -15,6 +15,9 @@ const DEFAULT_DEC_PRIVATE_MODES: IDecPrivateModes = Object.freeze({ export class CoreService implements ICoreService { serviceBrand: any; + public isCursorInitialized: boolean = false; + public isCursorHidden: boolean = false; + public isFocused: boolean = false; public decPrivateModes: IDecPrivateModes; private _onData = new EventEmitter(); diff --git a/src/common/services/Services.ts b/src/common/services/Services.ts index 0872d3db..c067448d 100644 --- a/src/common/services/Services.ts +++ b/src/common/services/Services.ts @@ -58,6 +58,13 @@ export const ICoreService = createDecorator('CoreService'); export interface ICoreService { serviceBrand: any; + /** + * Initially the cursor will not be visible until the first time the terminal + * is focused. + */ + isCursorInitialized: boolean; + isCursorHidden: boolean; + isFocused: boolean; readonly decPrivateModes: IDecPrivateModes; readonly onData: IEvent; diff --git a/src/renderer/CursorRenderLayer.ts b/src/renderer/CursorRenderLayer.ts index d2278983..bbbbcea4 100644 --- a/src/renderer/CursorRenderLayer.ts +++ b/src/renderer/CursorRenderLayer.ts @@ -9,7 +9,7 @@ import { ITerminal } from '../Types'; import { ICellData } from 'common/Types'; import { CellData } from 'common/buffer/CellData'; import { IColorSet } from 'browser/Types'; -import { IBufferService, IOptionsService } from 'common/services/Services'; +import { IBufferService, IOptionsService, ICoreService } from 'common/services/Services'; import { IEventEmitter } from 'common/EventEmitter'; interface ICursorState { @@ -39,7 +39,8 @@ export class CursorRenderLayer extends BaseRenderLayer { rendererId: number, private _onRequestRefreshRowsEvent: IEventEmitter, readonly bufferService: IBufferService, - readonly optionsService: IOptionsService + readonly optionsService: IOptionsService, + private readonly _coreService: ICoreService ) { super(container, 'cursor', zIndex, true, colors, rendererId, bufferService, optionsService); this._state = { @@ -127,7 +128,7 @@ export class CursorRenderLayer extends BaseRenderLayer { private _render(triggeredByAnimationFrame: boolean): void { // Don't draw the cursor if it's hidden - if (!this._terminal.cursorState || this._terminal.cursorHidden) { + if (!this._coreService.isCursorInitialized || this._terminal.cursorHidden) { this._clearCursor(); return; } diff --git a/src/renderer/Renderer.ts b/src/renderer/Renderer.ts index 45abbb82..28c4ce03 100644 --- a/src/renderer/Renderer.ts +++ b/src/renderer/Renderer.ts @@ -13,7 +13,7 @@ import { CharacterJoinerRegistry } from 'browser/renderer/CharacterJoinerRegistr import { Disposable } from 'common/Lifecycle'; import { IColorSet } from 'browser/Types'; import { ICharSizeService } from 'browser/services/Services'; -import { IBufferService, IOptionsService } from 'common/services/Services'; +import { IBufferService, IOptionsService, ICoreService } from 'common/services/Services'; import { removeTerminalFromCache } from 'browser/renderer/atlas/CharAtlasCache'; import { EventEmitter, IEvent } from 'common/EventEmitter'; @@ -36,7 +36,8 @@ export class Renderer extends Disposable implements IRenderer { private readonly _terminal: ITerminal, private readonly _bufferService: IBufferService, private readonly _charSizeService: ICharSizeService, - private readonly _optionsService: IOptionsService + private readonly _optionsService: IOptionsService, + readonly coreService: ICoreService ) { super(); const allowTransparency = this._optionsService.options.allowTransparency; @@ -46,7 +47,7 @@ export class Renderer extends Disposable implements IRenderer { new TextRenderLayer(this._terminal.screenElement, 0, this._colors, this._characterJoinerRegistry, allowTransparency, this._id, this._bufferService, _optionsService), new SelectionRenderLayer(this._terminal.screenElement, 1, this._colors, this._id, this._bufferService, _optionsService), new LinkRenderLayer(this._terminal.screenElement, 2, this._colors, this._id, this._terminal.linkifier, this._bufferService, _optionsService), - new CursorRenderLayer(this._terminal.screenElement, 3, this._colors, this._terminal, this._id, this._onRequestRefreshRows, this._bufferService, _optionsService) + new CursorRenderLayer(this._terminal.screenElement, 3, this._colors, this._terminal, this._id, this._onRequestRefreshRows, this._bufferService, _optionsService, coreService) ]; this.dimensions = { scaledCharWidth: null, From c204ab00e04908d4c2e808457d058ae69b2710a7 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Thu, 7 Nov 2019 18:22:18 -0800 Subject: [PATCH 39/92] Move cursorHidden into core service --- src/InputHandler.ts | 6 +++--- src/Terminal.ts | 3 --- src/Types.d.ts | 2 -- src/renderer/CursorRenderLayer.ts | 2 +- 4 files changed, 4 insertions(+), 9 deletions(-) diff --git a/src/InputHandler.ts b/src/InputHandler.ts index 74208482..3994d532 100644 --- a/src/InputHandler.ts +++ b/src/InputHandler.ts @@ -1449,7 +1449,7 @@ export class InputHandler extends Disposable implements IInputHandler { this._logService.debug('DECSET 1015 not supported (see #2507)'); break; case 25: // show cursor - this._terminal.cursorHidden = false; + this._coreService.isCursorHidden = false; break; case 1048: // alt screen cursor this.saveCursor(); @@ -1620,7 +1620,7 @@ export class InputHandler extends Disposable implements IInputHandler { this._logService.debug('DECRST 1015 not supported (see #2507)'); break; case 25: // hide cursor - this._terminal.cursorHidden = true; + this._coreService.isCursorHidden = true; break; case 1048: // alt screen cursor this.restoreCursor(); @@ -1954,7 +1954,7 @@ export class InputHandler extends Disposable implements IInputHandler { * http://vt100.net/docs/vt220-rm/table4-10.html */ public softReset(params: IParams): void { - this._terminal.cursorHidden = false; + this._coreService.isCursorHidden = false; this._terminal.insertMode = false; this._terminal.originMode = false; this._terminal.wraparoundMode = true; // defaults: xterm - true, vt100 - false diff --git a/src/Terminal.ts b/src/Terminal.ts index f8f2c946..e19901b4 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -90,8 +90,6 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp // TODO: We should remove options once components adopt optionsService public get options(): ITerminalOptions { return this.optionsService.options; } - public cursorHidden: boolean; - private _customKeyEventHandler: CustomKeyEventHandler; // common services @@ -249,7 +247,6 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp private _setup(): void { this._parent = document ? document.body : null; - this.cursorHidden = false; this._customKeyEventHandler = null; // modes diff --git a/src/Types.d.ts b/src/Types.d.ts index 11362058..4f6ce40f 100644 --- a/src/Types.d.ts +++ b/src/Types.d.ts @@ -38,7 +38,6 @@ export interface IInputHandlingTerminal { savedCols: number; mouseEvents: CoreMouseEventType; sendFocus: boolean; - cursorHidden: boolean; buffers: IBufferSet; buffer: IBuffer; @@ -154,7 +153,6 @@ export interface IInputHandler { export interface ITerminal extends IPublicTerminal, IElementAccessor, IBufferAccessor, ILinkifierAccessor { screenElement: HTMLElement; browser: IBrowser; - cursorHidden: boolean; buffer: IBuffer; buffers: IBufferSet; isFocused: boolean; diff --git a/src/renderer/CursorRenderLayer.ts b/src/renderer/CursorRenderLayer.ts index bbbbcea4..0bfdd35b 100644 --- a/src/renderer/CursorRenderLayer.ts +++ b/src/renderer/CursorRenderLayer.ts @@ -128,7 +128,7 @@ export class CursorRenderLayer extends BaseRenderLayer { private _render(triggeredByAnimationFrame: boolean): void { // Don't draw the cursor if it's hidden - if (!this._coreService.isCursorInitialized || this._terminal.cursorHidden) { + if (!this._coreService.isCursorInitialized || this._coreService.isCursorHidden) { this._clearCursor(); return; } From 873de2b127fb8f36ecefd5def0137258555bcbd5 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Thu, 7 Nov 2019 18:39:46 -0800 Subject: [PATCH 40/92] Move cursor render layer to browser --- .../src/renderLayer/CursorRenderLayer.ts | 5 +- src/Terminal.ts | 12 +-- src/Types.d.ts | 1 - .../renderer/CursorRenderLayer.ts | 88 +++++++++---------- src/browser/services/CoreBrowserService.ts | 19 ++++ src/browser/services/Services.ts | 7 ++ src/common/services/CoreService.ts | 1 - src/common/services/Services.ts | 1 - src/renderer/Renderer.ts | 31 +++---- 9 files changed, 93 insertions(+), 72 deletions(-) rename src/{ => browser}/renderer/CursorRenderLayer.ts (85%) create mode 100644 src/browser/services/CoreBrowserService.ts diff --git a/addons/xterm-addon-webgl/src/renderLayer/CursorRenderLayer.ts b/addons/xterm-addon-webgl/src/renderLayer/CursorRenderLayer.ts index c7c68ff6..b018cf2b 100644 --- a/addons/xterm-addon-webgl/src/renderLayer/CursorRenderLayer.ts +++ b/addons/xterm-addon-webgl/src/renderLayer/CursorRenderLayer.ts @@ -95,9 +95,8 @@ export class CursorRenderLayer extends BaseRenderLayer { }); } } else { - if (this._cursorBlinkStateManager) { - this._cursorBlinkStateManager.dispose(); - } + this._cursorBlinkStateManager?.dispose(); + this._cursorBlinkStateManager = undefined; } // Request a refresh from the terminal as management of rendering is being // moved back to the terminal diff --git a/src/Terminal.ts b/src/Terminal.ts index e19901b4..f9adacc1 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -48,7 +48,7 @@ import { ColorManager } from 'browser/ColorManager'; import { RenderService } from 'browser/services/RenderService'; import { IOptionsService, IBufferService, ICoreMouseService, ICoreService, ILogService, IDirtyRowService, IInstantiationService } from 'common/services/Services'; import { OptionsService } from 'common/services/OptionsService'; -import { ICharSizeService, IRenderService, IMouseService, ISelectionService, ISoundService } from 'browser/services/Services'; +import { ICharSizeService, IRenderService, IMouseService, ISelectionService, ISoundService, ICoreBrowserService } from 'browser/services/Services'; import { CharSizeService } from 'browser/services/CharSizeService'; import { BufferService, MINIMUM_COLS, MINIMUM_ROWS } from 'common/services/BufferService'; import { Disposable } from 'common/Lifecycle'; @@ -63,6 +63,7 @@ import { DirtyRowService } from 'common/services/DirtyRowService'; import { InstantiationService } from 'common/services/InstantiationService'; import { CoreMouseService } from 'common/services/CoreMouseService'; import { WriteBuffer } from 'common/input/WriteBuffer'; +import { CoreBrowserService } from 'browser/services/CoreBrowserService'; // Let it work inside Node.js for automated testing purposes. const document = (typeof window !== 'undefined') ? window.document : null; @@ -313,10 +314,6 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp } } - public get isFocused(): boolean { - return document.activeElement === this.textarea && document.hasFocus(); - } - private _setupOptionsListeners(): void { // TODO: These listeners should be owned by individual components this.optionsService.onOptionChange(key => { @@ -531,6 +528,9 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp this.register(addDisposableDomListener(this.textarea, 'blur', () => this._onTextAreaBlur())); this._helperContainer.appendChild(this.textarea); + const coreBrowserService = this._instantiationService.createInstance(CoreBrowserService, this.textarea); + this._instantiationService.setService(ICoreBrowserService, coreBrowserService); + this._charSizeService = this._instantiationService.createInstance(CharSizeService, this._document, this._helperContainer); this._instantiationService.setService(ICharSizeService, this._charSizeService); @@ -631,7 +631,7 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp private _createRenderer(): IRenderer { switch (this.options.rendererType) { - case 'canvas': return new Renderer(this._colorManager.colors, this, this._bufferService, this._charSizeService, this.optionsService, this._coreService); + case 'canvas': return new Renderer(this._colorManager.colors, this.screenElement, this.linkifier, this._bufferService, this._charSizeService, this.optionsService, this._coreService, this._coreBrowserService); case 'dom': return this._instantiationService.createInstance(DomRenderer, this._colorManager.colors, this.element, this.screenElement, this._viewportElement, this.linkifier); default: throw new Error(`Unrecognized rendererType "${this.options.rendererType}"`); } diff --git a/src/Types.d.ts b/src/Types.d.ts index 4f6ce40f..2a922ae1 100644 --- a/src/Types.d.ts +++ b/src/Types.d.ts @@ -155,7 +155,6 @@ export interface ITerminal extends IPublicTerminal, IElementAccessor, IBufferAcc browser: IBrowser; buffer: IBuffer; buffers: IBufferSet; - isFocused: boolean; viewport: IViewport; bracketedPasteMode: boolean; optionsService: IOptionsService; diff --git a/src/renderer/CursorRenderLayer.ts b/src/browser/renderer/CursorRenderLayer.ts similarity index 85% rename from src/renderer/CursorRenderLayer.ts rename to src/browser/renderer/CursorRenderLayer.ts index 0bfdd35b..0ca1b97a 100644 --- a/src/renderer/CursorRenderLayer.ts +++ b/src/browser/renderer/CursorRenderLayer.ts @@ -4,13 +4,13 @@ */ import { IRenderDimensions, IRequestRefreshRowsEvent } from 'browser/renderer/Types'; -import { BaseRenderLayer } from '../browser/renderer/BaseRenderLayer'; -import { ITerminal } from '../Types'; +import { BaseRenderLayer } from 'browser/renderer/BaseRenderLayer'; import { ICellData } from 'common/Types'; import { CellData } from 'common/buffer/CellData'; import { IColorSet } from 'browser/Types'; import { IBufferService, IOptionsService, ICoreService } from 'common/services/Services'; import { IEventEmitter } from 'common/EventEmitter'; +import { ICoreBrowserService } from 'browser/services/Services'; interface ICursorState { x: number; @@ -28,27 +28,27 @@ const BLINK_INTERVAL = 600; export class CursorRenderLayer extends BaseRenderLayer { private _state: ICursorState; private _cursorRenderers: {[key: string]: (x: number, y: number, cell: ICellData) => void}; - private _cursorBlinkStateManager: CursorBlinkStateManager; + private _cursorBlinkStateManager: CursorBlinkStateManager | undefined; private _cell: ICellData = new CellData(); constructor( container: HTMLElement, zIndex: number, colors: IColorSet, - private _terminal: ITerminal, rendererId: number, private _onRequestRefreshRowsEvent: IEventEmitter, readonly bufferService: IBufferService, readonly optionsService: IOptionsService, - private readonly _coreService: ICoreService + private readonly _coreService: ICoreService, + private readonly _coreBrowserService: ICoreBrowserService ) { super(container, 'cursor', zIndex, true, colors, rendererId, bufferService, optionsService); this._state = { - x: null, - y: null, - isFocused: null, - style: null, - width: null + x: 0, + y: 0, + isFocused: false, + style: '', + width: 0 }; this._cursorRenderers = { 'bar': this._renderBarCursor.bind(this), @@ -62,11 +62,11 @@ export class CursorRenderLayer extends BaseRenderLayer { super.resize(dim); // Resizing the canvas discards the contents of the canvas so clear state this._state = { - x: null, - y: null, - isFocused: null, - style: null, - width: null + x: 0, + y: 0, + isFocused: false, + style: '', + width: 0 }; } @@ -74,7 +74,7 @@ export class CursorRenderLayer extends BaseRenderLayer { this._clearCursor(); if (this._cursorBlinkStateManager) { this._cursorBlinkStateManager.dispose(); - this._cursorBlinkStateManager = null; + this._cursorBlinkStateManager = undefined; this.onOptionsChanged(); } } @@ -97,15 +97,13 @@ export class CursorRenderLayer extends BaseRenderLayer { public onOptionsChanged(): void { if (this._optionsService.options.cursorBlink) { if (!this._cursorBlinkStateManager) { - this._cursorBlinkStateManager = new CursorBlinkStateManager(this._terminal.isFocused, () => { + this._cursorBlinkStateManager = new CursorBlinkStateManager(this._coreBrowserService.isFocused, () => { this._render(true); }); } } else { - if (this._cursorBlinkStateManager) { - this._cursorBlinkStateManager.dispose(); - this._cursorBlinkStateManager = null; - } + this._cursorBlinkStateManager?.dispose(); + this._cursorBlinkStateManager = undefined; } // Request a refresh from the terminal as management of rendering is being // moved back to the terminal @@ -142,12 +140,12 @@ export class CursorRenderLayer extends BaseRenderLayer { return; } - this._bufferService.buffer.lines.get(cursorY).loadCell(this._bufferService.buffer.x, this._cell); + this._bufferService.buffer.lines.get(cursorY)!.loadCell(this._bufferService.buffer.x, this._cell); if (this._cell.content === undefined) { return; } - if (!this._terminal.isFocused) { + if (!this._coreBrowserService.isFocused) { this._clearCursor(); this._ctx.save(); this._ctx.fillStyle = this._colors.cursor.css; @@ -176,7 +174,7 @@ export class CursorRenderLayer extends BaseRenderLayer { // The cursor is already in the correct spot, don't redraw if (this._state.x === this._bufferService.buffer.x && this._state.y === viewportRelativeCursorY && - this._state.isFocused === this._terminal.isFocused && + this._state.isFocused === this._coreBrowserService.isFocused && this._state.style === this._optionsService.options.cursorStyle && this._state.width === this._cell.getWidth()) { return; @@ -199,11 +197,11 @@ export class CursorRenderLayer extends BaseRenderLayer { if (this._state) { this._clearCells(this._state.x, this._state.y, this._state.width, 1); this._state = { - x: null, - y: null, - isFocused: null, - style: null, - width: null + x: 0, + y: 0, + isFocused: false, + style: '', + width: 0 }; } } @@ -242,16 +240,16 @@ export class CursorRenderLayer extends BaseRenderLayer { class CursorBlinkStateManager { public isCursorVisible: boolean; - private _animationFrame: number; - private _blinkStartTimeout: number; - private _blinkInterval: number; + private _animationFrame: number | undefined; + private _blinkStartTimeout: number | undefined; + private _blinkInterval: number | undefined; /** * The time at which the animation frame was restarted, this is used on the * next render to restart the timers so they don't need to restart the timers * multiple times over a short period. */ - private _animationTimeRestarted: number; + private _animationTimeRestarted: number | undefined; constructor( isFocused: boolean, @@ -268,15 +266,15 @@ class CursorBlinkStateManager { public dispose(): void { if (this._blinkInterval) { window.clearInterval(this._blinkInterval); - this._blinkInterval = null; + this._blinkInterval = undefined; } if (this._blinkStartTimeout) { window.clearTimeout(this._blinkStartTimeout); - this._blinkStartTimeout = null; + this._blinkStartTimeout = undefined; } if (this._animationFrame) { window.cancelAnimationFrame(this._animationFrame); - this._animationFrame = null; + this._animationFrame = undefined; } } @@ -291,7 +289,7 @@ class CursorBlinkStateManager { if (!this._animationFrame) { this._animationFrame = window.requestAnimationFrame(() => { this._renderCallback(); - this._animationFrame = null; + this._animationFrame = undefined; }); } } @@ -311,7 +309,7 @@ class CursorBlinkStateManager { // started if (this._animationTimeRestarted) { const time = BLINK_INTERVAL - (Date.now() - this._animationTimeRestarted); - this._animationTimeRestarted = null; + this._animationTimeRestarted = undefined; if (time > 0) { this._restartInterval(time); return; @@ -322,7 +320,7 @@ class CursorBlinkStateManager { this.isCursorVisible = false; this._animationFrame = window.requestAnimationFrame(() => { this._renderCallback(); - this._animationFrame = null; + this._animationFrame = undefined; }); // Setup the blink interval @@ -332,7 +330,7 @@ class CursorBlinkStateManager { // calc time diff // Make restart interval do a setTimeout initially? const time = BLINK_INTERVAL - (Date.now() - this._animationTimeRestarted); - this._animationTimeRestarted = null; + this._animationTimeRestarted = undefined; this._restartInterval(time); return; } @@ -341,7 +339,7 @@ class CursorBlinkStateManager { this.isCursorVisible = !this.isCursorVisible; this._animationFrame = window.requestAnimationFrame(() => { this._renderCallback(); - this._animationFrame = null; + this._animationFrame = undefined; }); }, BLINK_INTERVAL); }, timeToStart); @@ -351,20 +349,20 @@ class CursorBlinkStateManager { this.isCursorVisible = true; if (this._blinkInterval) { window.clearInterval(this._blinkInterval); - this._blinkInterval = null; + this._blinkInterval = undefined; } if (this._blinkStartTimeout) { window.clearTimeout(this._blinkStartTimeout); - this._blinkStartTimeout = null; + this._blinkStartTimeout = undefined; } if (this._animationFrame) { window.cancelAnimationFrame(this._animationFrame); - this._animationFrame = null; + this._animationFrame = undefined; } } public resume(): void { - this._animationTimeRestarted = null; + this._animationTimeRestarted = undefined; this._restartInterval(); this.restartBlinkAnimation(); } diff --git a/src/browser/services/CoreBrowserService.ts b/src/browser/services/CoreBrowserService.ts new file mode 100644 index 00000000..e585f44a --- /dev/null +++ b/src/browser/services/CoreBrowserService.ts @@ -0,0 +1,19 @@ +/** + * Copyright (c) 2019 The xterm.js authors. All rights reserved. + * @license MIT + */ + +import { ICoreBrowserService } from './Services'; + +export class CoreBrowserService implements ICoreBrowserService { + serviceBrand: any; + + constructor( + private _textarea: HTMLTextAreaElement + ) { + } + + public get isFocused(): boolean { + return document.activeElement === this._textarea && document.hasFocus(); + } +} diff --git a/src/browser/services/Services.ts b/src/browser/services/Services.ts index 2170a04e..785fec34 100644 --- a/src/browser/services/Services.ts +++ b/src/browser/services/Services.ts @@ -23,6 +23,13 @@ export interface ICharSizeService { measure(): void; } +export const ICoreBrowserService = createDecorator('CoreBrowserService'); +export interface ICoreBrowserService { + serviceBrand: any; + + readonly isFocused: boolean; +} + export const IMouseService = createDecorator('MouseService'); export interface IMouseService { serviceBrand: any; diff --git a/src/common/services/CoreService.ts b/src/common/services/CoreService.ts index a235e539..11c3f305 100644 --- a/src/common/services/CoreService.ts +++ b/src/common/services/CoreService.ts @@ -17,7 +17,6 @@ export class CoreService implements ICoreService { public isCursorInitialized: boolean = false; public isCursorHidden: boolean = false; - public isFocused: boolean = false; public decPrivateModes: IDecPrivateModes; private _onData = new EventEmitter(); diff --git a/src/common/services/Services.ts b/src/common/services/Services.ts index c067448d..e4ff90d0 100644 --- a/src/common/services/Services.ts +++ b/src/common/services/Services.ts @@ -64,7 +64,6 @@ export interface ICoreService { */ isCursorInitialized: boolean; isCursorHidden: boolean; - isFocused: boolean; readonly decPrivateModes: IDecPrivateModes; readonly onData: IEvent; diff --git a/src/renderer/Renderer.ts b/src/renderer/Renderer.ts index 28c4ce03..f45fd4b5 100644 --- a/src/renderer/Renderer.ts +++ b/src/renderer/Renderer.ts @@ -3,16 +3,15 @@ * @license MIT */ -import { TextRenderLayer } from '../browser/renderer/TextRenderLayer'; -import { SelectionRenderLayer } from '../browser/renderer/SelectionRenderLayer'; -import { CursorRenderLayer } from './CursorRenderLayer'; +import { TextRenderLayer } from 'browser/renderer/TextRenderLayer'; +import { SelectionRenderLayer } from 'browser/renderer/SelectionRenderLayer'; +import { CursorRenderLayer } from 'browser/renderer/CursorRenderLayer'; import { IRenderLayer, IRenderer, IRenderDimensions, CharacterJoinerHandler, ICharacterJoinerRegistry, IRequestRefreshRowsEvent } from 'browser/renderer/Types'; -import { ITerminal } from '../Types'; -import { LinkRenderLayer } from '../browser/renderer/LinkRenderLayer'; +import { LinkRenderLayer } from 'browser/renderer/LinkRenderLayer'; import { CharacterJoinerRegistry } from 'browser/renderer/CharacterJoinerRegistry'; import { Disposable } from 'common/Lifecycle'; -import { IColorSet } from 'browser/Types'; -import { ICharSizeService } from 'browser/services/Services'; +import { IColorSet, ILinkifier } from 'browser/Types'; +import { ICharSizeService, ICoreBrowserService } from 'browser/services/Services'; import { IBufferService, IOptionsService, ICoreService } from 'common/services/Services'; import { removeTerminalFromCache } from 'browser/renderer/atlas/CharAtlasCache'; import { EventEmitter, IEvent } from 'common/EventEmitter'; @@ -33,21 +32,23 @@ export class Renderer extends Disposable implements IRenderer { constructor( private _colors: IColorSet, - private readonly _terminal: ITerminal, + private readonly _screenElement: HTMLElement, + private readonly _linkifier: ILinkifier, private readonly _bufferService: IBufferService, private readonly _charSizeService: ICharSizeService, private readonly _optionsService: IOptionsService, - readonly coreService: ICoreService + readonly coreService: ICoreService, + readonly coreBrowserService: ICoreBrowserService ) { super(); const allowTransparency = this._optionsService.options.allowTransparency; this._characterJoinerRegistry = new CharacterJoinerRegistry(this._bufferService); this._renderLayers = [ - new TextRenderLayer(this._terminal.screenElement, 0, this._colors, this._characterJoinerRegistry, allowTransparency, this._id, this._bufferService, _optionsService), - new SelectionRenderLayer(this._terminal.screenElement, 1, this._colors, this._id, this._bufferService, _optionsService), - new LinkRenderLayer(this._terminal.screenElement, 2, this._colors, this._id, this._terminal.linkifier, this._bufferService, _optionsService), - new CursorRenderLayer(this._terminal.screenElement, 3, this._colors, this._terminal, this._id, this._onRequestRefreshRows, this._bufferService, _optionsService, coreService) + new TextRenderLayer(this._screenElement, 0, this._colors, this._characterJoinerRegistry, allowTransparency, this._id, this._bufferService, _optionsService), + new SelectionRenderLayer(this._screenElement, 1, this._colors, this._id, this._bufferService, _optionsService), + new LinkRenderLayer(this._screenElement, 2, this._colors, this._id, this._linkifier, this._bufferService, _optionsService), + new CursorRenderLayer(this._screenElement, 3, this._colors, this._id, this._onRequestRefreshRows, this._bufferService, _optionsService, coreService, coreBrowserService) ]; this.dimensions = { scaledCharWidth: null, @@ -101,8 +102,8 @@ export class Renderer extends Disposable implements IRenderer { this._renderLayers.forEach(l => l.resize(this.dimensions)); // Resize the screen - this._terminal.screenElement.style.width = `${this.dimensions.canvasWidth}px`; - this._terminal.screenElement.style.height = `${this.dimensions.canvasHeight}px`; + this._screenElement.style.width = `${this.dimensions.canvasWidth}px`; + this._screenElement.style.height = `${this.dimensions.canvasHeight}px`; } public onCharSizeChanged(): void { From 3f7238d9dca3e20794a212ec13ebdedcde3aea4e Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Thu, 7 Nov 2019 18:45:13 -0800 Subject: [PATCH 41/92] Move Renderer to browser --- src/Terminal.ts | 4 +-- src/{ => browser}/renderer/Renderer.ts | 34 +++++++++++++------------- 2 files changed, 19 insertions(+), 19 deletions(-) rename src/{ => browser}/renderer/Renderer.ts (92%) diff --git a/src/Terminal.ts b/src/Terminal.ts index f9adacc1..3c589ffa 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -28,7 +28,7 @@ import { Viewport } from 'browser/Viewport'; import { rightClickHandler, moveTextAreaUnderMouseCursor, handlePasteEvent, copyHandler, paste } from 'browser/Clipboard'; import { C0 } from 'common/data/EscapeSequences'; import { InputHandler } from './InputHandler'; -import { Renderer } from './renderer/Renderer'; +import { Renderer } from 'browser/renderer/Renderer'; import { Linkifier } from 'browser/Linkifier'; import { SelectionService } from 'browser/services/SelectionService'; import * as Browser from 'common/Platform'; @@ -631,7 +631,7 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp private _createRenderer(): IRenderer { switch (this.options.rendererType) { - case 'canvas': return new Renderer(this._colorManager.colors, this.screenElement, this.linkifier, this._bufferService, this._charSizeService, this.optionsService, this._coreService, this._coreBrowserService); + case 'canvas': return this._instantiationService.createInstance(Renderer, this._colorManager.colors, this.screenElement, this.linkifier); case 'dom': return this._instantiationService.createInstance(DomRenderer, this._colorManager.colors, this.element, this.screenElement, this._viewportElement, this.linkifier); default: throw new Error(`Unrecognized rendererType "${this.options.rendererType}"`); } diff --git a/src/renderer/Renderer.ts b/src/browser/renderer/Renderer.ts similarity index 92% rename from src/renderer/Renderer.ts rename to src/browser/renderer/Renderer.ts index f45fd4b5..26b464cb 100644 --- a/src/renderer/Renderer.ts +++ b/src/browser/renderer/Renderer.ts @@ -34,11 +34,11 @@ export class Renderer extends Disposable implements IRenderer { private _colors: IColorSet, private readonly _screenElement: HTMLElement, private readonly _linkifier: ILinkifier, - private readonly _bufferService: IBufferService, - private readonly _charSizeService: ICharSizeService, - private readonly _optionsService: IOptionsService, - readonly coreService: ICoreService, - readonly coreBrowserService: ICoreBrowserService + @IBufferService private readonly _bufferService: IBufferService, + @ICharSizeService private readonly _charSizeService: ICharSizeService, + @IOptionsService private readonly _optionsService: IOptionsService, + @ICoreService readonly coreService: ICoreService, + @ICoreBrowserService readonly coreBrowserService: ICoreBrowserService ) { super(); const allowTransparency = this._optionsService.options.allowTransparency; @@ -51,18 +51,18 @@ export class Renderer extends Disposable implements IRenderer { new CursorRenderLayer(this._screenElement, 3, this._colors, this._id, this._onRequestRefreshRows, this._bufferService, _optionsService, coreService, coreBrowserService) ]; this.dimensions = { - scaledCharWidth: null, - scaledCharHeight: null, - scaledCellWidth: null, - scaledCellHeight: null, - scaledCharLeft: null, - scaledCharTop: null, - scaledCanvasWidth: null, - scaledCanvasHeight: null, - canvasWidth: null, - canvasHeight: null, - actualCellWidth: null, - actualCellHeight: null + scaledCharWidth: 0, + scaledCharHeight: 0, + scaledCellWidth: 0, + scaledCellHeight: 0, + scaledCharLeft: 0, + scaledCharTop: 0, + scaledCanvasWidth: 0, + scaledCanvasHeight: 0, + canvasWidth: 0, + canvasHeight: 0, + actualCellWidth: 0, + actualCellHeight: 0 }; this._devicePixelRatio = window.devicePixelRatio; this._updateDimensions(); From 3bf149bbc569ecd9ef2825366bf30e58e70efe11 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Thu, 7 Nov 2019 18:46:56 -0800 Subject: [PATCH 42/92] Fix webgl cursor --- addons/xterm-addon-webgl/src/renderLayer/CursorRenderLayer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/xterm-addon-webgl/src/renderLayer/CursorRenderLayer.ts b/addons/xterm-addon-webgl/src/renderLayer/CursorRenderLayer.ts index b018cf2b..413f4fdd 100644 --- a/addons/xterm-addon-webgl/src/renderLayer/CursorRenderLayer.ts +++ b/addons/xterm-addon-webgl/src/renderLayer/CursorRenderLayer.ts @@ -120,7 +120,7 @@ export class CursorRenderLayer extends BaseRenderLayer { private _render(terminal: Terminal, triggeredByAnimationFrame: boolean): void { // Don't draw the cursor if it's hidden // TODO: Need to expose API for this - if (!(terminal as any)._core.cursorState || (terminal as any)._core.cursorHidden) { + if (!(terminal as any)._core._coreService.isCursorInitialized || (terminal as any)._core._coreService.isCursorHidden) { this._clearCursor(); return; } From bcd4c766d61bbaf4d275d7faa1994d72923acdf5 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sat, 9 Nov 2019 09:09:59 -0800 Subject: [PATCH 43/92] Implement webgl renderer dispose Fixes #2254 --- addons/xterm-addon-webgl/src/WebglAddon.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/addons/xterm-addon-webgl/src/WebglAddon.ts b/addons/xterm-addon-webgl/src/WebglAddon.ts index 0074f9b2..be867d75 100644 --- a/addons/xterm-addon-webgl/src/WebglAddon.ts +++ b/addons/xterm-addon-webgl/src/WebglAddon.ts @@ -9,20 +9,28 @@ import { IRenderService } from 'browser/services/Services'; import { IColorSet } from 'browser/Types'; export class WebglAddon implements ITerminalAddon { + private _terminal?: Terminal; + constructor( private _preserveDrawingBuffer?: boolean ) {} public activate(terminal: Terminal): void { if (!terminal.element) { - throw new Error('Cannot activate WebglRendererAddon before Terminal.open'); + throw new Error('Cannot activate WebglAddon before Terminal.open'); } + this._terminal = terminal; const renderService: IRenderService = (terminal)._core._renderService; const colors: IColorSet = (terminal)._core._colorManager.colors; renderService.setRenderer(new WebglRenderer(terminal, colors, this._preserveDrawingBuffer)); } public dispose(): void { - throw new Error('WebglRendererAddon.dispose Not yet implemented'); + if (!this._terminal) { + throw new Error('Cannot dispose WebglAddon because it is activated'); + } + const renderService: IRenderService = (this._terminal)._core._renderService; + renderService.setRenderer((this._terminal)._core._createRenderer()); + renderService.onResize(this._terminal.cols, this._terminal.rows); } } From 2aec4e6d29c1b1fd8ed4c2004ceac74b2a1a8634 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sat, 9 Nov 2019 09:27:30 -0800 Subject: [PATCH 44/92] Add a test for removing canvases --- .../src/WebglRenderer.api.ts | 54 ++++++++++++------- 1 file changed, 34 insertions(+), 20 deletions(-) diff --git a/addons/xterm-addon-webgl/src/WebglRenderer.api.ts b/addons/xterm-addon-webgl/src/WebglRenderer.api.ts index 9303782d..4debb5f9 100644 --- a/addons/xterm-addon-webgl/src/WebglRenderer.api.ts +++ b/addons/xterm-addon-webgl/src/WebglRenderer.api.ts @@ -18,29 +18,25 @@ const height = 600; describe('WebGL Renderer Integration Tests', function(): void { this.timeout(20000); - before(async function(): Promise { - browser = await puppeteer.launch({ - headless: process.argv.indexOf('--headless') !== -1, - slowMo: 80, - args: [`--window-size=${width},${height}`, `--no-sandbox`] - }); - page = (await browser.pages())[0]; - await page.setViewport({ width, height }); - await page.goto(APP); - await openTerminal(); - await page.evaluate(`window.term.loadAddon(new WebglAddon(true));`); - }); - after(() => { browser.close(); }); - beforeEach(async () => { - await page.evaluate(`window.term.reset()`); + it('dispose removes renderer canvases', async () => { + await setupBrowser(); + assert.equal(await page.evaluate(`document.querySelectorAll('.xterm canvas').length`), 3); + await page.evaluate(`addon.dispose()`); + assert.equal(await page.evaluate(`document.querySelectorAll('.xterm canvas').length`), 0); }); - describe('WebGL Renderer', () => { - it('foreground colors normal', async function(): Promise { + describe('colors', () => { + before(async () => setupBrowser()); + + beforeEach(async () => { + await page.evaluate(`window.term.reset()`); + }); + + it('foreground colors normal', async () => { const theme: ITheme = { black: '#010203', red: '#040506', @@ -63,7 +59,7 @@ describe('WebGL Renderer Integration Tests', function(): void { assert.deepEqual(await getCellColor(8, 1), [22, 23, 24, 255]); }); - it('foreground colors bright', async function(): Promise { + it('foreground colors bright', async () => { const theme: ITheme = { brightBlack: '#010203', brightRed: '#040506', @@ -86,7 +82,7 @@ describe('WebGL Renderer Integration Tests', function(): void { assert.deepEqual(await getCellColor(8, 1), [22, 23, 24, 255]); }); - it('background colors normal', async function(): Promise { + it('background colors normal', async () => { const theme: ITheme = { black: '#010203', red: '#040506', @@ -109,7 +105,7 @@ describe('WebGL Renderer Integration Tests', function(): void { assert.deepEqual(await getCellColor(8, 1), [22, 23, 24, 255]); }); - it('background colors bright', async function(): Promise { + it('background colors bright', async () => { const theme: ITheme = { brightBlack: '#010203', brightRed: '#040506', @@ -161,3 +157,21 @@ async function getCellColor(col: number, row: number): Promise { `); return await page.evaluate(`Array.from(window.result)`); } + +async function setupBrowser(): Promise { + browser = await puppeteer.launch({ + headless: process.argv.indexOf('--headless') !== -1, + slowMo: 80, + args: [`--window-size=${width},${height}`, `--no-sandbox`] + }); + page = (await browser.pages())[0]; + await page.setViewport({ width, height }); + await page.goto(APP); + await openTerminal({ + rendererType: 'dom' + }); + await page.evaluate(` + window.addon = new WebglAddon(true); + window.term.loadAddon(window.addon); + `); +} From d09ac41f75dbedaf104aabae5f16a6849c4a2c1a Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sat, 9 Nov 2019 09:29:04 -0800 Subject: [PATCH 45/92] After tests wait for browser close --- addons/xterm-addon-webgl/src/WebglRenderer.api.ts | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/addons/xterm-addon-webgl/src/WebglRenderer.api.ts b/addons/xterm-addon-webgl/src/WebglRenderer.api.ts index 4debb5f9..be895743 100644 --- a/addons/xterm-addon-webgl/src/WebglRenderer.api.ts +++ b/addons/xterm-addon-webgl/src/WebglRenderer.api.ts @@ -18,9 +18,7 @@ const height = 600; describe('WebGL Renderer Integration Tests', function(): void { this.timeout(20000); - after(() => { - browser.close(); - }); + after(async () => browser.close()); it('dispose removes renderer canvases', async () => { await setupBrowser(); @@ -31,10 +29,7 @@ describe('WebGL Renderer Integration Tests', function(): void { describe('colors', () => { before(async () => setupBrowser()); - - beforeEach(async () => { - await page.evaluate(`window.term.reset()`); - }); + beforeEach(async () => page.evaluate(`window.term.reset()`)); it('foreground colors normal', async () => { const theme: ITheme = { From 8adbed84b07a8f278e082f4cc9888beaf92e0f06 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sat, 9 Nov 2019 09:34:40 -0800 Subject: [PATCH 46/92] webgl: Refresh rows on options change Fixes #2549 --- addons/xterm-addon-webgl/src/WebglRenderer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/xterm-addon-webgl/src/WebglRenderer.ts b/addons/xterm-addon-webgl/src/WebglRenderer.ts index 1f28d589..a32b03e0 100644 --- a/addons/xterm-addon-webgl/src/WebglRenderer.ts +++ b/addons/xterm-addon-webgl/src/WebglRenderer.ts @@ -189,7 +189,6 @@ export class WebglRenderer extends Disposable implements IRenderer { this._rectangleRenderer.updateSelection(this._model.selection, columnSelectMode); this._glyphRenderer.updateSelection(this._model, columnSelectMode); - // TODO: #2102 Should this move to RenderCoordinator? this._onRequestRefreshRows.fire({ start: 0, end: this._terminal.rows - 1 }); } @@ -201,6 +200,7 @@ export class WebglRenderer extends Disposable implements IRenderer { this._renderLayers.forEach(l => l.onOptionsChanged(this._terminal)); this._updateDimensions(); this._refreshCharAtlas(); + this._onRequestRefreshRows.fire({ start: 0, end: this._terminal.rows - 1 }); } /** From 22f4342194b0576bedc22f370c568992d906ea0e Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sat, 9 Nov 2019 10:07:24 -0800 Subject: [PATCH 47/92] Move options row refresh into render service --- addons/xterm-addon-webgl/src/WebglRenderer.ts | 1 - src/browser/renderer/dom/DomRenderer.ts | 1 - src/browser/services/RenderService.ts | 1 + 3 files changed, 1 insertion(+), 2 deletions(-) diff --git a/addons/xterm-addon-webgl/src/WebglRenderer.ts b/addons/xterm-addon-webgl/src/WebglRenderer.ts index a32b03e0..ab2cb354 100644 --- a/addons/xterm-addon-webgl/src/WebglRenderer.ts +++ b/addons/xterm-addon-webgl/src/WebglRenderer.ts @@ -200,7 +200,6 @@ export class WebglRenderer extends Disposable implements IRenderer { this._renderLayers.forEach(l => l.onOptionsChanged(this._terminal)); this._updateDimensions(); this._refreshCharAtlas(); - this._onRequestRefreshRows.fire({ start: 0, end: this._terminal.rows - 1 }); } /** diff --git a/src/browser/renderer/dom/DomRenderer.ts b/src/browser/renderer/dom/DomRenderer.ts index 3d544a90..e8fc85f6 100644 --- a/src/browser/renderer/dom/DomRenderer.ts +++ b/src/browser/renderer/dom/DomRenderer.ts @@ -340,7 +340,6 @@ export class DomRenderer extends Disposable implements IRenderer { // Force a refresh this._updateDimensions(); this._injectCss(); - this._onRequestRefreshRows.fire({ start: 0, end: this._bufferService.rows - 1 }); } public clear(): void { diff --git a/src/browser/services/RenderService.ts b/src/browser/services/RenderService.ts index b28c9971..2949c2c5 100644 --- a/src/browser/services/RenderService.ts +++ b/src/browser/services/RenderService.ts @@ -95,6 +95,7 @@ export class RenderService extends Disposable implements IRenderService { public changeOptions(): void { this._renderer.onOptionsChanged(); + this.refreshRows(0, this._rowCount - 1); this._fireOnCanvasResize(); } From 60bda1bbd3fb0df867686426a29c6ae3c57f7d00 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sat, 9 Nov 2019 11:04:55 -0800 Subject: [PATCH 48/92] Fix tests hanging --- addons/xterm-addon-webgl/src/WebglRenderer.api.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/xterm-addon-webgl/src/WebglRenderer.api.ts b/addons/xterm-addon-webgl/src/WebglRenderer.api.ts index be895743..73e83bb2 100644 --- a/addons/xterm-addon-webgl/src/WebglRenderer.api.ts +++ b/addons/xterm-addon-webgl/src/WebglRenderer.api.ts @@ -18,17 +18,17 @@ const height = 600; describe('WebGL Renderer Integration Tests', function(): void { this.timeout(20000); - after(async () => browser.close()); - it('dispose removes renderer canvases', async () => { await setupBrowser(); assert.equal(await page.evaluate(`document.querySelectorAll('.xterm canvas').length`), 3); await page.evaluate(`addon.dispose()`); assert.equal(await page.evaluate(`document.querySelectorAll('.xterm canvas').length`), 0); + await browser.close(); }); describe('colors', () => { before(async () => setupBrowser()); + after(async () => browser.close()); beforeEach(async () => page.evaluate(`window.term.reset()`)); it('foreground colors normal', async () => { From b8acdd6e673e0cbec386d5f969e45238a668621f Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sat, 9 Nov 2019 11:15:15 -0800 Subject: [PATCH 49/92] Remove slowmo from puppeteer --- addons/xterm-addon-attach/src/AttachAddon.api.ts | 2 +- addons/xterm-addon-fit/src/FitAddon.api.ts | 2 +- addons/xterm-addon-search/src/SearchAddon.api.ts | 2 +- addons/xterm-addon-web-links/src/WebLinksAddon.api.ts | 2 +- addons/xterm-addon-webgl/src/WebglRenderer.api.ts | 2 +- test/api/CharWidth.api.ts | 2 +- test/api/InputHandler.api.ts | 2 +- test/api/MouseTracking.api.ts | 2 +- test/api/Parser.api.ts | 2 +- test/api/Terminal.api.ts | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/addons/xterm-addon-attach/src/AttachAddon.api.ts b/addons/xterm-addon-attach/src/AttachAddon.api.ts index 94d11139..f027e25e 100644 --- a/addons/xterm-addon-attach/src/AttachAddon.api.ts +++ b/addons/xterm-addon-attach/src/AttachAddon.api.ts @@ -20,7 +20,7 @@ describe('AttachAddon', () => { this.timeout(20000); browser = await puppeteer.launch({ headless: process.argv.indexOf('--headless') !== -1, - slowMo: 80, + slowMo: 0, args: [`--window-size=${width},${height}`, `--no-sandbox`] }); page = (await browser.pages())[0]; diff --git a/addons/xterm-addon-fit/src/FitAddon.api.ts b/addons/xterm-addon-fit/src/FitAddon.api.ts index 4f955868..3115f2ed 100644 --- a/addons/xterm-addon-fit/src/FitAddon.api.ts +++ b/addons/xterm-addon-fit/src/FitAddon.api.ts @@ -19,7 +19,7 @@ describe('FitAddon', () => { this.timeout(20000); browser = await puppeteer.launch({ headless: process.argv.indexOf('--headless') !== -1, - slowMo: 80, + slowMo: 0, args: [`--window-size=${width},${height}`, `--no-sandbox`] }); page = (await browser.pages())[0]; diff --git a/addons/xterm-addon-search/src/SearchAddon.api.ts b/addons/xterm-addon-search/src/SearchAddon.api.ts index ff195f5e..96c62a7d 100644 --- a/addons/xterm-addon-search/src/SearchAddon.api.ts +++ b/addons/xterm-addon-search/src/SearchAddon.api.ts @@ -22,7 +22,7 @@ describe('Search Tests', function (): void { before(async function (): Promise { browser = await puppeteer.launch({ headless: process.argv.indexOf('--headless') !== -1, - slowMo: 80, + slowMo: 0, args: [`--window-size=${width},${height}`, `--no-sandbox`] }); page = (await browser.pages())[0]; diff --git a/addons/xterm-addon-web-links/src/WebLinksAddon.api.ts b/addons/xterm-addon-web-links/src/WebLinksAddon.api.ts index 3fb1a536..2a3fd199 100644 --- a/addons/xterm-addon-web-links/src/WebLinksAddon.api.ts +++ b/addons/xterm-addon-web-links/src/WebLinksAddon.api.ts @@ -19,7 +19,7 @@ describe('WebLinksAddon', () => { this.timeout(10000); browser = await puppeteer.launch({ headless: process.argv.indexOf('--headless') !== -1, - slowMo: 80, + slowMo: 0, args: [`--window-size=${width},${height}`, `--no-sandbox`] }); page = (await browser.pages())[0]; diff --git a/addons/xterm-addon-webgl/src/WebglRenderer.api.ts b/addons/xterm-addon-webgl/src/WebglRenderer.api.ts index 9303782d..a7079df1 100644 --- a/addons/xterm-addon-webgl/src/WebglRenderer.api.ts +++ b/addons/xterm-addon-webgl/src/WebglRenderer.api.ts @@ -21,7 +21,7 @@ describe('WebGL Renderer Integration Tests', function(): void { before(async function(): Promise { browser = await puppeteer.launch({ headless: process.argv.indexOf('--headless') !== -1, - slowMo: 80, + slowMo: 0, args: [`--window-size=${width},${height}`, `--no-sandbox`] }); page = (await browser.pages())[0]; diff --git a/test/api/CharWidth.api.ts b/test/api/CharWidth.api.ts index 0561bf07..a20ecc8b 100644 --- a/test/api/CharWidth.api.ts +++ b/test/api/CharWidth.api.ts @@ -20,7 +20,7 @@ describe('CharWidth Integration Tests', function(): void { before(async function(): Promise { browser = await puppeteer.launch({ headless: process.argv.indexOf('--headless') !== -1, - slowMo: 80, + slowMo: 0, args: [`--window-size=${width},${height}`, `--no-sandbox`] }); page = (await browser.pages())[0]; diff --git a/test/api/InputHandler.api.ts b/test/api/InputHandler.api.ts index fe068610..2070dfe6 100644 --- a/test/api/InputHandler.api.ts +++ b/test/api/InputHandler.api.ts @@ -20,7 +20,7 @@ describe('InputHandler Integration Tests', function(): void { before(async function(): Promise { browser = await puppeteer.launch({ headless: process.argv.indexOf('--headless') !== -1, - slowMo: 80, + slowMo: 0, args: [`--window-size=${width},${height}`, `--no-sandbox`] }); page = (await browser.pages())[0]; diff --git a/test/api/MouseTracking.api.ts b/test/api/MouseTracking.api.ts index 37d46f74..a993c6a1 100644 --- a/test/api/MouseTracking.api.ts +++ b/test/api/MouseTracking.api.ts @@ -204,7 +204,7 @@ describe('Mouse Tracking Tests', function(): void { before(async function(): Promise { browser = await puppeteer.launch({ headless: process.argv.indexOf('--headless') !== -1, - slowMo: 80, + slowMo: 0, args: [`--window-size=${width},${height}`, `--no-sandbox`] }); page = (await browser.pages())[0]; diff --git a/test/api/Parser.api.ts b/test/api/Parser.api.ts index e46242ab..4dcda70c 100644 --- a/test/api/Parser.api.ts +++ b/test/api/Parser.api.ts @@ -20,7 +20,7 @@ describe('Parser Integration Tests', function(): void { before(async function(): Promise { browser = await puppeteer.launch({ headless: process.argv.indexOf('--headless') !== -1, - slowMo: 80, + slowMo: 0, args: [`--window-size=${width},${height}`, `--no-sandbox`] }); page = (await browser.pages())[0]; diff --git a/test/api/Terminal.api.ts b/test/api/Terminal.api.ts index 08963d9c..07dc372c 100644 --- a/test/api/Terminal.api.ts +++ b/test/api/Terminal.api.ts @@ -20,7 +20,7 @@ describe('API Integration Tests', function(): void { before(async function(): Promise { browser = await puppeteer.launch({ headless: process.argv.indexOf('--headless') !== -1, - slowMo: 80, + slowMo: 0, args: [`--window-size=${width},${height}`, `--no-sandbox`] }); page = (await browser.pages())[0]; From 840e56cf1e0e53cab83b011f4455f1e62030e134 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sat, 9 Nov 2019 11:30:45 -0800 Subject: [PATCH 50/92] Fix Attach tests --- addons/xterm-addon-attach/src/AttachAddon.api.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/addons/xterm-addon-attach/src/AttachAddon.api.ts b/addons/xterm-addon-attach/src/AttachAddon.api.ts index f027e25e..627647cd 100644 --- a/addons/xterm-addon-attach/src/AttachAddon.api.ts +++ b/addons/xterm-addon-attach/src/AttachAddon.api.ts @@ -20,7 +20,6 @@ describe('AttachAddon', () => { this.timeout(20000); browser = await puppeteer.launch({ headless: process.argv.indexOf('--headless') !== -1, - slowMo: 0, args: [`--window-size=${width},${height}`, `--no-sandbox`] }); page = (await browser.pages())[0]; @@ -43,6 +42,7 @@ describe('AttachAddon', () => { const server = new WebSocket.Server({ port }); server.on('connection', socket => socket.send('foo')); await page.evaluate(`window.term.loadAddon(new window.AttachAddon(new WebSocket('ws://localhost:${port}')))`); + await pollFor(page, `window.term.buffer.getLine(0).translateToString(true)`, 'foo'); assert.equal(await page.evaluate(`window.term.buffer.getLine(0).translateToString(true)`), 'foo'); server.close(); }); @@ -55,6 +55,7 @@ describe('AttachAddon', () => { const data = new Uint8Array([102, 111, 111]); server.on('connection', socket => socket.send(data)); await page.evaluate(`window.term.loadAddon(new window.AttachAddon(new WebSocket('ws://localhost:${port}')))`); + await pollFor(page, `window.term.buffer.getLine(0).translateToString(true)`, 'foo'); assert.equal(await page.evaluate(`window.term.buffer.getLine(0).translateToString(true)`), 'foo'); server.close(); }); @@ -69,3 +70,12 @@ async function openTerminal(options: ITerminalOptions = {}): Promise { await page.waitForSelector('.xterm-text-layer'); } } + +async function pollFor(page: puppeteer.Page, fn: string, val: any): Promise { + const result = await page.evaluate(fn); + if (result !== val) { + return new Promise(r => { + setTimeout(() => r(pollFor(page, fn, val)), 10); + }); + } +} From d3bf013ceb1f31d81842b5abc9e49181f33f753a Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sat, 9 Nov 2019 13:57:42 -0800 Subject: [PATCH 51/92] Fix timing issues in web links addon tests --- .../xterm-addon-attach/src/AttachAddon.api.ts | 2 - .../src/WebLinksAddon.api.ts | 38 +++++++++++++------ 2 files changed, 26 insertions(+), 14 deletions(-) diff --git a/addons/xterm-addon-attach/src/AttachAddon.api.ts b/addons/xterm-addon-attach/src/AttachAddon.api.ts index 627647cd..264b32ee 100644 --- a/addons/xterm-addon-attach/src/AttachAddon.api.ts +++ b/addons/xterm-addon-attach/src/AttachAddon.api.ts @@ -43,7 +43,6 @@ describe('AttachAddon', () => { server.on('connection', socket => socket.send('foo')); await page.evaluate(`window.term.loadAddon(new window.AttachAddon(new WebSocket('ws://localhost:${port}')))`); await pollFor(page, `window.term.buffer.getLine(0).translateToString(true)`, 'foo'); - assert.equal(await page.evaluate(`window.term.buffer.getLine(0).translateToString(true)`), 'foo'); server.close(); }); @@ -56,7 +55,6 @@ describe('AttachAddon', () => { server.on('connection', socket => socket.send(data)); await page.evaluate(`window.term.loadAddon(new window.AttachAddon(new WebSocket('ws://localhost:${port}')))`); await pollFor(page, `window.term.buffer.getLine(0).translateToString(true)`, 'foo'); - assert.equal(await page.evaluate(`window.term.buffer.getLine(0).translateToString(true)`), 'foo'); server.close(); }); }); diff --git a/addons/xterm-addon-web-links/src/WebLinksAddon.api.ts b/addons/xterm-addon-web-links/src/WebLinksAddon.api.ts index 2a3fd199..49ec4fee 100644 --- a/addons/xterm-addon-web-links/src/WebLinksAddon.api.ts +++ b/addons/xterm-addon-web-links/src/WebLinksAddon.api.ts @@ -19,7 +19,6 @@ describe('WebLinksAddon', () => { this.timeout(10000); browser = await puppeteer.launch({ headless: process.argv.indexOf('--headless') !== -1, - slowMo: 0, args: [`--window-size=${width},${height}`, `--no-sandbox`] }); page = (await browser.pages())[0]; @@ -54,22 +53,25 @@ describe('WebLinksAddon', () => { async function testHostName(hostname: string): Promise { await openTerminal({ rendererType: 'dom' }); await page.evaluate(`window.term.loadAddon(new window.WebLinksAddon())`); + await new Promise(r => setTimeout(r, 100)); await page.evaluate(` + window.ready = false; window.term.writeln(' http://${hostname} '); window.term.writeln(' http://${hostname}/a~b#c~d?e~f '); window.term.writeln(' http://${hostname}/colon:test '); window.term.writeln(' http://${hostname}/colon:test: '); window.term.writeln('"http://${hostname}/"'); window.term.writeln('\\'http://${hostname}/\\''); - window.term.writeln('http://${hostname}/subpath/+/id'); + window.term.writeln('http://${hostname}/subpath/+/id', () => window.ready = true); `); - assert.equal(await getLinkAtCell(3, 1), `http://${hostname}`); - assert.equal(await getLinkAtCell(3, 2), `http://${hostname}/a~b#c~d?e~f`); - assert.equal(await getLinkAtCell(3, 3), `http://${hostname}/colon:test`); - assert.equal(await getLinkAtCell(3, 4), `http://${hostname}/colon:test`); - assert.equal(await getLinkAtCell(2, 5), `http://${hostname}/`); - assert.equal(await getLinkAtCell(2, 6), `http://${hostname}/`); - assert.equal(await getLinkAtCell(1, 7), `http://${hostname}/subpath/+/id`); + await pollFor(page, 'window.ready', true); + await pollForLinkAtCell(3, 1, `http://${hostname}`); + await pollForLinkAtCell(3, 2, `http://${hostname}/a~b#c~d?e~f`); + await pollForLinkAtCell(3, 3, `http://${hostname}/colon:test`); + await pollForLinkAtCell(3, 4, `http://${hostname}/colon:test`); + await pollForLinkAtCell(2, 5, `http://${hostname}/`); + await pollForLinkAtCell(2, 6, `http://${hostname}/`); + await pollForLinkAtCell(1, 7, `http://${hostname}/subpath/+/id`); } async function openTerminal(options: ITerminalOptions = {}): Promise { @@ -82,8 +84,20 @@ async function openTerminal(options: ITerminalOptions = {}): Promise { } } -async function getLinkAtCell(col: number, row: number): Promise { +async function pollForLinkAtCell(col: number, row: number, value: string): Promise { const rowSelector = `.xterm-rows > :nth-child(${row})`; - await page.hover(`${rowSelector} > :nth-child(${col})`); - return await page.evaluate(`Array.prototype.reduce.call(document.querySelectorAll('${rowSelector} > span[style]'), (a, b) => a + b.textContent, '');`); + await pollFor(page, `document.querySelectorAll('${rowSelector} > span[style]').length > 0`, true, async () => page.hover(`${rowSelector} > :nth-child(${col})`)); + assert.equal(await page.evaluate(`Array.prototype.reduce.call(document.querySelectorAll('${rowSelector} > span[style]'), (a, b) => a + b.textContent, '');`), value); +} + +async function pollFor(page: puppeteer.Page, fn: string, val: any, preFn?: () => Promise): Promise { + if (preFn) { + await preFn(); + } + const result = await page.evaluate(fn); + if (result !== val) { + return new Promise(r => { + setTimeout(() => r(pollFor(page, fn, val, preFn)), 10); + }); + } } From b0c0f8701e8037485e3f777545d5b17e3bbee3c9 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sat, 9 Nov 2019 14:08:37 -0800 Subject: [PATCH 52/92] Fix timing issues in webglrenderer --- .../src/WebglRenderer.api.ts | 87 +++++++++++-------- 1 file changed, 51 insertions(+), 36 deletions(-) diff --git a/addons/xterm-addon-webgl/src/WebglRenderer.api.ts b/addons/xterm-addon-webgl/src/WebglRenderer.api.ts index a7079df1..18d80276 100644 --- a/addons/xterm-addon-webgl/src/WebglRenderer.api.ts +++ b/addons/xterm-addon-webgl/src/WebglRenderer.api.ts @@ -15,13 +15,10 @@ let page: puppeteer.Page; const width = 800; const height = 600; -describe('WebGL Renderer Integration Tests', function(): void { - this.timeout(20000); - +describe.only('WebGL Renderer Integration Tests', function(): void { before(async function(): Promise { browser = await puppeteer.launch({ headless: process.argv.indexOf('--headless') !== -1, - slowMo: 0, args: [`--window-size=${width},${height}`, `--no-sandbox`] }); page = (await browser.pages())[0]; @@ -53,14 +50,14 @@ describe('WebGL Renderer Integration Tests', function(): void { }; await page.evaluate(`window.term.setOption('theme', ${JSON.stringify(theme)});`); await writeSync(`\\x1b[30m█\\x1b[31m█\\x1b[32m█\\x1b[33m█\\x1b[34m█\\x1b[35m█\\x1b[36m█\\x1b[37m█`); - assert.deepEqual(await getCellColor(1, 1), [1, 2, 3, 255]); - assert.deepEqual(await getCellColor(2, 1), [4, 5, 6, 255]); - assert.deepEqual(await getCellColor(3, 1), [7, 8, 9, 255]); - assert.deepEqual(await getCellColor(4, 1), [10, 11, 12, 255]); - assert.deepEqual(await getCellColor(5, 1), [13, 14, 15, 255]); - assert.deepEqual(await getCellColor(6, 1), [16, 17, 18, 255]); - assert.deepEqual(await getCellColor(7, 1), [19, 20, 21, 255]); - assert.deepEqual(await getCellColor(8, 1), [22, 23, 24, 255]); + await pollFor(page, () => getCellColor(1, 1), [1, 2, 3, 255]); + await pollFor(page, () => getCellColor(2, 1), [4, 5, 6, 255]); + await pollFor(page, () => getCellColor(3, 1), [7, 8, 9, 255]); + await pollFor(page, () => getCellColor(4, 1), [10, 11, 12, 255]); + await pollFor(page, () => getCellColor(5, 1), [13, 14, 15, 255]); + await pollFor(page, () => getCellColor(6, 1), [16, 17, 18, 255]); + await pollFor(page, () => getCellColor(7, 1), [19, 20, 21, 255]); + await pollFor(page, () => getCellColor(8, 1), [22, 23, 24, 255]); }); it('foreground colors bright', async function(): Promise { @@ -76,14 +73,14 @@ describe('WebGL Renderer Integration Tests', function(): void { }; await page.evaluate(`window.term.setOption('theme', ${JSON.stringify(theme)});`); await writeSync(`\\x1b[90m█\\x1b[91m█\\x1b[92m█\\x1b[93m█\\x1b[94m█\\x1b[95m█\\x1b[96m█\\x1b[97m█`); - assert.deepEqual(await getCellColor(1, 1), [1, 2, 3, 255]); - assert.deepEqual(await getCellColor(2, 1), [4, 5, 6, 255]); - assert.deepEqual(await getCellColor(3, 1), [7, 8, 9, 255]); - assert.deepEqual(await getCellColor(4, 1), [10, 11, 12, 255]); - assert.deepEqual(await getCellColor(5, 1), [13, 14, 15, 255]); - assert.deepEqual(await getCellColor(6, 1), [16, 17, 18, 255]); - assert.deepEqual(await getCellColor(7, 1), [19, 20, 21, 255]); - assert.deepEqual(await getCellColor(8, 1), [22, 23, 24, 255]); + await pollFor(page, () => getCellColor(1, 1), [1, 2, 3, 255]); + await pollFor(page, () => getCellColor(2, 1), [4, 5, 6, 255]); + await pollFor(page, () => getCellColor(3, 1), [7, 8, 9, 255]); + await pollFor(page, () => getCellColor(4, 1), [10, 11, 12, 255]); + await pollFor(page, () => getCellColor(5, 1), [13, 14, 15, 255]); + await pollFor(page, () => getCellColor(6, 1), [16, 17, 18, 255]); + await pollFor(page, () => getCellColor(7, 1), [19, 20, 21, 255]); + await pollFor(page, () => getCellColor(8, 1), [22, 23, 24, 255]); }); it('background colors normal', async function(): Promise { @@ -99,14 +96,14 @@ describe('WebGL Renderer Integration Tests', function(): void { }; await page.evaluate(`window.term.setOption('theme', ${JSON.stringify(theme)});`); await writeSync(`\\x1b[40m \\x1b[41m \\x1b[42m \\x1b[43m \\x1b[44m \\x1b[45m \\x1b[46m \\x1b[47m `); - assert.deepEqual(await getCellColor(1, 1), [1, 2, 3, 255]); - assert.deepEqual(await getCellColor(2, 1), [4, 5, 6, 255]); - assert.deepEqual(await getCellColor(3, 1), [7, 8, 9, 255]); - assert.deepEqual(await getCellColor(4, 1), [10, 11, 12, 255]); - assert.deepEqual(await getCellColor(5, 1), [13, 14, 15, 255]); - assert.deepEqual(await getCellColor(6, 1), [16, 17, 18, 255]); - assert.deepEqual(await getCellColor(7, 1), [19, 20, 21, 255]); - assert.deepEqual(await getCellColor(8, 1), [22, 23, 24, 255]); + await pollFor(page, () => getCellColor(1, 1), [1, 2, 3, 255]); + await pollFor(page, () => getCellColor(2, 1), [4, 5, 6, 255]); + await pollFor(page, () => getCellColor(3, 1), [7, 8, 9, 255]); + await pollFor(page, () => getCellColor(4, 1), [10, 11, 12, 255]); + await pollFor(page, () => getCellColor(5, 1), [13, 14, 15, 255]); + await pollFor(page, () => getCellColor(6, 1), [16, 17, 18, 255]); + await pollFor(page, () => getCellColor(7, 1), [19, 20, 21, 255]); + await pollFor(page, () => getCellColor(8, 1), [22, 23, 24, 255]); }); it('background colors bright', async function(): Promise { @@ -122,14 +119,14 @@ describe('WebGL Renderer Integration Tests', function(): void { }; await page.evaluate(`window.term.setOption('theme', ${JSON.stringify(theme)});`); await writeSync(`\\x1b[100m \\x1b[101m \\x1b[102m \\x1b[103m \\x1b[104m \\x1b[105m \\x1b[106m \\x1b[107m `); - assert.deepEqual(await getCellColor(1, 1), [1, 2, 3, 255]); - assert.deepEqual(await getCellColor(2, 1), [4, 5, 6, 255]); - assert.deepEqual(await getCellColor(3, 1), [7, 8, 9, 255]); - assert.deepEqual(await getCellColor(4, 1), [10, 11, 12, 255]); - assert.deepEqual(await getCellColor(5, 1), [13, 14, 15, 255]); - assert.deepEqual(await getCellColor(6, 1), [16, 17, 18, 255]); - assert.deepEqual(await getCellColor(7, 1), [19, 20, 21, 255]); - assert.deepEqual(await getCellColor(8, 1), [22, 23, 24, 255]); + await pollFor(page, () => getCellColor(1, 1), [1, 2, 3, 255]); + await pollFor(page, () => getCellColor(2, 1), [4, 5, 6, 255]); + await pollFor(page, () => getCellColor(3, 1), [7, 8, 9, 255]); + await pollFor(page, () => getCellColor(4, 1), [10, 11, 12, 255]); + await pollFor(page, () => getCellColor(5, 1), [13, 14, 15, 255]); + await pollFor(page, () => getCellColor(6, 1), [16, 17, 18, 255]); + await pollFor(page, () => getCellColor(7, 1), [19, 20, 21, 255]); + await pollFor(page, () => getCellColor(8, 1), [22, 23, 24, 255]); }); }); }); @@ -161,3 +158,21 @@ async function getCellColor(col: number, row: number): Promise { `); return await page.evaluate(`Array.from(window.result)`); } + +async function pollFor(page: puppeteer.Page, evalOrFn: string | (() => Promise), val: T, preFn?: () => Promise): Promise { + if (preFn) { + await preFn(); + } + const result = typeof evalOrFn === 'string' ? await page.evaluate(evalOrFn) : await evalOrFn(); + let equal = false; + if (typeof result === 'object') { + equal = Object.keys(result).every(e => result[e] === (val as any)[e]); + } else { + equal = result === val; + } + if (!equal) { + return new Promise(r => { + setTimeout(() => r(pollFor(page, evalOrFn, val, preFn)), 10); + }); + } +} From 74f04ae92e4251e029faab3ed05fe5f5e4c201da Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sat, 9 Nov 2019 14:12:46 -0800 Subject: [PATCH 53/92] Fix timing issues in char width test --- .../src/WebglRenderer.api.ts | 3 +-- test/api/CharWidth.api.ts | 17 ++++++------- test/api/TestUtils.ts | 24 +++++++++++++++++++ 3 files changed, 32 insertions(+), 12 deletions(-) create mode 100644 test/api/TestUtils.ts diff --git a/addons/xterm-addon-webgl/src/WebglRenderer.api.ts b/addons/xterm-addon-webgl/src/WebglRenderer.api.ts index 18d80276..9dfac843 100644 --- a/addons/xterm-addon-webgl/src/WebglRenderer.api.ts +++ b/addons/xterm-addon-webgl/src/WebglRenderer.api.ts @@ -4,7 +4,6 @@ */ import * as puppeteer from 'puppeteer'; -import { assert } from 'chai'; import { ITerminalOptions } from '../../../src/Types'; import { ITheme } from 'xterm'; @@ -15,7 +14,7 @@ let page: puppeteer.Page; const width = 800; const height = 600; -describe.only('WebGL Renderer Integration Tests', function(): void { +describe('WebGL Renderer Integration Tests', function(): void { before(async function(): Promise { browser = await puppeteer.launch({ headless: process.argv.indexOf('--headless') !== -1, diff --git a/test/api/CharWidth.api.ts b/test/api/CharWidth.api.ts index a20ecc8b..39f0bfe7 100644 --- a/test/api/CharWidth.api.ts +++ b/test/api/CharWidth.api.ts @@ -4,8 +4,8 @@ */ import * as puppeteer from 'puppeteer'; -import { assert } from 'chai'; import { ITerminalOptions } from 'xterm'; +import { pollFor } from './TestUtils'; const APP = 'http://127.0.0.1:3000/test'; @@ -15,12 +15,9 @@ const width = 800; const height = 600; describe('CharWidth Integration Tests', function(): void { - this.timeout(20000); - before(async function(): Promise { browser = await puppeteer.launch({ headless: process.argv.indexOf('--headless') !== -1, - slowMo: 0, args: [`--window-size=${width},${height}`, `--no-sandbox`] }); page = (await browser.pages())[0]; @@ -41,37 +38,37 @@ describe('CharWidth Integration Tests', function(): void { it('ASCII chars', async function(): Promise { const input = 'This is just ASCII text.#'; await page.evaluate(`window.term.write('${input}')`); - assert.equal(25, await sumWidths(0, 1, '#')); + await pollFor(page, () => sumWidths(0, 1, '#'), 25); }); it('combining chars', async function(): Promise { const input = 'e\u0301e\u0301e\u0301e\u0301e\u0301e\u0301e\u0301e\u0301e\u0301#'; await page.evaluate(`window.term.write('${input}')`); - assert.equal(10, await sumWidths(0, 1, '#')); + await pollFor(page, () => sumWidths(0, 1, '#'), 10); }); it('surrogate chars', async function(): Promise { const input = '𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞#'; await page.evaluate(`window.term.write('${input}')`); - assert.equal(28, await sumWidths(0, 1, '#')); + await pollFor(page, () => sumWidths(0, 1, '#'), 28); }); it('surrogate combining chars', async function(): Promise { const input = '𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301#'; await page.evaluate(`window.term.write('${input}')`); - assert.equal(12, await sumWidths(0, 1, '#')); + await pollFor(page, () => sumWidths(0, 1, '#'), 12); }); it('fullwidth chars', async function(): Promise { const input = '1234567890#'; await page.evaluate(`window.term.write('${input}')`); - assert.equal(21, await sumWidths(0, 1, '#')); + await pollFor(page, () => sumWidths(0, 1, '#'), 21); }); it('fullwidth chars offset 1', async function(): Promise { const input = 'a1234567890#'; await page.evaluate(`window.term.write('${input}')`); - assert.equal(22, await sumWidths(0, 1, '#')); + await pollFor(page, () => sumWidths(0, 1, '#'), 22); }); // TODO: multiline tests once #1685 is resolved diff --git a/test/api/TestUtils.ts b/test/api/TestUtils.ts new file mode 100644 index 00000000..ff1c661e --- /dev/null +++ b/test/api/TestUtils.ts @@ -0,0 +1,24 @@ +/** + * Copyright (c) 2019 The xterm.js authors. All rights reserved. + * @license MIT + */ + +import * as puppeteer from 'puppeteer'; + +export async function pollFor(page: puppeteer.Page, evalOrFn: string | (() => Promise), val: T, preFn?: () => Promise): Promise { + if (preFn) { + await preFn(); + } + const result = typeof evalOrFn === 'string' ? await page.evaluate(evalOrFn) : await evalOrFn(); + let equal = false; + if (typeof result === 'object') { + equal = Object.keys(result).every(e => result[e] === (val as any)[e]); + } else { + equal = result === val; + } + if (!equal) { + return new Promise(r => { + setTimeout(() => r(pollFor(page, evalOrFn, val, preFn)), 10); + }); + } +} From 2aaa997d395d97bd7ac9a6633ced85eae6fa19c0 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sat, 9 Nov 2019 14:15:16 -0800 Subject: [PATCH 54/92] Fix timing issues in input handler --- test/api/InputHandler.api.ts | 86 ++++++++++++++++++------------------ 1 file changed, 43 insertions(+), 43 deletions(-) diff --git a/test/api/InputHandler.api.ts b/test/api/InputHandler.api.ts index 2070dfe6..4bc2c07d 100644 --- a/test/api/InputHandler.api.ts +++ b/test/api/InputHandler.api.ts @@ -6,6 +6,7 @@ import * as puppeteer from 'puppeteer'; import { assert } from 'chai'; import { ITerminalOptions } from 'xterm'; +import { pollFor } from './TestUtils'; const APP = 'http://127.0.0.1:3000/test'; @@ -15,8 +16,6 @@ const width = 800; const height = 600; describe('InputHandler Integration Tests', function(): void { - this.timeout(20000); - before(async function(): Promise { browser = await puppeteer.launch({ headless: process.argv.indexOf('--headless') !== -1, @@ -45,7 +44,7 @@ describe('InputHandler Integration Tests', function(): void { // Explicit window.term.write('bar\\x1b[3D\\x1b[4@') `); - assert.deepEqual(await getLinesAsArray(2), [' foo', ' bar']); + await pollFor(page, () => getLinesAsArray(2), [' foo', ' bar']); }); it('CUU: Cursor Up Ps Times (default = 1) - CSI Ps A', async function(): Promise { @@ -55,7 +54,7 @@ describe('InputHandler Integration Tests', function(): void { // Explicit window.term.write('\x1b[2Ab') `); - assert.deepEqual(await getLinesAsArray(4), ['', ' b', '', 'a']); + await pollFor(page, () => getLinesAsArray(4), ['', ' b', '', 'a']); }); it('CUD: Cursor Down Ps Times (default = 1) - CSI Ps B', async function(): Promise { @@ -65,7 +64,7 @@ describe('InputHandler Integration Tests', function(): void { // Explicit window.term.write('\x1b[2Bb') `); - assert.deepEqual(await getLinesAsArray(4), ['', 'a', '', ' b']); + await pollFor(page, () => getLinesAsArray(4), ['', 'a', '', ' b']); }); it('CUF: Cursor Forward Ps Times (default = 1) - CSI Ps C', async function(): Promise { @@ -75,7 +74,7 @@ describe('InputHandler Integration Tests', function(): void { // Explicit window.term.write('\x1b[2Cb') `); - assert.deepEqual(await getLinesAsArray(1), [' a b']); + await pollFor(page, () => getLinesAsArray(1), [' a b']); }); it('CUB: Cursor Backward Ps Times (default = 1) - CSI Ps D', async function(): Promise { @@ -85,7 +84,7 @@ describe('InputHandler Integration Tests', function(): void { // Explicit window.term.write('\x1b[2Db') `); - assert.deepEqual(await getLinesAsArray(1), ['fba']); + await pollFor(page, () => getLinesAsArray(1), ['fba']); }); it('CNL: Cursor Next Line Ps Times (default = 1) - CSI Ps E', async function(): Promise { @@ -95,7 +94,7 @@ describe('InputHandler Integration Tests', function(): void { // Explicit window.term.write('\x1b[2Eb') `); - assert.deepEqual(await getLinesAsArray(4), ['', 'a', '', 'b']); + await pollFor(page, () => getLinesAsArray(4), ['', 'a', '', 'b']); }); it('CPL: Cursor Preceding Line Ps Times (default = 1) - CSI Ps F', async function(): Promise { @@ -105,7 +104,7 @@ describe('InputHandler Integration Tests', function(): void { // Explicit window.term.write('\x1b[2Fb') `); - assert.deepEqual(await getLinesAsArray(5), ['', 'b', '', 'a', '']); + await pollFor(page, () => getLinesAsArray(5), ['', 'b', '', 'a', '']); }); it('CHA: Cursor Character Absolute [column] (default = [row,1]) - CSI Ps G', async function(): Promise { @@ -115,7 +114,7 @@ describe('InputHandler Integration Tests', function(): void { // Explicit window.term.write('\x1b[10Gb') `); - assert.deepEqual(await getLinesAsArray(1), ['aoo b']); + await pollFor(page, () => getLinesAsArray(1), ['aoo b']); }); it('CUP: Cursor Position [row;column] (default = [1,1]) - CSI Ps ; Ps H', async function(): Promise { @@ -125,7 +124,7 @@ describe('InputHandler Integration Tests', function(): void { // Explicit window.term.write('\x1b[3;3Hb') `); - assert.deepEqual(await getLinesAsArray(3), ['aoo', '', ' b']); + await pollFor(page, () => getLinesAsArray(3), ['aoo', '', ' b']); }); it('CHT: Cursor Forward Tabulation Ps tab stops (default = 1) - CSI Ps I', async function(): Promise { @@ -135,7 +134,7 @@ describe('InputHandler Integration Tests', function(): void { // Explicit window.term.write('\\n\\r\x1b[2Ib') `); - assert.deepEqual(await getLinesAsArray(2), [' a', ' b']); + await pollFor(page, () => getLinesAsArray(2), [' a', ' b']); }); it('ED: Erase in Display, VT100 - CSI Ps J', async function(): Promise { @@ -145,26 +144,26 @@ describe('InputHandler Integration Tests', function(): void { window.term.resize(5, 5); window.term.write('${fixture}\x1b[J') `); - assert.deepEqual(await getLinesAsArray(3), ['abc', 'd', '']); + await pollFor(page, () => getLinesAsArray(3), ['abc', 'd', '']); await page.evaluate(` // 0: Erase Below window.term.reset() window.term.write('${fixture}\x1b[0J') `); - assert.deepEqual(await getLinesAsArray(3), ['abc', 'd', '']); + await pollFor(page, () => getLinesAsArray(3), ['abc', 'd', '']); await page.evaluate(` // 1: Erase Above window.term.reset() window.term.write('${fixture}\x1b[1J') `); - assert.deepEqual(await getLinesAsArray(3), ['', ' f', 'ghi']); + await pollFor(page, () => getLinesAsArray(3), ['', ' f', 'ghi']); await page.evaluate(` // 2: Erase Saved Lines (scrollback) window.term.reset() window.term.write('1\\n2\\n3\\n4\\n5${fixture}\x1b[3J') `); - assert.equal(await page.evaluate(`window.term.buffer.length`), 5); - assert.deepEqual(await getLinesAsArray(5), [' 4', ' 5', 'abc', 'def', 'ghi']); + await pollFor(page, () => page.evaluate(`window.term.buffer.length`), 5); + await pollFor(page, () => getLinesAsArray(5), [' 4', ' 5', 'abc', 'def', 'ghi']); }); it('DECSED: Erase in Display, VT220 - CSI ? Ps J', async function(): Promise { @@ -174,26 +173,26 @@ describe('InputHandler Integration Tests', function(): void { window.term.resize(5, 5); window.term.write('${fixture}\x1b[?J') `); - assert.deepEqual(await getLinesAsArray(3), ['abc', 'd', '']); + await pollFor(page, () => getLinesAsArray(3), ['abc', 'd', '']); await page.evaluate(` // 0: Erase Below window.term.reset() window.term.write('${fixture}\x1b[?0J') `); - assert.deepEqual(await getLinesAsArray(3), ['abc', 'd', '']); + await pollFor(page, () => getLinesAsArray(3), ['abc', 'd', '']); await page.evaluate(` // 1: Erase Above window.term.reset() window.term.write('${fixture}\x1b[?1J') `); - assert.deepEqual(await getLinesAsArray(3), ['', ' f', 'ghi']); + await pollFor(page, () => getLinesAsArray(3), ['', ' f', 'ghi']); await page.evaluate(` // 2: Erase Saved Lines (scrollback) window.term.reset() window.term.write('1\\n2\\n3\\n4\\n5${fixture}\x1b[?3J') `); - assert.equal(await page.evaluate(`window.term.buffer.length`), 5); - assert.deepEqual(await getLinesAsArray(5), [' 4', ' 5', 'abc', 'def', 'ghi']); + await pollFor(page, () => page.evaluate(`window.term.buffer.length`), 5); + await pollFor(page, () => getLinesAsArray(5), [' 4', ' 5', 'abc', 'def', 'ghi']); }); it('IL: Insert Ps Line(s) (default = 1) - CSI Ps L', async function(): Promise { @@ -203,7 +202,7 @@ describe('InputHandler Integration Tests', function(): void { // Explicit window.term.write('\x1b[2Lb') `); - assert.deepEqual(await getLinesAsArray(4), ['b', '', 'a', 'foo']); + await pollFor(page, () => getLinesAsArray(4), ['b', '', 'a', 'foo']); }); it('DL: Delete Ps Line(s) (default = 1) - CSI Ps M', async function(): Promise { @@ -213,7 +212,7 @@ describe('InputHandler Integration Tests', function(): void { // Explicit window.term.write('\x1b[1Ed\\ne\\nf\x1b[2F\x1b[2M') `); - assert.deepEqual(await getLinesAsArray(5), [' b', ' f', '', '', '']); + await pollFor(page, () => getLinesAsArray(5), [' b', ' f', '', '', '']); }); it('DCH: Delete Ps Character(s) (default = 1) - CSI Ps P', async function(): Promise { @@ -223,7 +222,7 @@ describe('InputHandler Integration Tests', function(): void { // Explicit window.term.write('\\n\\rdef\x1b[2;1H\x1b[2P') `); - assert.deepEqual(await getLinesAsArray(2), ['bc', 'f']); + await pollFor(page, () => getLinesAsArray(2), ['bc', 'f']); }); describe('DSR: Device Status Report', () => { @@ -232,31 +231,31 @@ describe('InputHandler Integration Tests', function(): void { window.term.onData(e => window.result = e); window.term.write('\\x1b[5n'); `); - assert.equal(await page.evaluate(`window.result`), '\x1b[0n'); + await pollFor(page, () => page.evaluate(`window.result`), '\x1b[0n'); }); it('Report Cursor Position (CPR) - CSI 6 n', async function(): Promise { await page.evaluate(`window.term.write('\\n\\nfoo')`); - assert.deepEqual(await page.evaluate(` + await pollFor(page, () => page.evaluate(` [window.term.buffer.cursorY, window.term.buffer.cursorX] `), [2, 3]); await page.evaluate(` window.term.onData(e => window.result = e); window.term.write('\\x1b[6n'); `); - assert.equal(await page.evaluate(`window.result`), '\x1b[3;4R'); + await pollFor(page, () => page.evaluate(`window.result`), '\x1b[3;4R'); }); it('Report Cursor Position (DECXCPR) - CSI ? 6 n', async function(): Promise { await page.evaluate(`window.term.write('\\n\\nfoo')`); - assert.deepEqual(await page.evaluate(` + await pollFor(page, () => page.evaluate(` [window.term.buffer.cursorY, window.term.buffer.cursorX] `), [2, 3]); await page.evaluate(` window.term.onData(e => window.result = e); window.term.write('\\x1b[?6n'); `); - assert.equal(await page.evaluate(`window.result`), '\x1b[?3;4R'); + await pollFor(page, () => page.evaluate(`window.result`), '\x1b[?3;4R'); }); }); @@ -277,22 +276,23 @@ describe('InputHandler Integration Tests', function(): void { await page.mouse.up(); // Clear selection await page.mouse.click((coords.left + coords.right) / 2, (coords.top + coords.bottom) / 2); - assert.equal(await page.evaluate(`window.term.getSelection().length`), 0); + await pollFor(page, () => page.evaluate(`window.term.getSelection().length`), 0); // Enable mouse events await page.evaluate(`window.term.write('\x1b[?1003h')`); // Click and drag and ensure there is no selection await page.mouse.click((coords.left + coords.right) / 2, (coords.top + coords.bottom) / 2); await page.mouse.down(); await page.mouse.move((coords.left + coords.right) / 2, (coords.top + coords.bottom) / 4); - assert.equal(await page.evaluate(`window.term.getSelection().length`), 0, 'mouse events are on so there should be no selection'); + // mouse events are on so there should be no selection + await pollFor(page, () => page.evaluate(`window.term.getSelection().length`), 0); await page.mouse.up(); }); it('Pm = 2004, Set bracketed paste mode', async function(): Promise { - assert.equal(await simulatePaste('foo'), 'foo'); + await pollFor(page, () => simulatePaste('foo'), 'foo'); await page.evaluate(`window.term.write('\x1b[?2004h')`); - assert.equal(await simulatePaste('bar'), '\x1b[200~bar\x1b[201~'); + await pollFor(page, () => simulatePaste('bar'), '\x1b[200~bar\x1b[201~'); await page.evaluate(`window.term.write('\x1b[?2004l')`); - assert.equal(await simulatePaste('baz'), 'baz'); + await pollFor(page, () => simulatePaste('baz'), 'baz'); }); }); }); @@ -309,32 +309,32 @@ describe('InputHandler Integration Tests', function(): void { window.term.writeln(''); window.term.write('#\x1b[5b'); `); - assert.deepEqual(await getLinesAsArray(4), ['##', '##', '##', '######']); - assert.deepEqual(await getCursor(), {col: 6, row: 3}); + await pollFor(page, () => getLinesAsArray(4), ['##', '##', '##', '######']); + await pollFor(page, () => getCursor(), {col: 6, row: 3}); // should not repeat on fullwidth chars await page.evaluate(` window.term.reset(); window.term.write('¥\x1b[10b'); `); - assert.deepEqual(await getLinesAsArray(1), ['¥']); + await pollFor(page, () => getLinesAsArray(1), ['¥']); // should repeat only base char of combining await page.evaluate(` window.term.reset(); window.term.write('e\u0301\x1b[5b'); `); - assert.deepEqual(await getLinesAsArray(1), ['e\u0301eeeee']); + await pollFor(page, () => getLinesAsArray(1), ['e\u0301eeeee']); // should wrap correctly await page.evaluate(` window.term.reset(); window.term.write('#\x1b[15b'); `); - assert.deepEqual(await getLinesAsArray(2), ['##########', '######']); + await pollFor(page, () => getLinesAsArray(2), ['##########', '######']); await page.evaluate(` window.term.reset(); window.term.write('\x1b[?7l'); // disable wrap around window.term.write('#\x1b[15b'); `); - assert.deepEqual(await getLinesAsArray(2), ['##########', '']); + await pollFor(page, () => getLinesAsArray(2), ['##########', '']); // any successful sequence should reset REP await page.evaluate(` window.term.reset(); @@ -344,7 +344,7 @@ describe('InputHandler Integration Tests', function(): void { window.term.writeln(''); window.term.write('abcdefg\x1b[3D\x1b[10b#\x1b[3b'); `); - assert.deepEqual(await getLinesAsArray(3), ['#', ' #', 'abcd####']); + await pollFor(page, () => getLinesAsArray(3), ['#', ' #', 'abcd####']); }); }); @@ -360,7 +360,7 @@ describe('InputHandler Integration Tests', function(): void { window.term.resize(10, 4); window.term.write('\\x1b[?47l\\x1b8'); `); - assert.deepEqual(await getCursor(), {col: 1, row: 3}); + await pollFor(page, () => getCursor(), {col: 1, row: 3}); }); }); }); From 17677568f9ab3402355ba94870ab1defc3bbede6 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sat, 9 Nov 2019 14:16:57 -0800 Subject: [PATCH 55/92] Fix timing issues in mouse tracking tests --- test/api/MouseTracking.api.ts | 249 +++++++++++++++++----------------- 1 file changed, 122 insertions(+), 127 deletions(-) diff --git a/test/api/MouseTracking.api.ts b/test/api/MouseTracking.api.ts index a993c6a1..910763cd 100644 --- a/test/api/MouseTracking.api.ts +++ b/test/api/MouseTracking.api.ts @@ -4,8 +4,8 @@ */ import * as puppeteer from 'puppeteer'; -import { assert } from 'chai'; import { ITerminalOptions } from 'xterm'; +import { pollFor } from './TestUtils'; const APP = 'http://127.0.0.1:3000/test'; @@ -199,21 +199,16 @@ function parseReport(encoding: string, msg: number[]): {state: any; row: number; * Mouse tracking tests. */ describe('Mouse Tracking Tests', function(): void { - this.timeout(60000); - before(async function(): Promise { browser = await puppeteer.launch({ headless: process.argv.indexOf('--headless') !== -1, - slowMo: 0, args: [`--window-size=${width},${height}`, `--no-sandbox`] }); page = (await browser.pages())[0]; await page.setViewport({ width, height }); }); - after(() => { - browser.close(); - }); + after(async () => browser.close()); beforeEach(async () => { await page.goto(APP); @@ -246,18 +241,18 @@ describe('Mouse Tracking Tests', function(): void { // test at 0,0 await mouseDown('left'); - assert.deepEqual(await getReports(encoding), [{col: 1, row: 1, state: {action: 'press', button: 'left', modifier: {control: false, shift: false, meta: false}}}]); + await pollFor(page, () => getReports(encoding), [{col: 1, row: 1, state: {action: 'press', button: 'left', modifier: {control: false, shift: false, meta: false}}}]); // mouseup should not report await mouseUp('left'); - assert.deepEqual(await getReports(encoding), []); + await pollFor(page, () => getReports(encoding), []); // mousemove should not report await mouseMove(50, 10); - assert.deepEqual(await getReports(encoding), []); + await pollFor(page, () => getReports(encoding), []); await mouseDown('left'); await mouseUp('left'); - assert.deepEqual(await getReports(encoding), [{col: 51, row: 11, state: {action: 'press', button: 'left', modifier: {control: false, shift: false, meta: false}}}]); + await pollFor(page, () => getReports(encoding), [{col: 51, row: 11, state: {action: 'press', button: 'left', modifier: {control: false, shift: false, meta: false}}}]); // test at max rows/cols // bug: we are capped at col 95 currently @@ -265,7 +260,7 @@ describe('Mouse Tracking Tests', function(): void { await mouseMove(cols - 1, rows - 1); await mouseDown('left'); await mouseUp('left'); - assert.deepEqual(await getReports(encoding), [{col: 95, row: rows, state: {action: 'press', button: 'left', modifier: {control: false, shift: false, meta: false}}}]); + await pollFor(page, () => getReports(encoding), [{col: 95, row: rows, state: {action: 'press', button: 'left', modifier: {control: false, shift: false, meta: false}}}]); // button press/move/release tests // left button @@ -274,7 +269,7 @@ describe('Mouse Tracking Tests', function(): void { await mouseDown('left'); await mouseMove(44, 24); await mouseUp('left'); - assert.deepEqual(await getReports(encoding), [{col: 44, row: 25, state: {action: 'press', button: 'left', modifier: {control: false, shift: false, meta: false}}}]); + await pollFor(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); @@ -282,7 +277,7 @@ describe('Mouse Tracking Tests', function(): void { // await mouseDown('middle'); // await mouseMove(44, 24); // await mouseUp('middle'); - // assert.deepEqual(await getReports(encoding), [{col: 44, row: 25, state: {action: 'press', button: 'middle', modifier: {control: false, shift: false, meta: false}}}]); + // await pollFor(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); @@ -290,15 +285,15 @@ describe('Mouse Tracking Tests', function(): void { await mouseDown('right'); await mouseMove(44, 24); await mouseUp('right'); - assert.deepEqual(await getReports(encoding), [{col: 44, row: 25, state: {action: 'press', button: 'right', modifier: {control: false, shift: false, meta: false}}}]); + await pollFor(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(); - assert.deepEqual(await getReports(encoding), []); + await pollFor(page, () => getReports(encoding), []); await wheelDown(); - assert.deepEqual(await getReports(encoding), []); + await pollFor(page, () => getReports(encoding), []); // modifiers // CTRL @@ -310,7 +305,7 @@ describe('Mouse Tracking Tests', function(): void { await mouseUp('left'); await wheelDown(); await page.keyboard.up('Control'); - assert.deepEqual(await getReports(encoding), [{col: 44, row: 25, state: {action: 'press', button: 'left', modifier: {control: false, shift: false, meta: false}}}]); + await pollFor(page, () => getReports(encoding), [{col: 44, row: 25, state: {action: 'press', button: 'left', modifier: {control: false, shift: false, meta: false}}}]); // ALT @@ -322,7 +317,7 @@ describe('Mouse Tracking Tests', function(): void { await mouseUp('left'); await wheelDown(); await page.keyboard.up('Alt'); - assert.deepEqual(await getReports(encoding), [{col: 44, row: 25, state: {action: 'press', button: 'left', modifier: {control: false, shift: false, meta: false}}}]); + await pollFor(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 @@ -337,9 +332,9 @@ describe('Mouse Tracking Tests', function(): void { await wheelDown(); await page.keyboard.up('Shift'); if (noShift) { - assert.deepEqual(await getReports(encoding), []); + await pollFor(page, () => getReports(encoding), []); } else { - assert.deepEqual(await getReports(encoding), [ + await pollFor(page, () => getReports(encoding), [ {col: 44, row: 25, state: {action: 'press', button: 'left', modifier: {control: false, shift: false, meta: false}}} ]); } @@ -358,7 +353,7 @@ describe('Mouse Tracking Tests', function(): void { await page.keyboard.up('Control'); await page.keyboard.up('Alt'); // await page.keyboard.up('Shift'); - assert.deepEqual(await getReports(encoding), [{col: 44, row: 25, state: {action: 'press', button: 'left', modifier: {control: false, shift: false, meta: false}}}]); + await pollFor(page, () => getReports(encoding), [{col: 44, row: 25, state: {action: 'press', button: 'left', modifier: {control: false, shift: false, meta: false}}}]); }); it('SGR encoding', async () => { const encoding = 'SGR'; @@ -368,24 +363,24 @@ describe('Mouse Tracking Tests', function(): void { // test at 0,0 await mouseDown('left'); - assert.deepEqual(await getReports(encoding), [{col: 1, row: 1, state: {action: 'press', button: 'left', modifier: {control: false, shift: false, meta: false}}}]); + await pollFor(page, () => getReports(encoding), [{col: 1, row: 1, state: {action: 'press', button: 'left', modifier: {control: false, shift: false, meta: false}}}]); // mouseup should not report await mouseUp('left'); - assert.deepEqual(await getReports(encoding), []); + await pollFor(page, () => getReports(encoding), []); // mousemove should not report await mouseMove(50, 10); - assert.deepEqual(await getReports(encoding), []); + await pollFor(page, () => getReports(encoding), []); await mouseDown('left'); await mouseUp('left'); - assert.deepEqual(await getReports(encoding), [{col: 51, row: 11, state: {action: 'press', button: 'left', modifier: {control: false, shift: false, meta: false}}}]); + await pollFor(page, () => getReports(encoding), [{col: 51, row: 11, state: {action: 'press', button: 'left', modifier: {control: false, shift: false, meta: false}}}]); // test at max rows/cols await mouseMove(cols - 1, rows - 1); await mouseDown('left'); await mouseUp('left'); - assert.deepEqual(await getReports(encoding), [{col: cols, row: rows, state: {action: 'press', button: 'left', modifier: {control: false, shift: false, meta: false}}}]); + await pollFor(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 @@ -394,7 +389,7 @@ describe('Mouse Tracking Tests', function(): void { await mouseDown('left'); await mouseMove(44, 24); await mouseUp('left'); - assert.deepEqual(await getReports(encoding), [{col: 44, row: 25, state: {action: 'press', button: 'left', modifier: {control: false, shift: false, meta: false}}}]); + await pollFor(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); @@ -402,7 +397,7 @@ describe('Mouse Tracking Tests', function(): void { // await mouseDown('middle'); // await mouseMove(44, 24); // await mouseUp('middle'); - // assert.deepEqual(await getReports(encoding), [{col: 44, row: 25, state: {action: 'press', button: 'middle', modifier: {control: false, shift: false, meta: false}}}]); + // await pollFor(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); @@ -410,15 +405,15 @@ describe('Mouse Tracking Tests', function(): void { await mouseDown('right'); await mouseMove(44, 24); await mouseUp('right'); - assert.deepEqual(await getReports(encoding), [{col: 44, row: 25, state: {action: 'press', button: 'right', modifier: {control: false, shift: false, meta: false}}}]); + await pollFor(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(); - assert.deepEqual(await getReports(encoding), []); + await pollFor(page, () => getReports(encoding), []); await wheelDown(); - assert.deepEqual(await getReports(encoding), []); + await pollFor(page, () => getReports(encoding), []); // modifiers // CTRL @@ -430,7 +425,7 @@ describe('Mouse Tracking Tests', function(): void { await mouseUp('left'); await wheelDown(); await page.keyboard.up('Control'); - assert.deepEqual(await getReports(encoding), [{col: 44, row: 25, state: {action: 'press', button: 'left', modifier: {control: false, shift: false, meta: false}}}]); + await pollFor(page, () => getReports(encoding), [{col: 44, row: 25, state: {action: 'press', button: 'left', modifier: {control: false, shift: false, meta: false}}}]); // ALT @@ -442,7 +437,7 @@ describe('Mouse Tracking Tests', function(): void { await mouseUp('left'); await wheelDown(); await page.keyboard.up('Alt'); - assert.deepEqual(await getReports(encoding), [{col: 44, row: 25, state: {action: 'press', button: 'left', modifier: {control: false, shift: false, meta: false}}}]); + await pollFor(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 @@ -455,9 +450,9 @@ describe('Mouse Tracking Tests', function(): void { await wheelDown(); await page.keyboard.up('Shift'); if (noShift) { - assert.deepEqual(await getReports(encoding), []); + await pollFor(page, () => getReports(encoding), []); } else { - assert.deepEqual(await getReports(encoding), [ + await pollFor(page, () => getReports(encoding), [ {col: 44, row: 25, state: {action: 'press', button: 'left', modifier: {control: false, shift: false, meta: false}}} ]); } @@ -476,7 +471,7 @@ describe('Mouse Tracking Tests', function(): void { await page.keyboard.up('Control'); await page.keyboard.up('Alt'); // await page.keyboard.up('Shift'); - assert.deepEqual(await getReports(encoding), [{col: 44, row: 25, state: {action: 'press', button: 'left', modifier: {control: false, shift: false, meta: false}}}]); + await pollFor(page, () => getReports(encoding), [{col: 44, row: 25, state: {action: 'press', button: 'left', modifier: {control: false, shift: false, meta: false}}}]); }); }); describe('DECSET 1000 (VT200 mouse)', () => { @@ -495,22 +490,22 @@ describe('Mouse Tracking Tests', function(): void { // test at 0,0 await mouseDown('left'); - assert.deepEqual(await getReports(encoding), [ + await pollFor(page, () => getReports(encoding), [ {col: 1, row: 1, state: {action: 'press', button: 'left', modifier: {control: false, shift: false, meta: false}}} ]); // mouseup should report, encoding cannot report released button await mouseUp('left'); - assert.deepEqual(await getReports(encoding), [ + await pollFor(page, () => getReports(encoding), [ {col: 1, row: 1, state: {action: 'release', button: '', modifier: {control: false, shift: false, meta: false}}} ]); // mousemove should not report await mouseMove(50, 10); - assert.deepEqual(await getReports(encoding), []); + await pollFor(page, () => getReports(encoding), []); await mouseDown('left'); await mouseUp('left'); - assert.deepEqual(await getReports(encoding), [ + await pollFor(page, () => getReports(encoding), [ {col: 51, row: 11, state: {action: 'press', button: 'left', modifier: {control: false, shift: false, meta: false}}}, {col: 51, row: 11, state: {action: 'release', button: '', modifier: {control: false, shift: false, meta: false}}} ]); @@ -521,7 +516,7 @@ describe('Mouse Tracking Tests', function(): void { await mouseMove(cols - 1, rows - 1); await mouseDown('left'); await mouseUp('left'); - assert.deepEqual(await getReports(encoding), [ + await pollFor(page, () => getReports(encoding), [ {col: 95, row: rows, state: {action: 'press', button: 'left', modifier: {control: false, shift: false, meta: false}}}, {col: 95, row: rows, state: {action: 'release', button: '', modifier: {control: false, shift: false, meta: false}}} ]); @@ -533,7 +528,7 @@ describe('Mouse Tracking Tests', function(): void { await mouseDown('left'); await mouseMove(44, 24); await mouseUp('left'); - assert.deepEqual(await getReports(encoding), [ + await pollFor(page, () => getReports(encoding), [ {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}}} ]); @@ -544,7 +539,7 @@ describe('Mouse Tracking Tests', function(): void { // await mouseDown('middle'); // await mouseMove(44, 24); // await mouseUp('middle'); - // assert.deepEqual(await getReports(encoding), [{col: 44, row: 25, state: {action: 'press', button: 'middle', modifier: {control: false, shift: false, meta: false}}}]); + // await pollFor(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); @@ -552,7 +547,7 @@ describe('Mouse Tracking Tests', function(): void { await mouseDown('right'); await mouseMove(44, 24); await mouseUp('right'); - assert.deepEqual(await getReports(encoding), [ + await pollFor(page, () => getReports(encoding), [ {col: 44, row: 25, state: {action: 'press', button: 'right', modifier: {control: false, shift: false, meta: false}}}, {col: 45, row: 25, state: {action: 'release', button: '', modifier: {control: false, shift: false, meta: false}}} ]); @@ -561,9 +556,9 @@ describe('Mouse Tracking Tests', function(): void { await mouseMove(43, 24); await getReports(encoding); // clear reports await wheelUp(); - assert.deepEqual(await getReports(encoding), [{col: 44, row: 25, state: {action: 'up', button: 'wheel', modifier: {control: false, shift: false, meta: false}}}]); + await pollFor(page, () => getReports(encoding), [{col: 44, row: 25, state: {action: 'up', button: 'wheel', modifier: {control: false, shift: false, meta: false}}}]); await wheelDown(); - assert.deepEqual(await getReports(encoding), [{col: 44, row: 25, state: {action: 'down', button: 'wheel', modifier: {control: false, shift: false, meta: false}}}]); + await pollFor(page, () => getReports(encoding), [{col: 44, row: 25, state: {action: 'down', button: 'wheel', modifier: {control: false, shift: false, meta: false}}}]); // modifiers // CTRL @@ -575,7 +570,7 @@ describe('Mouse Tracking Tests', function(): void { await mouseUp('left'); await wheelDown(); await page.keyboard.up('Control'); - assert.deepEqual(await getReports(encoding), [ + await pollFor(page, () => getReports(encoding), [ {col: 44, row: 25, state: {action: 'press', button: 'left', modifier: {control: true, shift: false, meta: false}}}, {col: 45, row: 25, state: {action: 'release', button: '', modifier: {control: true, shift: false, meta: false}}}, {col: 45, row: 25, state: {action: 'down', button: 'wheel', modifier: {control: true, shift: false, meta: false}}} @@ -590,7 +585,7 @@ describe('Mouse Tracking Tests', function(): void { await mouseUp('left'); await wheelDown(); await page.keyboard.up('Alt'); - assert.deepEqual(await getReports(encoding), [ + await pollFor(page, () => getReports(encoding), [ {col: 44, row: 25, state: {action: 'press', button: 'left', modifier: {control: false, shift: false, meta: true}}}, {col: 45, row: 25, state: {action: 'release', button: '', modifier: {control: false, shift: false, meta: true}}}, {col: 45, row: 25, state: {action: 'down', button: 'wheel', modifier: {control: false, shift: false, meta: true}}} @@ -608,11 +603,11 @@ describe('Mouse Tracking Tests', function(): void { await wheelDown(); await page.keyboard.up('Shift'); if (noShift) { - assert.deepEqual(await getReports(encoding), [ + await pollFor(page, () => getReports(encoding), [ {col: 45, row: 25, state: {action: 'down', button: 'wheel', modifier: {control: false, shift: true, meta: false}}} ]); } else { - assert.deepEqual(await getReports(encoding), [ + await pollFor(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}}} @@ -633,7 +628,7 @@ describe('Mouse Tracking Tests', function(): void { await page.keyboard.up('Control'); await page.keyboard.up('Alt'); // await page.keyboard.up('Shift'); - assert.deepEqual(await getReports(encoding), [ + await pollFor(page, () => getReports(encoding), [ {col: 44, row: 25, state: {action: 'press', button: 'left', modifier: {control: true, shift: false, meta: true}}}, {col: 45, row: 25, state: {action: 'release', button: '', modifier: {control: true, shift: false, meta: true}}}, {col: 45, row: 25, state: {action: 'down', button: 'wheel', modifier: {control: true, shift: false, meta: true}}} @@ -647,22 +642,22 @@ describe('Mouse Tracking Tests', function(): void { // test at 0,0 await mouseDown('left'); - assert.deepEqual(await getReports(encoding), [ + await pollFor(page, () => getReports(encoding), [ {col: 1, row: 1, state: {action: 'press', button: 'left', modifier: {control: false, shift: false, meta: false}}} ]); // mouseup should report await mouseUp('left'); - assert.deepEqual(await getReports(encoding), [ + await pollFor(page, () => getReports(encoding), [ {col: 1, row: 1, state: {action: 'release', button: 'left', modifier: {control: false, shift: false, meta: false}}} ]); // mousemove should not report await mouseMove(50, 10); - assert.deepEqual(await getReports(encoding), []); + await pollFor(page, () => getReports(encoding), []); await mouseDown('left'); await mouseUp('left'); - assert.deepEqual(await getReports(encoding), [ + await pollFor(page, () => getReports(encoding), [ {col: 51, row: 11, state: {action: 'press', button: 'left', modifier: {control: false, shift: false, meta: false}}}, {col: 51, row: 11, state: {action: 'release', button: 'left', modifier: {control: false, shift: false, meta: false}}} ]); @@ -671,7 +666,7 @@ describe('Mouse Tracking Tests', function(): void { await mouseMove(cols - 1, rows - 1); await mouseDown('left'); await mouseUp('left'); - assert.deepEqual(await getReports(encoding), [ + await pollFor(page, () => getReports(encoding), [ {col: cols, row: rows, state: {action: 'press', button: 'left', modifier: {control: false, shift: false, meta: false}}}, {col: cols, row: rows, state: {action: 'release', button: 'left', modifier: {control: false, shift: false, meta: false}}} ]); @@ -683,7 +678,7 @@ describe('Mouse Tracking Tests', function(): void { await mouseDown('left'); await mouseMove(44, 24); await mouseUp('left'); - assert.deepEqual(await getReports(encoding), [ + await pollFor(page, () => getReports(encoding), [ {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}}} ]); @@ -694,7 +689,7 @@ describe('Mouse Tracking Tests', function(): void { // await mouseDown('middle'); // await mouseMove(44, 24); // await mouseUp('middle'); - // assert.deepEqual(await getReports(encoding), [{col: 44, row: 25, state: {action: 'press', button: 'middle', modifier: {control: false, shift: false, meta: false}}}]); + // await pollFor(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); @@ -702,7 +697,7 @@ describe('Mouse Tracking Tests', function(): void { await mouseDown('right'); await mouseMove(44, 24); await mouseUp('right'); - assert.deepEqual(await getReports(encoding), [ + await pollFor(page, () => getReports(encoding), [ {col: 44, row: 25, state: {action: 'press', button: 'right', modifier: {control: false, shift: false, meta: false}}}, {col: 45, row: 25, state: {action: 'release', button: 'right', modifier: {control: false, shift: false, meta: false}}} ]); @@ -711,9 +706,9 @@ describe('Mouse Tracking Tests', function(): void { await mouseMove(43, 24); await getReports(encoding); // clear reports await wheelUp(); - assert.deepEqual(await getReports(encoding), [{col: 44, row: 25, state: {action: 'up', button: 'wheel', modifier: {control: false, shift: false, meta: false}}}]); + await pollFor(page, () => getReports(encoding), [{col: 44, row: 25, state: {action: 'up', button: 'wheel', modifier: {control: false, shift: false, meta: false}}}]); await wheelDown(); - assert.deepEqual(await getReports(encoding), [{col: 44, row: 25, state: {action: 'down', button: 'wheel', modifier: {control: false, shift: false, meta: false}}}]); + await pollFor(page, () => getReports(encoding), [{col: 44, row: 25, state: {action: 'down', button: 'wheel', modifier: {control: false, shift: false, meta: false}}}]); // modifiers // CTRL @@ -725,7 +720,7 @@ describe('Mouse Tracking Tests', function(): void { await mouseUp('left'); await wheelDown(); await page.keyboard.up('Control'); - assert.deepEqual(await getReports(encoding), [ + await pollFor(page, () => getReports(encoding), [ {col: 44, row: 25, state: {action: 'press', button: 'left', modifier: {control: true, shift: false, meta: false}}}, {col: 45, row: 25, state: {action: 'release', button: 'left', modifier: {control: true, shift: false, meta: false}}}, {col: 45, row: 25, state: {action: 'down', button: 'wheel', modifier: {control: true, shift: false, meta: false}}} @@ -740,7 +735,7 @@ describe('Mouse Tracking Tests', function(): void { await mouseUp('left'); await wheelDown(); await page.keyboard.up('Alt'); - assert.deepEqual(await getReports(encoding), [ + await pollFor(page, () => getReports(encoding), [ {col: 44, row: 25, state: {action: 'press', button: 'left', modifier: {control: false, shift: false, meta: true}}}, {col: 45, row: 25, state: {action: 'release', button: 'left', modifier: {control: false, shift: false, meta: true}}}, {col: 45, row: 25, state: {action: 'down', button: 'wheel', modifier: {control: false, shift: false, meta: true}}} @@ -757,11 +752,11 @@ describe('Mouse Tracking Tests', function(): void { await wheelDown(); await page.keyboard.up('Shift'); if (noShift) { - assert.deepEqual(await getReports(encoding), [ + await pollFor(page, () => getReports(encoding), [ {col: 45, row: 25, state: {action: 'down', button: 'wheel', modifier: {control: false, shift: true, meta: false}}} ]); } else { - assert.deepEqual(await getReports(encoding), [ + await pollFor(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}}} @@ -782,7 +777,7 @@ describe('Mouse Tracking Tests', function(): void { await page.keyboard.up('Control'); await page.keyboard.up('Alt'); // await page.keyboard.up('Shift'); - assert.deepEqual(await getReports(encoding), [ + await pollFor(page, () => getReports(encoding), [ {col: 44, row: 25, state: {action: 'press', button: 'left', modifier: {control: true, shift: false, meta: true}}}, {col: 45, row: 25, state: {action: 'release', button: 'left', modifier: {control: true, shift: false, meta: true}}}, {col: 45, row: 25, state: {action: 'down', button: 'wheel', modifier: {control: true, shift: false, meta: true}}} @@ -805,22 +800,22 @@ describe('Mouse Tracking Tests', function(): void { // test at 0,0 await mouseDown('left'); - assert.deepEqual(await getReports(encoding), [ + await pollFor(page, () => getReports(encoding), [ {col: 1, row: 1, state: {action: 'press', button: 'left', modifier: {control: false, shift: false, meta: false}}} ]); // mouseup should report, encoding cannot report released button await mouseUp('left'); - assert.deepEqual(await getReports(encoding), [ + await pollFor(page, () => getReports(encoding), [ {col: 1, row: 1, state: {action: 'release', button: '', modifier: {control: false, shift: false, meta: false}}} ]); // mousemove should not report await mouseMove(50, 10); - assert.deepEqual(await getReports(encoding), []); + await pollFor(page, () => getReports(encoding), []); await mouseDown('left'); await mouseUp('left'); - assert.deepEqual(await getReports(encoding), [ + await pollFor(page, () => getReports(encoding), [ {col: 51, row: 11, state: {action: 'press', button: 'left', modifier: {control: false, shift: false, meta: false}}}, {col: 51, row: 11, state: {action: 'release', button: '', modifier: {control: false, shift: false, meta: false}}} ]); @@ -831,7 +826,7 @@ describe('Mouse Tracking Tests', function(): void { await mouseMove(cols - 1, rows - 1); await mouseDown('left'); await mouseUp('left'); - assert.deepEqual(await getReports(encoding), [ + await pollFor(page, () => getReports(encoding), [ {col: 95, row: rows, state: {action: 'press', button: 'left', modifier: {control: false, shift: false, meta: false}}}, {col: 95, row: rows, state: {action: 'release', button: '', modifier: {control: false, shift: false, meta: false}}} ]); @@ -843,7 +838,7 @@ describe('Mouse Tracking Tests', function(): void { await mouseDown('left'); await mouseMove(44, 24); await mouseUp('left'); - assert.deepEqual(await getReports(encoding), [ + await pollFor(page, () => getReports(encoding), [ {col: 44, row: 25, state: {action: 'press', button: 'left', modifier: {control: false, shift: false, meta: false}}}, {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}}} @@ -855,7 +850,7 @@ describe('Mouse Tracking Tests', function(): void { // await mouseDown('middle'); // await mouseMove(44, 24); // await mouseUp('middle'); - // assert.deepEqual(await getReports(encoding), [{col: 44, row: 25, state: {action: 'press', button: 'middle', modifier: {control: false, shift: false, meta: false}}}]); + // await pollFor(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); @@ -863,7 +858,7 @@ describe('Mouse Tracking Tests', function(): void { await mouseDown('right'); await mouseMove(44, 24); await mouseUp('right'); - assert.deepEqual(await getReports(encoding), [ + await pollFor(page, () => getReports(encoding), [ {col: 44, row: 25, state: {action: 'press', button: 'right', modifier: {control: false, shift: false, meta: false}}}, {col: 45, row: 25, state: {action: 'move', button: 'right', modifier: {control: false, shift: false, meta: false}}}, {col: 45, row: 25, state: {action: 'release', button: '', modifier: {control: false, shift: false, meta: false}}} @@ -873,9 +868,9 @@ describe('Mouse Tracking Tests', function(): void { await mouseMove(43, 24); await getReports(encoding); // clear reports await wheelUp(); - assert.deepEqual(await getReports(encoding), [{col: 44, row: 25, state: {action: 'up', button: 'wheel', modifier: {control: false, shift: false, meta: false}}}]); + await pollFor(page, () => getReports(encoding), [{col: 44, row: 25, state: {action: 'up', button: 'wheel', modifier: {control: false, shift: false, meta: false}}}]); await wheelDown(); - assert.deepEqual(await getReports(encoding), [{col: 44, row: 25, state: {action: 'down', button: 'wheel', modifier: {control: false, shift: false, meta: false}}}]); + await pollFor(page, () => getReports(encoding), [{col: 44, row: 25, state: {action: 'down', button: 'wheel', modifier: {control: false, shift: false, meta: false}}}]); // modifiers // CTRL @@ -887,7 +882,7 @@ describe('Mouse Tracking Tests', function(): void { await mouseUp('left'); await wheelDown(); await page.keyboard.up('Control'); - assert.deepEqual(await getReports(encoding), [ + await pollFor(page, () => getReports(encoding), [ {col: 44, row: 25, state: {action: 'press', button: 'left', modifier: {control: true, shift: false, meta: false}}}, {col: 45, row: 25, state: {action: 'move', button: 'left', modifier: {control: true, shift: false, meta: false}}}, {col: 45, row: 25, state: {action: 'release', button: '', modifier: {control: true, shift: false, meta: false}}}, @@ -903,7 +898,7 @@ describe('Mouse Tracking Tests', function(): void { await mouseUp('left'); await wheelDown(); await page.keyboard.up('Alt'); - assert.deepEqual(await getReports(encoding), [ + await pollFor(page, () => getReports(encoding), [ {col: 44, row: 25, state: {action: 'press', button: 'left', modifier: {control: false, shift: false, meta: true}}}, {col: 45, row: 25, state: {action: 'move', button: 'left', modifier: {control: false, shift: false, meta: true}}}, {col: 45, row: 25, state: {action: 'release', button: '', modifier: {control: false, shift: false, meta: true}}}, @@ -921,11 +916,11 @@ describe('Mouse Tracking Tests', function(): void { await wheelDown(); await page.keyboard.up('Shift'); if (noShift) { - assert.deepEqual(await getReports(encoding), [ + await pollFor(page, () => getReports(encoding), [ {col: 45, row: 25, state: {action: 'down', button: 'wheel', modifier: {control: false, shift: true, meta: false}}} ]); } else { - assert.deepEqual(await getReports(encoding), [ + await pollFor(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}}}, @@ -947,7 +942,7 @@ describe('Mouse Tracking Tests', function(): void { await page.keyboard.up('Control'); await page.keyboard.up('Alt'); // await page.keyboard.up('Shift'); - assert.deepEqual(await getReports(encoding), [ + await pollFor(page, () => getReports(encoding), [ {col: 44, row: 25, state: {action: 'press', button: 'left', modifier: {control: true, shift: false, meta: true}}}, {col: 45, row: 25, state: {action: 'move', button: 'left', modifier: {control: true, shift: false, meta: true}}}, {col: 45, row: 25, state: {action: 'release', button: '', modifier: {control: true, shift: false, meta: true}}}, @@ -963,22 +958,22 @@ describe('Mouse Tracking Tests', function(): void { // test at 0,0 // bug: release is fired immediately await mouseDown('left'); - assert.deepEqual(await getReports(encoding), [ + await pollFor(page, () => getReports(encoding), [ {col: 1, row: 1, state: {action: 'press', button: 'left', modifier: {control: false, shift: false, meta: false}}} ]); // mouseup should report, encoding cannot report released button await mouseUp('left'); - assert.deepEqual(await getReports(encoding), [ + await pollFor(page, () => getReports(encoding), [ {col: 1, row: 1, state: {action: 'release', button: 'left', modifier: {control: false, shift: false, meta: false}}} ]); // mousemove should not report await mouseMove(50, 10); - assert.deepEqual(await getReports(encoding), []); + await pollFor(page, () => getReports(encoding), []); await mouseDown('left'); await mouseUp('left'); - assert.deepEqual(await getReports(encoding), [ + await pollFor(page, () => getReports(encoding), [ {col: 51, row: 11, state: {action: 'press', button: 'left', modifier: {control: false, shift: false, meta: false}}}, {col: 51, row: 11, state: {action: 'release', button: 'left', modifier: {control: false, shift: false, meta: false}}} ]); @@ -987,7 +982,7 @@ describe('Mouse Tracking Tests', function(): void { await mouseMove(cols - 1, rows - 1); await mouseDown('left'); await mouseUp('left'); - assert.deepEqual(await getReports(encoding), [ + await pollFor(page, () => getReports(encoding), [ {col: cols, row: rows, state: {action: 'press', button: 'left', modifier: {control: false, shift: false, meta: false}}}, {col: cols, row: rows, state: {action: 'release', button: 'left', modifier: {control: false, shift: false, meta: false}}} ]); @@ -999,7 +994,7 @@ describe('Mouse Tracking Tests', function(): void { await mouseDown('left'); await mouseMove(44, 24); await mouseUp('left'); - assert.deepEqual(await getReports(encoding), [ + await pollFor(page, () => getReports(encoding), [ {col: 44, row: 25, state: {action: 'press', button: 'left', modifier: {control: false, shift: false, meta: false}}}, {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}}} @@ -1011,7 +1006,7 @@ describe('Mouse Tracking Tests', function(): void { // await mouseDown('middle'); // await mouseMove(44, 24); // await mouseUp('middle'); - // assert.deepEqual(await getReports(encoding), [{col: 44, row: 25, state: {action: 'press', button: 'middle', modifier: {control: false, shift: false, meta: false}}}]); + // await pollFor(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); @@ -1019,7 +1014,7 @@ describe('Mouse Tracking Tests', function(): void { await mouseDown('right'); await mouseMove(44, 24); await mouseUp('right'); - assert.deepEqual(await getReports(encoding), [ + await pollFor(page, () => getReports(encoding), [ {col: 44, row: 25, state: {action: 'press', button: 'right', modifier: {control: false, shift: false, meta: false}}}, {col: 45, row: 25, state: {action: 'move', button: 'right', modifier: {control: false, shift: false, meta: false}}}, {col: 45, row: 25, state: {action: 'release', button: 'right', modifier: {control: false, shift: false, meta: false}}} @@ -1029,9 +1024,9 @@ describe('Mouse Tracking Tests', function(): void { await mouseMove(43, 24); await getReports(encoding); // clear reports await wheelUp(); - assert.deepEqual(await getReports(encoding), [{col: 44, row: 25, state: {action: 'up', button: 'wheel', modifier: {control: false, shift: false, meta: false}}}]); + await pollFor(page, () => getReports(encoding), [{col: 44, row: 25, state: {action: 'up', button: 'wheel', modifier: {control: false, shift: false, meta: false}}}]); await wheelDown(); - assert.deepEqual(await getReports(encoding), [{col: 44, row: 25, state: {action: 'down', button: 'wheel', modifier: {control: false, shift: false, meta: false}}}]); + await pollFor(page, () => getReports(encoding), [{col: 44, row: 25, state: {action: 'down', button: 'wheel', modifier: {control: false, shift: false, meta: false}}}]); // modifiers // CTRL @@ -1043,7 +1038,7 @@ describe('Mouse Tracking Tests', function(): void { await mouseUp('left'); await wheelDown(); await page.keyboard.up('Control'); - assert.deepEqual(await getReports(encoding), [ + await pollFor(page, () => getReports(encoding), [ {col: 44, row: 25, state: {action: 'press', button: 'left', modifier: {control: true, shift: false, meta: false}}}, {col: 45, row: 25, state: {action: 'move', button: 'left', modifier: {control: true, shift: false, meta: false}}}, {col: 45, row: 25, state: {action: 'release', button: 'left', modifier: {control: true, shift: false, meta: false}}}, @@ -1059,7 +1054,7 @@ describe('Mouse Tracking Tests', function(): void { await mouseUp('left'); await wheelDown(); await page.keyboard.up('Alt'); - assert.deepEqual(await getReports(encoding), [ + await pollFor(page, () => getReports(encoding), [ {col: 44, row: 25, state: {action: 'press', button: 'left', modifier: {control: false, shift: false, meta: true}}}, {col: 45, row: 25, state: {action: 'move', button: 'left', modifier: {control: false, shift: false, meta: true}}}, {col: 45, row: 25, state: {action: 'release', button: 'left', modifier: {control: false, shift: false, meta: true}}}, @@ -1077,11 +1072,11 @@ describe('Mouse Tracking Tests', function(): void { await wheelDown(); await page.keyboard.up('Shift'); if (noShift) { - assert.deepEqual(await getReports(encoding), [ + await pollFor(page, () => getReports(encoding), [ {col: 45, row: 25, state: {action: 'down', button: 'wheel', modifier: {control: false, shift: true, meta: false}}} ]); } else { - assert.deepEqual(await getReports(encoding), [ + await pollFor(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}}}, @@ -1103,7 +1098,7 @@ describe('Mouse Tracking Tests', function(): void { await page.keyboard.up('Control'); await page.keyboard.up('Alt'); // await page.keyboard.up('Shift'); - assert.deepEqual(await getReports(encoding), [ + await pollFor(page, () => getReports(encoding), [ {col: 44, row: 25, state: {action: 'press', button: 'left', modifier: {control: true, shift: false, meta: true}}}, {col: 45, row: 25, state: {action: 'move', button: 'left', modifier: {control: true, shift: false, meta: true}}}, {col: 45, row: 25, state: {action: 'release', button: 'left', modifier: {control: true, shift: false, meta: true}}}, @@ -1124,24 +1119,24 @@ describe('Mouse Tracking Tests', function(): void { // test at 0,0 await mouseDown('left'); - assert.deepEqual(await getReports(encoding), [ + await pollFor(page, () => getReports(encoding), [ {col: 1, row: 1, state: {action: 'press', button: 'left', modifier: {control: false, shift: false, meta: false}}} ]); // mouseup should report, encoding cannot report released button await mouseUp('left'); - assert.deepEqual(await getReports(encoding), [ + await pollFor(page, () => getReports(encoding), [ {col: 1, row: 1, state: {action: 'release', button: '', modifier: {control: false, shift: false, meta: false}}} ]); // mousemove should report await mouseMove(50, 10); - assert.deepEqual(await getReports(encoding), [ + await pollFor(page, () => getReports(encoding), [ {col: 51, row: 11, state: {action: 'move', button: '', modifier: {control: false, shift: false, meta: false}}} ]); await mouseDown('left'); await mouseUp('left'); - assert.deepEqual(await getReports(encoding), [ + await pollFor(page, () => getReports(encoding), [ {col: 51, row: 11, state: {action: 'press', button: 'left', modifier: {control: false, shift: false, meta: false}}}, {col: 51, row: 11, state: {action: 'release', button: '', modifier: {control: false, shift: false, meta: false}}} ]); @@ -1152,7 +1147,7 @@ describe('Mouse Tracking Tests', function(): void { await mouseMove(cols - 1, rows - 1); await mouseDown('left'); await mouseUp('left'); - assert.deepEqual(await getReports(encoding), [ + await pollFor(page, () => getReports(encoding), [ {col: 95, row: rows, state: {action: 'move', button: '', modifier: {control: false, shift: false, meta: false}}}, {col: 95, row: rows, state: {action: 'press', button: 'left', modifier: {control: false, shift: false, meta: false}}}, {col: 95, row: rows, state: {action: 'release', button: '', modifier: {control: false, shift: false, meta: false}}} @@ -1164,7 +1159,7 @@ describe('Mouse Tracking Tests', function(): void { await mouseDown('left'); await mouseMove(44, 24); await mouseUp('left'); - assert.deepEqual(await getReports(encoding), [ + await pollFor(page, () => getReports(encoding), [ {col: 44, row: 25, state: {action: 'move', button: '', modifier: {control: false, shift: false, meta: false}}}, {col: 44, row: 25, state: {action: 'press', button: 'left', modifier: {control: false, shift: false, meta: false}}}, {col: 45, row: 25, state: {action: 'move', button: 'left', modifier: {control: false, shift: false, meta: false}}}, @@ -1177,14 +1172,14 @@ describe('Mouse Tracking Tests', function(): void { // await mouseDown('middle'); // await mouseMove(44, 24); // await mouseUp('middle'); - // assert.deepEqual(await getReports(encoding), [{col: 44, row: 25, state: {action: 'press', button: 'middle', modifier: {control: false, shift: false, meta: false}}}]); + // await pollFor(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); await mouseUp('right'); - assert.deepEqual(await getReports(encoding), [ + await pollFor(page, () => getReports(encoding), [ {col: 44, row: 25, state: {action: 'move', button: '', modifier: {control: false, shift: false, meta: false}}}, {col: 44, row: 25, state: {action: 'press', button: 'right', modifier: {control: false, shift: false, meta: false}}}, {col: 45, row: 25, state: {action: 'move', button: 'right', modifier: {control: false, shift: false, meta: false}}}, @@ -1195,9 +1190,9 @@ describe('Mouse Tracking Tests', function(): void { await mouseMove(43, 24); await getReports(encoding); // clear reports await wheelUp(); - assert.deepEqual(await getReports(encoding), [{col: 44, row: 25, state: {action: 'up', button: 'wheel', modifier: {control: false, shift: false, meta: false}}}]); + await pollFor(page, () => getReports(encoding), [{col: 44, row: 25, state: {action: 'up', button: 'wheel', modifier: {control: false, shift: false, meta: false}}}]); await wheelDown(); - assert.deepEqual(await getReports(encoding), [{col: 44, row: 25, state: {action: 'down', button: 'wheel', modifier: {control: false, shift: false, meta: false}}}]); + await pollFor(page, () => getReports(encoding), [{col: 44, row: 25, state: {action: 'down', button: 'wheel', modifier: {control: false, shift: false, meta: false}}}]); // modifiers // CTRL @@ -1208,7 +1203,7 @@ describe('Mouse Tracking Tests', function(): void { await mouseUp('left'); await wheelDown(); await page.keyboard.up('Control'); - assert.deepEqual(await getReports(encoding), [ + await pollFor(page, () => getReports(encoding), [ {col: 44, row: 25, state: {action: 'move', button: '', modifier: {control: true, shift: false, meta: false}}}, {col: 44, row: 25, state: {action: 'press', button: 'left', modifier: {control: true, shift: false, meta: false}}}, {col: 45, row: 25, state: {action: 'move', button: 'left', modifier: {control: true, shift: false, meta: false}}}, @@ -1224,7 +1219,7 @@ describe('Mouse Tracking Tests', function(): void { await mouseUp('left'); await wheelDown(); await page.keyboard.up('Alt'); - assert.deepEqual(await getReports(encoding), [ + await pollFor(page, () => getReports(encoding), [ {col: 44, row: 25, state: {action: 'move', button: '', modifier: {control: false, shift: false, meta: true}}}, {col: 44, row: 25, state: {action: 'press', button: 'left', modifier: {control: false, shift: false, meta: true}}}, {col: 45, row: 25, state: {action: 'move', button: 'left', modifier: {control: false, shift: false, meta: true}}}, @@ -1242,12 +1237,12 @@ describe('Mouse Tracking Tests', function(): void { await wheelDown(); await page.keyboard.up('Shift'); if (noShift) { - assert.deepEqual(await getReports(encoding), [ + await pollFor(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 { - assert.deepEqual(await getReports(encoding), [ + await pollFor(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}}}, @@ -1269,7 +1264,7 @@ describe('Mouse Tracking Tests', function(): void { await page.keyboard.up('Control'); await page.keyboard.up('Alt'); // await page.keyboard.up('Shift'); - assert.deepEqual(await getReports(encoding), [ + await pollFor(page, () => getReports(encoding), [ {col: 44, row: 25, state: {action: 'move', button: '', modifier: {control: true, shift: false, meta: true}}}, {col: 44, row: 25, state: {action: 'press', button: 'left', modifier: {control: true, shift: false, meta: true}}}, {col: 45, row: 25, state: {action: 'move', button: 'left', modifier: {control: true, shift: false, meta: true}}}, @@ -1285,24 +1280,24 @@ describe('Mouse Tracking Tests', function(): void { // test at 0,0 await mouseDown('left'); - assert.deepEqual(await getReports(encoding), [ + await pollFor(page, () => getReports(encoding), [ {col: 1, row: 1, state: {action: 'press', button: 'left', modifier: {control: false, shift: false, meta: false}}} ]); // mouseup should report, encoding cannot report released button await mouseUp('left'); - assert.deepEqual(await getReports(encoding), [ + await pollFor(page, () => getReports(encoding), [ {col: 1, row: 1, state: {action: 'release', button: 'left', modifier: {control: false, shift: false, meta: false}}} ]); // mousemove should report await mouseMove(50, 10); - assert.deepEqual(await getReports(encoding), [ + await pollFor(page, () => getReports(encoding), [ {col: 51, row: 11, state: {action: 'move', button: '', modifier: {control: false, shift: false, meta: false}}} ]); await mouseDown('left'); await mouseUp('left'); - assert.deepEqual(await getReports(encoding), [ + await pollFor(page, () => getReports(encoding), [ {col: 51, row: 11, state: {action: 'press', button: 'left', modifier: {control: false, shift: false, meta: false}}}, {col: 51, row: 11, state: {action: 'release', button: 'left', modifier: {control: false, shift: false, meta: false}}} ]); @@ -1313,7 +1308,7 @@ describe('Mouse Tracking Tests', function(): void { await mouseMove(cols - 1, rows - 1); await mouseDown('left'); await mouseUp('left'); - assert.deepEqual(await getReports(encoding), [ + await pollFor(page, () => getReports(encoding), [ {col: cols, row: rows, state: {action: 'move', button: '', modifier: {control: false, shift: false, meta: false}}}, {col: cols, row: rows, state: {action: 'press', button: 'left', modifier: {control: false, shift: false, meta: false}}}, {col: cols, row: rows, state: {action: 'release', button: 'left', modifier: {control: false, shift: false, meta: false}}} @@ -1325,7 +1320,7 @@ describe('Mouse Tracking Tests', function(): void { await mouseDown('left'); await mouseMove(44, 24); await mouseUp('left'); - assert.deepEqual(await getReports(encoding), [ + await pollFor(page, () => getReports(encoding), [ {col: 44, row: 25, state: {action: 'move', button: '', modifier: {control: false, shift: false, meta: false}}}, {col: 44, row: 25, state: {action: 'press', button: 'left', modifier: {control: false, shift: false, meta: false}}}, {col: 45, row: 25, state: {action: 'move', button: 'left', modifier: {control: false, shift: false, meta: false}}}, @@ -1338,14 +1333,14 @@ describe('Mouse Tracking Tests', function(): void { // await mouseDown('middle'); // await mouseMove(44, 24); // await mouseUp('middle'); - // assert.deepEqual(await getReports(encoding), [{col: 44, row: 25, state: {action: 'press', button: 'middle', modifier: {control: false, shift: false, meta: false}}}]); + // await pollFor(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); await mouseUp('right'); - assert.deepEqual(await getReports(encoding), [ + await pollFor(page, () => getReports(encoding), [ {col: 44, row: 25, state: {action: 'move', button: '', modifier: {control: false, shift: false, meta: false}}}, {col: 44, row: 25, state: {action: 'press', button: 'right', modifier: {control: false, shift: false, meta: false}}}, {col: 45, row: 25, state: {action: 'move', button: 'right', modifier: {control: false, shift: false, meta: false}}}, @@ -1356,9 +1351,9 @@ describe('Mouse Tracking Tests', function(): void { await mouseMove(43, 24); await getReports(encoding); // clear reports await wheelUp(); - assert.deepEqual(await getReports(encoding), [{col: 44, row: 25, state: {action: 'up', button: 'wheel', modifier: {control: false, shift: false, meta: false}}}]); + await pollFor(page, () => getReports(encoding), [{col: 44, row: 25, state: {action: 'up', button: 'wheel', modifier: {control: false, shift: false, meta: false}}}]); await wheelDown(); - assert.deepEqual(await getReports(encoding), [{col: 44, row: 25, state: {action: 'down', button: 'wheel', modifier: {control: false, shift: false, meta: false}}}]); + await pollFor(page, () => getReports(encoding), [{col: 44, row: 25, state: {action: 'down', button: 'wheel', modifier: {control: false, shift: false, meta: false}}}]); // modifiers // CTRL @@ -1369,7 +1364,7 @@ describe('Mouse Tracking Tests', function(): void { await mouseUp('left'); await wheelDown(); await page.keyboard.up('Control'); - assert.deepEqual(await getReports(encoding), [ + await pollFor(page, () => getReports(encoding), [ {col: 44, row: 25, state: {action: 'move', button: '', modifier: {control: true, shift: false, meta: false}}}, {col: 44, row: 25, state: {action: 'press', button: 'left', modifier: {control: true, shift: false, meta: false}}}, {col: 45, row: 25, state: {action: 'move', button: 'left', modifier: {control: true, shift: false, meta: false}}}, @@ -1385,7 +1380,7 @@ describe('Mouse Tracking Tests', function(): void { await mouseUp('left'); await wheelDown(); await page.keyboard.up('Alt'); - assert.deepEqual(await getReports(encoding), [ + await pollFor(page, () => getReports(encoding), [ {col: 44, row: 25, state: {action: 'move', button: '', modifier: {control: false, shift: false, meta: true}}}, {col: 44, row: 25, state: {action: 'press', button: 'left', modifier: {control: false, shift: false, meta: true}}}, {col: 45, row: 25, state: {action: 'move', button: 'left', modifier: {control: false, shift: false, meta: true}}}, @@ -1403,12 +1398,12 @@ describe('Mouse Tracking Tests', function(): void { await wheelDown(); await page.keyboard.up('Shift'); if (noShift) { - assert.deepEqual(await getReports(encoding), [ + await pollFor(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 { - assert.deepEqual(await getReports(encoding), [ + await pollFor(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}}}, @@ -1430,7 +1425,7 @@ describe('Mouse Tracking Tests', function(): void { await page.keyboard.up('Control'); await page.keyboard.up('Alt'); // await page.keyboard.up('Shift'); - assert.deepEqual(await getReports(encoding), [ + await pollFor(page, () => getReports(encoding), [ {col: 44, row: 25, state: {action: 'move', button: '', modifier: {control: true, shift: false, meta: true}}}, {col: 44, row: 25, state: {action: 'press', button: 'left', modifier: {control: true, shift: false, meta: true}}}, {col: 45, row: 25, state: {action: 'move', button: 'left', modifier: {control: true, shift: false, meta: true}}}, From 34e703e4725b06179ec0d69d8258513f5e674ad3 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sat, 9 Nov 2019 17:29:32 -0800 Subject: [PATCH 56/92] Fix most of Terminal.api timing issues --- package.json | 2 + test/api/MouseTracking.api.ts | 4 +- test/api/Parser.api.ts | 7 +- test/api/Terminal.api.ts | 215 +++++++++++++++++----------------- test/api/TestUtils.ts | 17 +-- yarn.lock | 36 +++++- 6 files changed, 156 insertions(+), 125 deletions(-) diff --git a/package.json b/package.json index 7ed71741..c37594af 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ }, "devDependencies": { "@types/chai": "^3.4.34", + "@types/deep-equal": "^1.0.1", "@types/glob": "^5.0.35", "@types/jsdom": "11.0.1", "@types/mocha": "^2.2.33", @@ -39,6 +40,7 @@ "@types/webpack": "^4.4.11", "@types/ws": "^6.0.1", "chai": "3.5.0", + "deep-equal": "^1.1.0", "express": "^4.17.1", "express-ws": "^4.0.0", "glob": "^7.0.5", diff --git a/test/api/MouseTracking.api.ts b/test/api/MouseTracking.api.ts index 910763cd..d10a5b95 100644 --- a/test/api/MouseTracking.api.ts +++ b/test/api/MouseTracking.api.ts @@ -198,8 +198,8 @@ function parseReport(encoding: string, msg: number[]): {state: any; row: number; /** * Mouse tracking tests. */ -describe('Mouse Tracking Tests', function(): void { - before(async function(): Promise { +describe('Mouse Tracking Tests', () => { + before(async () => { browser = await puppeteer.launch({ headless: process.argv.indexOf('--headless') !== -1, args: [`--window-size=${width},${height}`, `--no-sandbox`] diff --git a/test/api/Parser.api.ts b/test/api/Parser.api.ts index 4dcda70c..9b0fb66b 100644 --- a/test/api/Parser.api.ts +++ b/test/api/Parser.api.ts @@ -15,12 +15,9 @@ const width = 800; const height = 600; describe('Parser Integration Tests', function(): void { - this.timeout(20000); - before(async function(): Promise { browser = await puppeteer.launch({ headless: process.argv.indexOf('--headless') !== -1, - slowMo: 0, args: [`--window-size=${width},${height}`, `--no-sandbox`] }); page = (await browser.pages())[0]; @@ -29,9 +26,7 @@ describe('Parser Integration Tests', function(): void { await openTerminal(); }); - after(() => { - browser.close(); - }); + after(async () => browser.close()); describe('addCsiHandler', () => { it('should call custom CSI handler with js array params', async () => { diff --git a/test/api/Terminal.api.ts b/test/api/Terminal.api.ts index 07dc372c..6d7a4d20 100644 --- a/test/api/Terminal.api.ts +++ b/test/api/Terminal.api.ts @@ -6,6 +6,7 @@ import * as puppeteer from 'puppeteer'; import { assert } from 'chai'; import { ITerminalOptions } from 'xterm'; +import { pollFor, writeSync } from './TestUtils'; const APP = 'http://127.0.0.1:3000/test'; @@ -14,55 +15,47 @@ let page: puppeteer.Page; const width = 800; const height = 600; -describe('API Integration Tests', function(): void { - this.timeout(20000); - - before(async function(): Promise { +describe.only('API Integration Tests', function(): void { + before(async () => { browser = await puppeteer.launch({ headless: process.argv.indexOf('--headless') !== -1, - slowMo: 0, args: [`--window-size=${width},${height}`, `--no-sandbox`] }); page = (await browser.pages())[0]; await page.setViewport({ width, height }); }); - after(() => { - browser.close(); - }); + after(async () => browser.close()); + beforeEach(async () => page.goto(APP)); - beforeEach(async () => { - await page.goto(APP); - }); - - it('Default options', async function(): Promise { + it('Default options', async () => { await openTerminal(); assert.equal(await page.evaluate(`window.term.cols`), 80); assert.equal(await page.evaluate(`window.term.rows`), 24); }); - it('write', async function(): Promise { + it('write', async () => { await openTerminal(); await page.evaluate(` window.term.write('foo'); window.term.write('bar'); window.term.write('文'); `); - assert.equal(await page.evaluate(`window.term.buffer.getLine(0).translateToString(true)`), 'foobar文'); + await pollFor(page, `window.term.buffer.getLine(0).translateToString(true)`, 'foobar文'); }); - it('write with callback', async function(): Promise { + it('write with callback', async () => { await openTerminal(); await page.evaluate(` window.term.write('foo', () => { window.__x = 'a'; }); window.term.write('bar', () => { window.__x += 'b'; }); window.term.write('文', () => { window.__x += 'c'; }); `); - assert.equal(await page.evaluate(`window.term.buffer.getLine(0).translateToString(true)`), 'foobar文'); - assert.equal(await page.evaluate(`window.__x`), 'abc'); + await pollFor(page, `window.term.buffer.getLine(0).translateToString(true)`, 'foobar文'); + await pollFor(page, `window.__x`, 'abc'); }); - it('write - bytes (UTF8)', async function(): Promise { + it('write - bytes (UTF8)', async () => { await openTerminal(); await page.evaluate(` // foo @@ -72,10 +65,10 @@ describe('API Integration Tests', function(): void { // 文 window.term.write(new Uint8Array([230, 150, 135])); `); - assert.equal(await page.evaluate(`window.term.buffer.getLine(0).translateToString(true)`), 'foobar文'); + await pollFor(page, `window.term.buffer.getLine(0).translateToString(true)`, 'foobar文'); }); - it('write - bytes (UTF8) with callback', async function(): Promise { + it('write - bytes (UTF8) with callback', async () => { await openTerminal(); await page.evaluate(` // foo @@ -85,48 +78,48 @@ describe('API Integration Tests', function(): void { // 文 window.term.write(new Uint8Array([230, 150, 135]), () => { window.__x += 'C'; }); `); - assert.equal(await page.evaluate(`window.term.buffer.getLine(0).translateToString(true)`), 'foobar文'); - assert.equal(await page.evaluate(`window.__x`), 'ABC'); + await pollFor(page, `window.term.buffer.getLine(0).translateToString(true)`, 'foobar文'); + await pollFor(page, `window.__x`, 'ABC'); }); - it('writeln', async function(): Promise { + it('writeln', async () => { await openTerminal(); await page.evaluate(` window.term.writeln('foo'); window.term.writeln('bar'); window.term.writeln('文'); `); - assert.equal(await page.evaluate(`window.term.buffer.getLine(0).translateToString(true)`), 'foo'); - assert.equal(await page.evaluate(`window.term.buffer.getLine(1).translateToString(true)`), 'bar'); - assert.equal(await page.evaluate(`window.term.buffer.getLine(2).translateToString(true)`), '文'); + await pollFor(page, `window.term.buffer.getLine(0).translateToString(true)`, 'foo'); + await pollFor(page, `window.term.buffer.getLine(1).translateToString(true)`, 'bar'); + await pollFor(page, `window.term.buffer.getLine(2).translateToString(true)`, '文'); }); - it('writeln with callback', async function(): Promise { + it('writeln with callback', async () => { await openTerminal(); await page.evaluate(` window.term.writeln('foo', () => { window.__x = '1'; }); window.term.writeln('bar', () => { window.__x += '2'; }); window.term.writeln('文', () => { window.__x += '3'; }); `); - assert.equal(await page.evaluate(`window.term.buffer.getLine(0).translateToString(true)`), 'foo'); - assert.equal(await page.evaluate(`window.term.buffer.getLine(1).translateToString(true)`), 'bar'); - assert.equal(await page.evaluate(`window.term.buffer.getLine(2).translateToString(true)`), '文'); - assert.equal(await page.evaluate(`window.__x`), '123'); + await pollFor(page, `window.term.buffer.getLine(0).translateToString(true)`, 'foo'); + await pollFor(page, `window.term.buffer.getLine(1).translateToString(true)`, 'bar'); + await pollFor(page, `window.term.buffer.getLine(2).translateToString(true)`, '文'); + await pollFor(page, `window.__x`, '123'); }); - it('writeln - bytes (UTF8)', async function(): Promise { + it('writeln - bytes (UTF8)', async () => { await openTerminal(); await page.evaluate(` window.term.writeln(new Uint8Array([102, 111, 111])); window.term.writeln(new Uint8Array([98, 97, 114])); window.term.writeln(new Uint8Array([230, 150, 135])); `); - assert.equal(await page.evaluate(`window.term.buffer.getLine(0).translateToString(true)`), 'foo'); - assert.equal(await page.evaluate(`window.term.buffer.getLine(1).translateToString(true)`), 'bar'); - assert.equal(await page.evaluate(`window.term.buffer.getLine(2).translateToString(true)`), '文'); + await pollFor(page, `window.term.buffer.getLine(0).translateToString(true)`, 'foo'); + await pollFor(page, `window.term.buffer.getLine(1).translateToString(true)`, 'bar'); + await pollFor(page, `window.term.buffer.getLine(2).translateToString(true)`, '文'); }); - it('paste', async function(): Promise { + it('paste', async () => { await openTerminal(); await page.evaluate(` window.calls = []; @@ -137,26 +130,28 @@ describe('API Integration Tests', function(): void { window.term.paste('foo'); }); `); - assert.deepEqual(await page.evaluate(`window.calls`), ['foo', '\rfoo\rbar\r', '\x1b[200~foo\x1b[201~']); + await pollFor(page, `window.calls`, ['foo', '\rfoo\rbar\r', '\x1b[200~foo\x1b[201~']); }); - it('clear', async function(): Promise { + it('clear', async () => { await openTerminal({ rows: 5 }); await page.evaluate(` window.term.write('test0'); + window.parsed = 0; for (let i = 1; i < 10; i++) { - window.term.write('\\n\\rtest' + i); + window.term.write('\\n\\rtest' + i, () => window.parsed++); } `); + await pollFor(page, `window.parsed`, 9); await page.evaluate(`window.term.clear()`); - assert.equal(await page.evaluate(`window.term.buffer.length`), '5'); - assert.equal(await page.evaluate(`window.term.buffer.getLine(0).translateToString(true)`), 'test9'); + await pollFor(page, `window.term.buffer.length`, 5); + await pollFor(page, `window.term.buffer.getLine(0).translateToString(true)`, 'test9'); for (let i = 1; i < 5; i++) { - assert.equal(await page.evaluate(`window.term.buffer.getLine(${i}).translateToString(true)`), ''); + await pollFor(page, `window.term.buffer.getLine(${i}).translateToString(true)`, ''); } }); - it('getOption, setOption', async function(): Promise { + it('getOption, setOption', async () => { await openTerminal(); assert.equal(await page.evaluate(`window.term.getOption('rendererType')`), 'canvas'); await page.evaluate(`window.term.setOption('rendererType', 'dom')`); @@ -164,9 +159,10 @@ describe('API Integration Tests', function(): void { }); describe('renderer', () => { - it('foreground', async function(): Promise { + it('foreground', async () => { await openTerminal({ rendererType: 'dom' }); - await page.evaluate(`window.term.write('\\x1b[30m0\\x1b[31m1\\x1b[32m2\\x1b[33m3\\x1b[34m4\\x1b[35m5\\x1b[36m6\\x1b[37m7')`); + await writeSync(page, '\\x1b[30m0\\x1b[31m1\\x1b[32m2\\x1b[33m3\\x1b[34m4\\x1b[35m5\\x1b[36m6\\x1b[37m7'); + await pollFor(page, `!!document.querySelector('.xterm-rows > :nth-child(1) > :nth-child(1)')`, true); assert.deepEqual(await page.evaluate(` [ document.querySelector('.xterm-rows > :nth-child(1) > :nth-child(1)').className, @@ -188,9 +184,10 @@ describe('API Integration Tests', function(): void { ]); }); - it('background', async function(): Promise { + it('background', async () => { await openTerminal({ rendererType: 'dom' }); - await page.evaluate(`window.term.write('\\x1b[40m0\\x1b[41m1\\x1b[42m2\\x1b[43m3\\x1b[44m4\\x1b[45m5\\x1b[46m6\\x1b[47m7')`); + await writeSync(page, '\\x1b[40m0\\x1b[41m1\\x1b[42m2\\x1b[43m3\\x1b[44m4\\x1b[45m5\\x1b[46m6\\x1b[47m7'); + await pollFor(page, `!!document.querySelector('.xterm-rows > :nth-child(1) > :nth-child(1)')`, true); assert.deepEqual(await page.evaluate(` [ document.querySelector('.xterm-rows > :nth-child(1) > :nth-child(1)').className, @@ -213,7 +210,7 @@ describe('API Integration Tests', function(): void { }); }); - it('selection', async function(): Promise { + it('selection', async () => { await openTerminal({ rows: 5, cols: 5 }); await page.evaluate(`window.term.write('\\n\\nfoo\\n\\n\\rbar\\n\\n\\rbaz')`); assert.equal(await page.evaluate(`window.term.hasSelection()`), false); @@ -233,7 +230,7 @@ describe('API Integration Tests', function(): void { assert.deepEqual(await page.evaluate(`window.term.getSelectionPosition()`), { startColumn: 1, startRow: 2, endColumn: 3, endRow: 2 }); }); - it('focus, blur', async function(): Promise { + it('focus, blur', async () => { await openTerminal(); assert.equal(await page.evaluate(`document.activeElement.className`), ''); await page.evaluate(`window.term.focus()`); @@ -243,7 +240,7 @@ describe('API Integration Tests', function(): void { }); describe('loadAddon', () => { - it('constructor', async function(): Promise { + it('constructor', async () => { await openTerminal({ cols: 5 }); await page.evaluate(` window.cols = 0; @@ -255,7 +252,7 @@ describe('API Integration Tests', function(): void { assert.equal(await page.evaluate(`window.cols`), 5); }); - it('dispose (addon)', async function(): Promise { + it('dispose (addon)', async () => { await openTerminal(); await page.evaluate(` window.disposeCalled = false @@ -270,7 +267,7 @@ describe('API Integration Tests', function(): void { assert.equal(await page.evaluate(`window.disposeCalled`), true); }); - it('dispose (terminal)', async function(): Promise { + it('dispose (terminal)', async () => { await openTerminal(); await page.evaluate(` window.disposeCalled = false @@ -286,19 +283,19 @@ describe('API Integration Tests', function(): void { }); describe('Events', () => { - it('onCursorMove', async function(): Promise { + it('onCursorMove', async () => { await openTerminal(); await page.evaluate(` window.callCount = 0; window.term.onCursorMove(e => window.callCount++); window.term.write('foo'); `); - assert.equal(await page.evaluate(`window.callCount`), 1); + await pollFor(page, `window.callCount`, 1); await page.evaluate(`window.term.write('bar')`); - assert.equal(await page.evaluate(`window.callCount`), 2); + await pollFor(page, `window.callCount`, 2); }); - it('onData', async function(): Promise { + it('onData', async () => { await openTerminal(); await page.evaluate(` window.calls = []; @@ -308,7 +305,7 @@ describe('API Integration Tests', function(): void { assert.deepEqual(await page.evaluate(`window.calls`), ['f', 'o', 'o']); }); - it('onKey', async function(): Promise { + it('onKey', async () => { await openTerminal(); await page.evaluate(` window.calls = []; @@ -318,19 +315,19 @@ describe('API Integration Tests', function(): void { assert.deepEqual(await page.evaluate(`window.calls`), ['f', 'o', 'o']); }); - it('onLineFeed', async function(): Promise { + it('onLineFeed', async () => { await openTerminal(); await page.evaluate(` window.callCount = 0; window.term.onLineFeed(() => callCount++); window.term.writeln('foo'); `); - assert.equal(await page.evaluate(`window.callCount`), 1); + await pollFor(page, `window.callCount`, 1); await page.evaluate(`window.term.writeln('bar')`); - assert.equal(await page.evaluate(`window.callCount`), 2); + await pollFor(page, `window.callCount`, 2); }); - it('onScroll', async function(): Promise { + it('onScroll', async () => { await openTerminal({ rows: 5 }); await page.evaluate(` window.calls = []; @@ -339,94 +336,94 @@ describe('API Integration Tests', function(): void { window.term.writeln('foo'); } `); - assert.deepEqual(await page.evaluate(`window.calls`), []); + await pollFor(page, `window.calls`, []); await page.evaluate(`window.term.writeln('bar')`); - assert.deepEqual(await page.evaluate(`window.calls`), [1]); + await pollFor(page, `window.calls`, [1]); await page.evaluate(`window.term.writeln('baz')`); - assert.deepEqual(await page.evaluate(`window.calls`), [1, 2]); + await pollFor(page, `window.calls`, [1, 2]); }); - it('onSelectionChange', async function(): Promise { + it('onSelectionChange', async () => { await openTerminal(); await page.evaluate(` window.callCount = 0; window.term.onSelectionChange(() => window.callCount++); `); - assert.equal(await page.evaluate(`window.callCount`), 0); + await pollFor(page, `window.callCount`, 0); await page.evaluate(`window.term.selectAll()`); - assert.equal(await page.evaluate(`window.callCount`), 1); + await pollFor(page, `window.callCount`, 1); await page.evaluate(`window.term.clearSelection()`); - assert.equal(await page.evaluate(`window.callCount`), 2); + await pollFor(page, `window.callCount`, 2); }); - it('onRender', async function(): Promise { + it('onRender', async () => { await openTerminal(); await page.evaluate(` window.calls = []; window.term.onRender(e => window.calls.push([e.start, e.end])); `); - assert.deepEqual(await page.evaluate(`window.calls`), []); + await pollFor(page, `window.calls`, []); await page.evaluate(`window.term.write('foo')`); - assert.deepEqual(await page.evaluate(`window.calls`), [[0, 0]]); + await pollFor(page, `window.calls`, [[0, 0]]); await page.evaluate(`window.term.write('bar\\n\\nbaz')`); - assert.deepEqual(await page.evaluate(`window.calls`), [[0, 0], [0, 2]]); + await pollFor(page, `window.calls`, [[0, 0], [0, 2]]); }); - it('onResize', async function(): Promise { + it('onResize', async () => { await openTerminal(); await page.evaluate(` window.calls = []; window.term.onResize(e => window.calls.push([e.cols, e.rows])); `); - assert.deepEqual(await page.evaluate(`window.calls`), []); + await pollFor(page, `window.calls`, []); await page.evaluate(`window.term.resize(10, 5)`); - assert.deepEqual(await page.evaluate(`window.calls`), [[10, 5]]); + await pollFor(page, `window.calls`, [[10, 5]]); await page.evaluate(`window.term.resize(20, 15)`); - assert.deepEqual(await page.evaluate(`window.calls`), [[10, 5], [20, 15]]); + await pollFor(page, `window.calls`, [[10, 5], [20, 15]]); }); - it('onTitleChange', async function(): Promise { + it('onTitleChange', async () => { await openTerminal(); await page.evaluate(` window.calls = []; window.term.onTitleChange(e => window.calls.push(e)); `); - assert.deepEqual(await page.evaluate(`window.calls`), []); + await pollFor(page, `window.calls`, []); await page.evaluate(`window.term.write('\\x1b]2;foo\\x9c')`); - assert.deepEqual(await page.evaluate(`window.calls`), ['foo']); + await pollFor(page, `window.calls`, ['foo']); }); }); describe('buffer', () => { - it('cursorX, cursorY', async function(): Promise { + it('cursorX, cursorY', async () => { await openTerminal({ rows: 5, cols: 5 }); assert.equal(await page.evaluate(`window.term.buffer.cursorX`), 0); assert.equal(await page.evaluate(`window.term.buffer.cursorY`), 0); - await page.evaluate(`window.term.write('foo')`); + await writeSync(page, 'foo'); assert.equal(await page.evaluate(`window.term.buffer.cursorX`), 3); assert.equal(await page.evaluate(`window.term.buffer.cursorY`), 0); - await page.evaluate(`window.term.write('\\n')`); + await writeSync(page, '\\n'); assert.equal(await page.evaluate(`window.term.buffer.cursorX`), 3); assert.equal(await page.evaluate(`window.term.buffer.cursorY`), 1); - await page.evaluate(`window.term.write('\\r')`); + await writeSync(page, '\\r'); assert.equal(await page.evaluate(`window.term.buffer.cursorX`), 0); assert.equal(await page.evaluate(`window.term.buffer.cursorY`), 1); - await page.evaluate(`window.term.write('abcde')`); + await writeSync(page, 'abcde'); assert.equal(await page.evaluate(`window.term.buffer.cursorX`), 5); assert.equal(await page.evaluate(`window.term.buffer.cursorY`), 1); - await page.evaluate(`window.term.write('\\n\\r\\n\\n\\n\\n\\n')`); + await writeSync(page, '\\n\\r\\n\\n\\n\\n\\n'); assert.equal(await page.evaluate(`window.term.buffer.cursorX`), 0); assert.equal(await page.evaluate(`window.term.buffer.cursorY`), 4); }); - it('viewportY', async function(): Promise { + it('viewportY', async () => { await openTerminal({ rows: 5 }); assert.equal(await page.evaluate(`window.term.buffer.viewportY`), 0); - await page.evaluate(`window.term.write('\\n\\n\\n\\n')`); + await writeSync(page, '\\n\\n\\n\\n'); assert.equal(await page.evaluate(`window.term.buffer.viewportY`), 0); - await page.evaluate(`window.term.write('\\n')`); + await writeSync(page, '\\n'); assert.equal(await page.evaluate(`window.term.buffer.viewportY`), 1); - await page.evaluate(`window.term.write('\\n\\n\\n\\n')`); + await writeSync(page, '\\n\\n\\n\\n'); assert.equal(await page.evaluate(`window.term.buffer.viewportY`), 5); await page.evaluate(`window.term.scrollLines(-1)`); assert.equal(await page.evaluate(`window.term.buffer.viewportY`), 4); @@ -434,14 +431,14 @@ describe('API Integration Tests', function(): void { assert.equal(await page.evaluate(`window.term.buffer.viewportY`), 0); }); - it('baseY', async function(): Promise { + it('baseY', async () => { await openTerminal({ rows: 5 }); assert.equal(await page.evaluate(`window.term.buffer.baseY`), 0); - await page.evaluate(`window.term.write('\\n\\n\\n\\n')`); + await writeSync(page, '\\n\\n\\n\\n'); assert.equal(await page.evaluate(`window.term.buffer.baseY`), 0); - await page.evaluate(`window.term.write('\\n')`); + await writeSync(page, '\\n'); assert.equal(await page.evaluate(`window.term.buffer.baseY`), 1); - await page.evaluate(`window.term.write('\\n\\n\\n\\n')`); + await writeSync(page, '\\n\\n\\n\\n'); assert.equal(await page.evaluate(`window.term.buffer.baseY`), 5); await page.evaluate(`window.term.scrollLines(-1)`); assert.equal(await page.evaluate(`window.term.buffer.baseY`), 5); @@ -449,44 +446,44 @@ describe('API Integration Tests', function(): void { assert.equal(await page.evaluate(`window.term.buffer.baseY`), 5); }); - it('length', async function(): Promise { + it('length', async () => { await openTerminal({ rows: 5 }); assert.equal(await page.evaluate(`window.term.buffer.length`), 5); - await page.evaluate(`window.term.write('\\n\\n\\n\\n')`); + await writeSync(page, '\\n\\n\\n\\n'); assert.equal(await page.evaluate(`window.term.buffer.length`), 5); - await page.evaluate(`window.term.write('\\n')`); + await writeSync(page, '\\n'); assert.equal(await page.evaluate(`window.term.buffer.length`), 6); - await page.evaluate(`window.term.write('\\n\\n\\n\\n')`); + await writeSync(page, '\\n\\n\\n\\n'); assert.equal(await page.evaluate(`window.term.buffer.length`), 10); }); describe('getLine', () => { - it('invalid index', async function(): Promise { + it('invalid index', async () => { await openTerminal({ rows: 5 }); assert.equal(await page.evaluate(`window.term.buffer.getLine(-1)`), undefined); assert.equal(await page.evaluate(`window.term.buffer.getLine(5)`), undefined); }); - it('isWrapped', async function(): Promise { + it('isWrapped', async () => { await openTerminal({ cols: 5 }); assert.equal(await page.evaluate(`window.term.buffer.getLine(0).isWrapped`), false); assert.equal(await page.evaluate(`window.term.buffer.getLine(1).isWrapped`), false); - await page.evaluate(`window.term.write('abcde')`); + await writeSync(page, 'abcde'); assert.equal(await page.evaluate(`window.term.buffer.getLine(0).isWrapped`), false); assert.equal(await page.evaluate(`window.term.buffer.getLine(1).isWrapped`), false); - await page.evaluate(`window.term.write('f')`); + await writeSync(page, 'f'); assert.equal(await page.evaluate(`window.term.buffer.getLine(0).isWrapped`), false); assert.equal(await page.evaluate(`window.term.buffer.getLine(1).isWrapped`), true); }); - it('translateToString', async function(): Promise { + it('translateToString', async () => { await openTerminal({ cols: 5 }); assert.equal(await page.evaluate(`window.term.buffer.getLine(0).translateToString()`), ' '); assert.equal(await page.evaluate(`window.term.buffer.getLine(0).translateToString(true)`), ''); - await page.evaluate(`window.term.write('foo')`); + await writeSync(page, 'foo'); assert.equal(await page.evaluate(`window.term.buffer.getLine(0).translateToString()`), 'foo '); assert.equal(await page.evaluate(`window.term.buffer.getLine(0).translateToString(true)`), 'foo'); - await page.evaluate(`window.term.write('bar')`); + await writeSync(page, 'bar'); assert.equal(await page.evaluate(`window.term.buffer.getLine(0).translateToString()`), 'fooba'); assert.equal(await page.evaluate(`window.term.buffer.getLine(0).translateToString(true)`), 'fooba'); assert.equal(await page.evaluate(`window.term.buffer.getLine(1).translateToString(true)`), 'r'); @@ -494,13 +491,13 @@ describe('API Integration Tests', function(): void { assert.equal(await page.evaluate(`window.term.buffer.getLine(0).translateToString(false, 1, 3)`), 'oo'); }); - it('getCell', async function(): Promise { + it('getCell', async () => { await openTerminal({ cols: 5 }); assert.equal(await page.evaluate(`window.term.buffer.getLine(0).getCell(-1)`), undefined); assert.equal(await page.evaluate(`window.term.buffer.getLine(0).getCell(5)`), undefined); assert.equal(await page.evaluate(`window.term.buffer.getLine(0).getCell(0).char`), ''); assert.equal(await page.evaluate(`window.term.buffer.getLine(0).getCell(0).width`), 1); - await page.evaluate(`window.term.write('a文')`); + await writeSync(page, 'a文'); assert.equal(await page.evaluate(`window.term.buffer.getLine(0).getCell(0).char`), 'a'); assert.equal(await page.evaluate(`window.term.buffer.getLine(0).getCell(0).width`), 1); assert.equal(await page.evaluate(`window.term.buffer.getLine(0).getCell(1).char`), '文'); @@ -511,7 +508,7 @@ describe('API Integration Tests', function(): void { }); }); - it('dispose', async function(): Promise { + it('dispose', async () => { await page.evaluate(` window.term = new Terminal(); window.term.dispose(); @@ -519,7 +516,7 @@ describe('API Integration Tests', function(): void { assert.equal(await page.evaluate(`window.term._core._isDisposed`), true); }); - it('dispose (opened)', async function(): Promise { + it('dispose (opened)', async () => { await openTerminal(); await page.evaluate(`window.term.dispose()`); assert.equal(await page.evaluate(`window.term._core._isDisposed`), true); diff --git a/test/api/TestUtils.ts b/test/api/TestUtils.ts index ff1c661e..f0dd71d4 100644 --- a/test/api/TestUtils.ts +++ b/test/api/TestUtils.ts @@ -4,21 +4,24 @@ */ import * as puppeteer from 'puppeteer'; +import deepEqual = require('deep-equal'); export async function pollFor(page: puppeteer.Page, evalOrFn: string | (() => Promise), val: T, preFn?: () => Promise): Promise { if (preFn) { await preFn(); } const result = typeof evalOrFn === 'string' ? await page.evaluate(evalOrFn) : await evalOrFn(); - let equal = false; - if (typeof result === 'object') { - equal = Object.keys(result).every(e => result[e] === (val as any)[e]); - } else { - equal = result === val; - } - if (!equal) { + if (!deepEqual(result, val)) { return new Promise(r => { setTimeout(() => r(pollFor(page, evalOrFn, val, preFn)), 10); }); } } + +export async function writeSync(page: puppeteer.Page, data: string): Promise { + await page.evaluate(` + window.ready = false; + window.term.write('${data}', () => window.ready = true); + `); + await pollFor(page, 'window.ready', true); +} diff --git a/yarn.lock b/yarn.lock index cb02cf91..171b66e4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -130,6 +130,11 @@ resolved "https://registry.yarnpkg.com/@types/cli-table/-/cli-table-0.3.0.tgz#f1857156bf5fd115c6a2db260ba0be1f8fc5671c" integrity sha512-QnZUISJJXyhyD6L1e5QwXDV/A5i2W1/gl6D6YMc8u0ncPepbv/B4w3S+izVvtAg60m6h+JP09+Y/0zF2mojlFQ== +"@types/deep-equal@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@types/deep-equal/-/deep-equal-1.0.1.tgz#71cfabb247c22bcc16d536111f50c0ed12476b03" + integrity sha512-mMUu4nWHLBlHtxXY17Fg6+ucS/MnndyOWyOe7MmwkoMYxvfQU2ajtRaEvqSUv+aVkMqH/C0NCI8UoVfRNQ10yg== + "@types/events@*": version "1.2.0" resolved "https://registry.yarnpkg.com/@types/events/-/events-1.2.0.tgz#81a6731ce4df43619e5c8c945383b3e62a89ea86" @@ -1454,6 +1459,18 @@ deep-eql@^0.1.3: dependencies: type-detect "0.1.1" +deep-equal@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.1.0.tgz#3103cdf8ab6d32cf4a8df7865458f2b8d33f3745" + integrity sha512-ZbfWJq/wN1Z273o7mUSjILYqehAktR2NVoSrOukDkU9kg2v/Uv89yU4Cvz8seJeAmtN5oqiefKq8FPuXOboqLw== + dependencies: + is-arguments "^1.0.4" + is-date-object "^1.0.1" + is-regex "^1.0.4" + object-is "^1.0.1" + object-keys "^1.1.1" + regexp.prototype.flags "^1.2.0" + deep-extend@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" @@ -2567,6 +2584,11 @@ is-accessor-descriptor@^1.0.0: dependencies: kind-of "^6.0.0" +is-arguments@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.0.4.tgz#3faf966c7cba0ff437fb31f6250082fcf0448cf3" + integrity sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA== + is-arrayish@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" @@ -3634,7 +3656,12 @@ object-copy@^0.1.0: define-property "^0.2.5" kind-of "^3.0.3" -object-keys@^1.0.11, object-keys@^1.0.12: +object-is@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.0.1.tgz#0aa60ec9989a0b3ed795cf4d06f62cf1ad6539b6" + integrity sha1-CqYOyZiaCz7Xlc9NBvYs8a1lObY= + +object-keys@^1.0.11, object-keys@^1.0.12, object-keys@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== @@ -4238,6 +4265,13 @@ regex-not@^1.0.0, regex-not@^1.0.2: extend-shallow "^3.0.2" safe-regex "^1.1.0" +regexp.prototype.flags@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.2.0.tgz#6b30724e306a27833eeb171b66ac8890ba37e41c" + integrity sha512-ztaw4M1VqgMwl9HlPpOuiYgItcHlunW0He2fE6eNfT6E/CF2FtYi9ofOYe4mKntstYk0Fyh/rDRBdS3AnxjlrA== + dependencies: + define-properties "^1.1.2" + release-zalgo@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/release-zalgo/-/release-zalgo-1.0.0.tgz#09700b7e5074329739330e535c5a90fb67851730" From 7ff6e4f4933bc604325aab5feb2b248e9a735082 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sat, 9 Nov 2019 18:28:24 -0800 Subject: [PATCH 57/92] Fix flaky terminal api tests --- test/api/Terminal.api.ts | 10 ++++++---- test/api/TestUtils.ts | 7 ++++++- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/test/api/Terminal.api.ts b/test/api/Terminal.api.ts index 6d7a4d20..0b05736d 100644 --- a/test/api/Terminal.api.ts +++ b/test/api/Terminal.api.ts @@ -6,7 +6,7 @@ import * as puppeteer from 'puppeteer'; import { assert } from 'chai'; import { ITerminalOptions } from 'xterm'; -import { pollFor, writeSync } from './TestUtils'; +import { pollFor, timeout, writeSync } from './TestUtils'; const APP = 'http://127.0.0.1:3000/test'; @@ -15,7 +15,7 @@ let page: puppeteer.Page; const width = 800; const height = 600; -describe.only('API Integration Tests', function(): void { +describe('API Integration Tests', function(): void { before(async () => { browser = await puppeteer.launch({ headless: process.argv.indexOf('--headless') !== -1, @@ -162,7 +162,7 @@ describe.only('API Integration Tests', function(): void { it('foreground', async () => { await openTerminal({ rendererType: 'dom' }); await writeSync(page, '\\x1b[30m0\\x1b[31m1\\x1b[32m2\\x1b[33m3\\x1b[34m4\\x1b[35m5\\x1b[36m6\\x1b[37m7'); - await pollFor(page, `!!document.querySelector('.xterm-rows > :nth-child(1) > :nth-child(1)')`, true); + await pollFor(page, `document.querySelectorAll('.xterm-rows > :nth-child(1) > *').length`, 9); assert.deepEqual(await page.evaluate(` [ document.querySelector('.xterm-rows > :nth-child(1) > :nth-child(1)').className, @@ -187,7 +187,7 @@ describe.only('API Integration Tests', function(): void { it('background', async () => { await openTerminal({ rendererType: 'dom' }); await writeSync(page, '\\x1b[40m0\\x1b[41m1\\x1b[42m2\\x1b[43m3\\x1b[44m4\\x1b[45m5\\x1b[46m6\\x1b[47m7'); - await pollFor(page, `!!document.querySelector('.xterm-rows > :nth-child(1) > :nth-child(1)')`, true); + await pollFor(page, `document.querySelectorAll('.xterm-rows > :nth-child(1) > *').length`, 9); assert.deepEqual(await page.evaluate(` [ document.querySelector('.xterm-rows > :nth-child(1) > :nth-child(1)').className, @@ -358,6 +358,7 @@ describe.only('API Integration Tests', function(): void { it('onRender', async () => { await openTerminal(); + await timeout(20); // Ensure all init events are fired await page.evaluate(` window.calls = []; window.term.onRender(e => window.calls.push([e.start, e.end])); @@ -371,6 +372,7 @@ describe.only('API Integration Tests', function(): void { it('onResize', async () => { await openTerminal(); + await timeout(20); // Ensure all init events are fired await page.evaluate(` window.calls = []; window.term.onResize(e => window.calls.push([e.cols, e.rows])); diff --git a/test/api/TestUtils.ts b/test/api/TestUtils.ts index f0dd71d4..a31c2b46 100644 --- a/test/api/TestUtils.ts +++ b/test/api/TestUtils.ts @@ -12,8 +12,9 @@ export async function pollFor(page: puppeteer.Page, evalOrFn: string | (() => } const result = typeof evalOrFn === 'string' ? await page.evaluate(evalOrFn) : await evalOrFn(); if (!deepEqual(result, val)) { + console.log('result', result, 'val', val); return new Promise(r => { - setTimeout(() => r(pollFor(page, evalOrFn, val, preFn)), 10); + setTimeout(() => r(pollFor(page, evalOrFn, val, preFn)), 1); }); } } @@ -25,3 +26,7 @@ export async function writeSync(page: puppeteer.Page, data: string): Promise { + return new Promise(r => setTimeout(r, ms)); +} From b0640b7705b2b80df84c867ba60913e5729ca5cd Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sat, 9 Nov 2019 18:28:50 -0800 Subject: [PATCH 58/92] Remove slowMo param everywhere --- addons/xterm-addon-fit/src/FitAddon.api.ts | 1 - addons/xterm-addon-search/src/SearchAddon.api.ts | 1 - test/api/InputHandler.api.ts | 1 - 3 files changed, 3 deletions(-) diff --git a/addons/xterm-addon-fit/src/FitAddon.api.ts b/addons/xterm-addon-fit/src/FitAddon.api.ts index 3115f2ed..aa73a9b6 100644 --- a/addons/xterm-addon-fit/src/FitAddon.api.ts +++ b/addons/xterm-addon-fit/src/FitAddon.api.ts @@ -19,7 +19,6 @@ describe('FitAddon', () => { this.timeout(20000); browser = await puppeteer.launch({ headless: process.argv.indexOf('--headless') !== -1, - slowMo: 0, args: [`--window-size=${width},${height}`, `--no-sandbox`] }); page = (await browser.pages())[0]; diff --git a/addons/xterm-addon-search/src/SearchAddon.api.ts b/addons/xterm-addon-search/src/SearchAddon.api.ts index 96c62a7d..72a69b5f 100644 --- a/addons/xterm-addon-search/src/SearchAddon.api.ts +++ b/addons/xterm-addon-search/src/SearchAddon.api.ts @@ -22,7 +22,6 @@ describe('Search Tests', function (): void { before(async function (): Promise { browser = await puppeteer.launch({ headless: process.argv.indexOf('--headless') !== -1, - slowMo: 0, args: [`--window-size=${width},${height}`, `--no-sandbox`] }); page = (await browser.pages())[0]; diff --git a/test/api/InputHandler.api.ts b/test/api/InputHandler.api.ts index 4bc2c07d..968a9e0b 100644 --- a/test/api/InputHandler.api.ts +++ b/test/api/InputHandler.api.ts @@ -19,7 +19,6 @@ describe('InputHandler Integration Tests', function(): void { before(async function(): Promise { browser = await puppeteer.launch({ headless: process.argv.indexOf('--headless') !== -1, - slowMo: 0, args: [`--window-size=${width},${height}`, `--no-sandbox`] }); page = (await browser.pages())[0]; From 971e69f50592046d12ad64ae8cd11575c9d725b3 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sat, 9 Nov 2019 18:43:25 -0800 Subject: [PATCH 59/92] Reduce flakiness --- .../src/WebLinksAddon.api.ts | 28 +++++++++++-------- test/api/MouseTracking.api.ts | 20 ++++++------- test/api/Parser.api.ts | 17 ++++------- test/api/TestUtils.ts | 1 - 4 files changed, 31 insertions(+), 35 deletions(-) diff --git a/addons/xterm-addon-web-links/src/WebLinksAddon.api.ts b/addons/xterm-addon-web-links/src/WebLinksAddon.api.ts index 49ec4fee..0cb222c8 100644 --- a/addons/xterm-addon-web-links/src/WebLinksAddon.api.ts +++ b/addons/xterm-addon-web-links/src/WebLinksAddon.api.ts @@ -53,18 +53,14 @@ describe('WebLinksAddon', () => { async function testHostName(hostname: string): Promise { await openTerminal({ rendererType: 'dom' }); await page.evaluate(`window.term.loadAddon(new window.WebLinksAddon())`); - await new Promise(r => setTimeout(r, 100)); - await page.evaluate(` - window.ready = false; - window.term.writeln(' http://${hostname} '); - window.term.writeln(' http://${hostname}/a~b#c~d?e~f '); - window.term.writeln(' http://${hostname}/colon:test '); - window.term.writeln(' http://${hostname}/colon:test: '); - window.term.writeln('"http://${hostname}/"'); - window.term.writeln('\\'http://${hostname}/\\''); - window.term.writeln('http://${hostname}/subpath/+/id', () => window.ready = true); - `); - await pollFor(page, 'window.ready', true); + const data = ` http://${hostname} \\r\\n` + + ` http://${hostname}/a~b#c~d?e~f \\r\\n` + + ` http://${hostname}/colon:test \\r\\n` + + ` http://${hostname}/colon:test: \\r\\n` + + `"http://${hostname}/"\\r\\n` + + `\\'http://${hostname}/\\'\\r\\n` + + `http://${hostname}/subpath/+/id`; + await writeSync(page, data); await pollForLinkAtCell(3, 1, `http://${hostname}`); await pollForLinkAtCell(3, 2, `http://${hostname}/a~b#c~d?e~f`); await pollForLinkAtCell(3, 3, `http://${hostname}/colon:test`); @@ -101,3 +97,11 @@ async function pollFor(page: puppeteer.Page, fn: string, val: any, preFn?: () => }); } } + +async function writeSync(page: puppeteer.Page, data: string): Promise { + await page.evaluate(` + window.ready = false; + window.term.write('${data}', () => window.ready = true); + `); + await pollFor(page, 'window.ready', true); +} diff --git a/test/api/MouseTracking.api.ts b/test/api/MouseTracking.api.ts index d10a5b95..f8124350 100644 --- a/test/api/MouseTracking.api.ts +++ b/test/api/MouseTracking.api.ts @@ -5,7 +5,7 @@ import * as puppeteer from 'puppeteer'; import { ITerminalOptions } from 'xterm'; -import { pollFor } from './TestUtils'; +import { pollFor, writeSync } from './TestUtils'; const APP = 'http://127.0.0.1:3000/test'; @@ -199,7 +199,7 @@ function parseReport(encoding: string, msg: number[]): {state: any; row: number; * Mouse tracking tests. */ describe('Mouse Tracking Tests', () => { - before(async () => { + before(async function(): Promise { browser = await puppeteer.launch({ headless: process.argv.indexOf('--headless') !== -1, args: [`--window-size=${width},${height}`, `--no-sandbox`] @@ -237,7 +237,7 @@ describe('Mouse Tracking Tests', () => { const encoding = 'DEFAULT'; await resetMouseModes(); await mouseMove(0, 0); - await page.evaluate(`window.term.write('\x1b[?9h');`); + await writeSync(page, '\x1b[?9h'); // test at 0,0 await mouseDown('left'); @@ -359,7 +359,7 @@ describe('Mouse Tracking Tests', () => { const encoding = 'SGR'; await resetMouseModes(); await mouseMove(0, 0); - await page.evaluate(`window.term.write('\x1b[?9h\x1b[?1006h');`); + await writeSync(page, '\x1b[?9h\x1b[?1006h'); // test at 0,0 await mouseDown('left'); @@ -486,7 +486,7 @@ describe('Mouse Tracking Tests', () => { const encoding = 'DEFAULT'; await resetMouseModes(); await mouseMove(0, 0); - await page.evaluate(`window.term.write('\x1b[?1000h');`); + await writeSync(page, '\x1b[?1000h'); // test at 0,0 await mouseDown('left'); @@ -638,7 +638,7 @@ describe('Mouse Tracking Tests', () => { const encoding = 'SGR'; await resetMouseModes(); await mouseMove(0, 0); - await page.evaluate(`window.term.write('\x1b[?1000h\x1b[?1006h');`); + await writeSync(page, '\x1b[?1000h\x1b[?1006h'); // test at 0,0 await mouseDown('left'); @@ -796,7 +796,7 @@ describe('Mouse Tracking Tests', () => { const encoding = 'DEFAULT'; await resetMouseModes(); await mouseMove(0, 0); - await page.evaluate(`window.term.write('\x1b[?1002h');`); + await writeSync(page, '\x1b[?1002h'); // test at 0,0 await mouseDown('left'); @@ -953,7 +953,7 @@ describe('Mouse Tracking Tests', () => { const encoding = 'SGR'; await resetMouseModes(); await mouseMove(0, 0); - await page.evaluate(`window.term.write('\x1b[?1002h\x1b[?1006h');`); + await writeSync(page, '\x1b[?1002h\x1b[?1006h'); // test at 0,0 // bug: release is fired immediately @@ -1115,7 +1115,7 @@ describe('Mouse Tracking Tests', () => { const encoding = 'DEFAULT'; await resetMouseModes(); await mouseMove(0, 0); - await page.evaluate(`window.term.write('\x1b[?1003h');`); + await writeSync(page, '\x1b[?1003h'); // test at 0,0 await mouseDown('left'); @@ -1276,7 +1276,7 @@ describe('Mouse Tracking Tests', () => { const encoding = 'SGR'; await resetMouseModes(); await mouseMove(0, 0); - await page.evaluate(`window.term.write('\x1b[?1003h\x1b[?1006h');`); + await writeSync(page, '\x1b[?1003h\x1b[?1006h'); // test at 0,0 await mouseDown('left'); diff --git a/test/api/Parser.api.ts b/test/api/Parser.api.ts index 9b0fb66b..72f56799 100644 --- a/test/api/Parser.api.ts +++ b/test/api/Parser.api.ts @@ -6,6 +6,7 @@ import * as puppeteer from 'puppeteer'; import { assert } from 'chai'; import { ITerminalOptions } from 'xterm'; +import { writeSync } from './TestUtils'; const APP = 'http://127.0.0.1:3000/test'; @@ -38,9 +39,7 @@ describe('Parser Integration Tests', function(): void { return false; }, ''); `); - await page.evaluate(` - window.term.write('\x1b[38;5;123mparams\x1b[38:2::50:100:150msubparams'); - `); + await writeSync(page, '\x1b[38;5;123mparams\x1b[38:2::50:100:150msubparams'); assert.deepEqual(await page.evaluate(`(() => _customCsiHandlerParams)();`), [[38, 5, 123], [38, [2, -1, 50, 100, 150]]]); }); }); @@ -62,9 +61,7 @@ describe('Parser Integration Tests', function(): void { return false; }); `); - await page.evaluate(` - window.term.write('\x1bP1;2+psome data\x1b\\\\'); - `); + await writeSync(page, '\x1bP1;2+psome data\x1b\\\\'); assert.deepEqual(await page.evaluate(`(() => _customDcsHandlerCallStack)();`), [['C', [1, 2], 'some data'], ['B', [1, 2], 'some data']]); }); }); @@ -86,9 +83,7 @@ describe('Parser Integration Tests', function(): void { return false; }); `); - await page.evaluate(` - window.term.write('\x1b(B'); - `); + await writeSync(page, '\x1b(B'); assert.deepEqual(await page.evaluate(`(() => _customEscHandlerCallStack)();`), ['C', 'B']); }); }); @@ -110,9 +105,7 @@ describe('Parser Integration Tests', function(): void { return false; }); `); - await page.evaluate(` - window.term.write('\x1b]1234;some data\x07'); - `); + await writeSync(page, '\x1b]1234;some data\x07'); assert.deepEqual(await page.evaluate(`(() => _customOscHandlerCallStack)();`), [['C', 'some data'], ['B', 'some data']]); }); }); diff --git a/test/api/TestUtils.ts b/test/api/TestUtils.ts index a31c2b46..d3674812 100644 --- a/test/api/TestUtils.ts +++ b/test/api/TestUtils.ts @@ -12,7 +12,6 @@ export async function pollFor(page: puppeteer.Page, evalOrFn: string | (() => } const result = typeof evalOrFn === 'string' ? await page.evaluate(evalOrFn) : await evalOrFn(); if (!deepEqual(result, val)) { - console.log('result', result, 'val', val); return new Promise(r => { setTimeout(() => r(pollFor(page, evalOrFn, val, preFn)), 1); }); From 669622108cbe19d1ef1126d478aff291d261336b Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sat, 9 Nov 2019 18:48:09 -0800 Subject: [PATCH 60/92] Wait for all elements to be rendered in web links --- addons/xterm-addon-web-links/src/WebLinksAddon.api.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/xterm-addon-web-links/src/WebLinksAddon.api.ts b/addons/xterm-addon-web-links/src/WebLinksAddon.api.ts index 0cb222c8..7bbaf29e 100644 --- a/addons/xterm-addon-web-links/src/WebLinksAddon.api.ts +++ b/addons/xterm-addon-web-links/src/WebLinksAddon.api.ts @@ -82,7 +82,7 @@ async function openTerminal(options: ITerminalOptions = {}): Promise { async function pollForLinkAtCell(col: number, row: number, value: string): Promise { const rowSelector = `.xterm-rows > :nth-child(${row})`; - await pollFor(page, `document.querySelectorAll('${rowSelector} > span[style]').length > 0`, true, async () => page.hover(`${rowSelector} > :nth-child(${col})`)); + await pollFor(page, `document.querySelectorAll('${rowSelector} > span[style]').length >= ${value.length}`, true, async () => page.hover(`${rowSelector} > :nth-child(${col})`)); assert.equal(await page.evaluate(`Array.prototype.reduce.call(document.querySelectorAll('${rowSelector} > span[style]'), (a, b) => a + b.textContent, '');`), value); } From 057d0a16f2ef5c41abf4854f2b23158f0a831c25 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sat, 9 Nov 2019 18:54:24 -0800 Subject: [PATCH 61/92] Ensure link hover exists before hovering --- addons/xterm-addon-web-links/src/WebLinksAddon.api.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/addons/xterm-addon-web-links/src/WebLinksAddon.api.ts b/addons/xterm-addon-web-links/src/WebLinksAddon.api.ts index 7bbaf29e..ab61fdbf 100644 --- a/addons/xterm-addon-web-links/src/WebLinksAddon.api.ts +++ b/addons/xterm-addon-web-links/src/WebLinksAddon.api.ts @@ -82,6 +82,8 @@ async function openTerminal(options: ITerminalOptions = {}): Promise { async function pollForLinkAtCell(col: number, row: number, value: string): Promise { const rowSelector = `.xterm-rows > :nth-child(${row})`; + // Ensure the hover element exists before trying to hover it + await pollFor(page, `!!document.querySelector('${rowSelector} > :nth-child(${col})')`, true); await pollFor(page, `document.querySelectorAll('${rowSelector} > span[style]').length >= ${value.length}`, true, async () => page.hover(`${rowSelector} > :nth-child(${col})`)); assert.equal(await page.evaluate(`Array.prototype.reduce.call(document.querySelectorAll('${rowSelector} > span[style]'), (a, b) => a + b.textContent, '');`), value); } From 40b3766abc26c4a3233d9511bed80cdf872b76b1 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sat, 9 Nov 2019 19:05:20 -0800 Subject: [PATCH 62/92] Fix import that got lost in merge --- addons/xterm-addon-webgl/src/WebglRenderer.api.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/addons/xterm-addon-webgl/src/WebglRenderer.api.ts b/addons/xterm-addon-webgl/src/WebglRenderer.api.ts index c3c54340..51dae37a 100644 --- a/addons/xterm-addon-webgl/src/WebglRenderer.api.ts +++ b/addons/xterm-addon-webgl/src/WebglRenderer.api.ts @@ -6,6 +6,7 @@ import * as puppeteer from 'puppeteer'; import { ITerminalOptions } from '../../../src/Types'; import { ITheme } from 'xterm'; +import { assert } from 'chai'; const APP = 'http://127.0.0.1:3000/test'; From 8a6e0fca07fa28ef51df1d1af296c73ab53f402b Mon Sep 17 00:00:00 2001 From: Daniel Date: Sat, 9 Nov 2019 21:15:07 -0800 Subject: [PATCH 63/92] Move parts over to CellData --- addons/xterm-addon-webgl/src/WebglRenderer.ts | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/addons/xterm-addon-webgl/src/WebglRenderer.ts b/addons/xterm-addon-webgl/src/WebglRenderer.ts index ab2cb354..785f6e69 100644 --- a/addons/xterm-addon-webgl/src/WebglRenderer.ts +++ b/addons/xterm-addon-webgl/src/WebglRenderer.ts @@ -23,6 +23,7 @@ import { IColorSet } from 'browser/Types'; import { FLAGS } from './Constants'; import { getCompatAttr } from './CharDataCompat'; import { EventEmitter } from 'common/EventEmitter'; +import { CellData } from 'common/buffer/CellData'; export const INDICIES_PER_CELL = 4; @@ -32,6 +33,7 @@ export class WebglRenderer extends Disposable implements IRenderer { private _devicePixelRatio: number; private _model: RenderModel = new RenderModel(); + private _workCell: CellData = new CellData(); private _canvas: HTMLCanvasElement; private _gl: IWebGL2RenderingContext; @@ -258,12 +260,19 @@ export class WebglRenderer extends Disposable implements IRenderer { const line = terminal.buffer.lines.get(row)!; this._model.lineLengths[y] = 0; for (let x = 0; x < terminal.cols; x++) { - const charData = line.get(x); - const chars = charData[CHAR_DATA_CHAR_INDEX]; - let code = charData[CHAR_DATA_CODE_INDEX]; + line.loadCell(x, this._workCell); + + // const charData = line.get(x); + // const chars = charData[CHAR_DATA_CHAR_INDEX]; + const chars = this._workCell.getChars(); + // let code = charData[CHAR_DATA_CODE_INDEX]; + let code = this._workCell.getCode(); const attr = getCompatAttr(line, x); // charData[CHAR_DATA_ATTR_INDEX]; const i = ((y * terminal.cols) + x) * INDICIES_PER_CELL; + // console.log('workcell', this._workCell); + // console.log('attr: ' + attr); + if (code !== NULL_CELL_CODE) { this._model.lineLengths[y] = x + 1; } From e72ee3c58dc87ccfaffd103eff24439e1554ce96 Mon Sep 17 00:00:00 2001 From: Daniel Date: Sat, 9 Nov 2019 22:28:59 -0800 Subject: [PATCH 64/92] Move webgl drawing over from compat Still need to tackle glyph key --- .../src/RectangleRenderer.ts | 25 ++++++++++++------- addons/xterm-addon-webgl/src/WebglRenderer.ts | 22 +++++++--------- .../src/atlas/CharAtlasUtils.ts | 4 +-- .../src/atlas/WebglCharAtlas.ts | 14 +++++------ 4 files changed, 34 insertions(+), 31 deletions(-) diff --git a/addons/xterm-addon-webgl/src/RectangleRenderer.ts b/addons/xterm-addon-webgl/src/RectangleRenderer.ts index 2c464d2a..70b5ee3c 100644 --- a/addons/xterm-addon-webgl/src/RectangleRenderer.ts +++ b/addons/xterm-addon-webgl/src/RectangleRenderer.ts @@ -8,7 +8,7 @@ import { IRenderModel, IWebGLVertexArrayObject, IWebGL2RenderingContext, ISelect import { fill } from 'common/TypedArrayUtils'; import { INVERTED_DEFAULT_COLOR } from 'browser/renderer/atlas/Constants'; import { is256Color } from './atlas/CharAtlasUtils'; -import { DEFAULT_COLOR } from 'common/buffer/Constants'; +import { DEFAULT_COLOR, Attributes, FgFlags } from 'common/buffer/Constants'; import { Terminal } from 'xterm'; import { IColorSet, IColor } from 'browser/Types'; import { IRenderDimensions } from 'browser/renderer/Types'; @@ -248,37 +248,44 @@ export class RectangleRenderer { for (let y = 0; y < terminal.rows; y++) { let currentStartX = -1; let currentBg = DEFAULT_COLOR; + let currentFg = DEFAULT_COLOR; for (let x = 0; x < terminal.cols; x++) { const modelIndex = ((y * terminal.cols) + x) * 4; const bg = model.cells[modelIndex + 2]; + const fg = model.cells[modelIndex + 1]; if (bg !== currentBg) { // A rectangle needs to be drawn if going from non-default to another color + // TODO: DEFAULT_COLOR probably isn't right anymore? if (currentBg !== DEFAULT_COLOR) { const offset = rectangleCount++ * INDICES_PER_RECTANGLE; - this._updateRectangle(vertices, offset, currentBg, currentStartX, x, y); + this._updateRectangle(vertices, offset, currentFg, currentBg, currentStartX, x, y); } currentStartX = x; currentBg = bg; + currentFg = fg; } } // Finish rectangle if it's still going if (currentBg !== DEFAULT_COLOR) { const offset = rectangleCount++ * INDICES_PER_RECTANGLE; - this._updateRectangle(vertices, offset, currentBg, currentStartX, terminal.cols, y); + this._updateRectangle(vertices, offset, currentFg, currentBg, currentStartX, terminal.cols, y); } } vertices.count = rectangleCount; } - private _updateRectangle(vertices: IVertices, offset: number, bg: number, startX: number, endX: number, y: number): void { + private _updateRectangle(vertices: IVertices, offset: number, fg: number, bg: number, startX: number, endX: number, y: number): void { let color: IColor | null = null; - if (bg === INVERTED_DEFAULT_COLOR) { + if (fg & FgFlags.INVERSE) { + // Inverted color color = this._colors.foreground; - } else if (is256Color(bg)) { - color = this._colors.ansi[bg]; + } else if ((bg & Attributes.CM_MASK) === Attributes.CM_P16 || (bg & Attributes.CM_MASK) === Attributes.CM_P256) { + // 256 palette + color = this._colors.ansi[bg & Attributes.PCOLOR_MASK]; } else { - // TODO: Add support for true color - color = this._colors.foreground; + // TODO: Support true color + // Default color + color = this._colors.background; } if (vertices.attributes.length < offset + 4) { vertices.attributes = expandFloat32Array(vertices.attributes, this._terminal.rows * this._terminal.cols * INDICES_PER_RECTANGLE); diff --git a/addons/xterm-addon-webgl/src/WebglRenderer.ts b/addons/xterm-addon-webgl/src/WebglRenderer.ts index 785f6e69..ef20f614 100644 --- a/addons/xterm-addon-webgl/src/WebglRenderer.ts +++ b/addons/xterm-addon-webgl/src/WebglRenderer.ts @@ -14,7 +14,7 @@ import { IWebGL2RenderingContext } from './Types'; import { INVERTED_DEFAULT_COLOR } from 'browser/renderer/atlas/Constants'; import { RenderModel, COMBINED_CHAR_BIT_MASK } from './RenderModel'; import { Disposable } from 'common/Lifecycle'; -import { DEFAULT_COLOR, CHAR_DATA_CHAR_INDEX, CHAR_DATA_CODE_INDEX, NULL_CELL_CODE } from 'common/buffer/Constants'; +import { DEFAULT_COLOR, CHAR_DATA_CHAR_INDEX, CHAR_DATA_CODE_INDEX, NULL_CELL_CODE, FgFlags } from 'common/buffer/Constants'; import { Terminal, IEvent } from 'xterm'; import { getLuminance } from './ColorUtils'; import { IRenderLayer } from './renderLayer/Types'; @@ -262,17 +262,11 @@ export class WebglRenderer extends Disposable implements IRenderer { for (let x = 0; x < terminal.cols; x++) { line.loadCell(x, this._workCell); - // const charData = line.get(x); - // const chars = charData[CHAR_DATA_CHAR_INDEX]; const chars = this._workCell.getChars(); - // let code = charData[CHAR_DATA_CODE_INDEX]; let code = this._workCell.getCode(); const attr = getCompatAttr(line, x); // charData[CHAR_DATA_ATTR_INDEX]; const i = ((y * terminal.cols) + x) * INDICIES_PER_CELL; - // console.log('workcell', this._workCell); - // console.log('attr: ' + attr); - if (code !== NULL_CELL_CODE) { this._model.lineLengths[y] = x + 1; } @@ -283,12 +277,11 @@ export class WebglRenderer extends Disposable implements IRenderer { } // Resolve bg and fg and cache in the model - const flags = attr >> 18; - let bg = attr & 0x1ff; - let fg = (attr >> 9) & 0x1ff; + let bg = this._workCell.bg; + let fg = this._workCell.fg; // If inverse flag is on, the foreground should become the background. - if (flags & FLAGS.INVERSE) { + if (this._workCell.isInverse()) { const temp = bg; bg = fg; fg = temp; @@ -299,8 +292,11 @@ export class WebglRenderer extends Disposable implements IRenderer { bg = INVERTED_DEFAULT_COLOR; } } - const drawInBrightColor = terminal.options.drawBoldTextInBrightColors && !!(flags & FLAGS.BOLD) && fg < 8 && fg !== INVERTED_DEFAULT_COLOR; - fg += drawInBrightColor ? 8 : 0; + + // Apply drawBoldTextInBrightColors + if (terminal.options.drawBoldTextInBrightColors && this._workCell.isBold() && fg & FgFlags.BOLD && this._workCell.getFgColor() < 8) { + fg += 8; + } // Flag combined chars with a bit mask so they're easily identifiable if (chars.length > 1) { diff --git a/addons/xterm-addon-webgl/src/atlas/CharAtlasUtils.ts b/addons/xterm-addon-webgl/src/atlas/CharAtlasUtils.ts index 857fa628..821b53a4 100644 --- a/addons/xterm-addon-webgl/src/atlas/CharAtlasUtils.ts +++ b/addons/xterm-addon-webgl/src/atlas/CharAtlasUtils.ts @@ -4,7 +4,7 @@ */ import { ICharAtlasConfig } from './Types'; -import { DEFAULT_COLOR } from 'common/buffer/Constants'; +import { DEFAULT_COLOR, Attributes } from 'common/buffer/Constants'; import { Terminal, FontWeight } from 'xterm'; import { IColorSet, IColor } from 'browser/Types'; @@ -57,5 +57,5 @@ export function configEquals(a: ICharAtlasConfig, b: ICharAtlasConfig): boolean } export function is256Color(colorCode: number): boolean { - return colorCode < DEFAULT_COLOR; + return (colorCode & Attributes.CM_MASK) === Attributes.CM_P16 || (colorCode & Attributes.CM_MASK) === Attributes.CM_P256; } diff --git a/addons/xterm-addon-webgl/src/atlas/WebglCharAtlas.ts b/addons/xterm-addon-webgl/src/atlas/WebglCharAtlas.ts index 8d8e995b..14cd017d 100644 --- a/addons/xterm-addon-webgl/src/atlas/WebglCharAtlas.ts +++ b/addons/xterm-addon-webgl/src/atlas/WebglCharAtlas.ts @@ -6,7 +6,7 @@ import { ICharAtlasConfig } from './Types'; import { DIM_OPACITY, INVERTED_DEFAULT_COLOR } from 'browser/renderer/atlas/Constants'; import { IRasterizedGlyph, IBoundingBox, IRasterizedGlyphSet } from '../Types'; -import { DEFAULT_COLOR, DEFAULT_ATTR } from 'common/buffer/Constants'; +import { DEFAULT_COLOR, DEFAULT_ATTR, FgFlags, Attributes } from 'common/buffer/Constants'; import { is256Color } from './CharAtlasUtils'; import { throwIfFalsy } from '../WebglUtils'; import { IColor } from 'browser/Types'; @@ -161,26 +161,26 @@ export class WebglCharAtlas implements IDisposable { return this._config.colors.ansi[idx]; } - private _getBackgroundColor(bg: number): IColor { + private _getBackgroundColor(bg: number, fg: number): 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. return TRANSPARENT_COLOR; - } else if (bg === INVERTED_DEFAULT_COLOR) { + } else if (fg & FgFlags.INVERSE) { return this._config.colors.foreground; } else if (is256Color(bg)) { - return this._getColorFromAnsiIndex(bg); + return this._getColorFromAnsiIndex(bg & Attributes.PCOLOR_MASK); } // TODO: Support true color return this._config.colors.background; } private _getForegroundColor(fg: number): IColor { - if (fg === INVERTED_DEFAULT_COLOR) { + if (fg & FgFlags.INVERSE) { return this._config.colors.background; } else if (is256Color(fg)) { - return this._getColorFromAnsiIndex(fg); + return this._getColorFromAnsiIndex(fg & Attributes.PCOLOR_MASK); } // TODO: Support true color return this._config.colors.foreground; @@ -202,7 +202,7 @@ export class WebglCharAtlas implements IDisposable { this._tmpCtx.save(); // draw the background - const backgroundColor = this._getBackgroundColor(bg); + const backgroundColor = this._getBackgroundColor(bg, fg); // Use a 'copy' composite operation to clear any existing glyph out of _tmpCtxWithAlpha, regardless of // transparency in backgroundColor this._tmpCtx.globalCompositeOperation = 'copy'; From 8f0f8bf827435d9268ef59d88b4a54fd3b99fdeb Mon Sep 17 00:00:00 2001 From: Daniel Date: Sat, 9 Nov 2019 22:31:10 -0800 Subject: [PATCH 65/92] Remove cache reliance on flags --- .../src/atlas/WebglCharAtlas.ts | 22 +++++++++---------- 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/addons/xterm-addon-webgl/src/atlas/WebglCharAtlas.ts b/addons/xterm-addon-webgl/src/atlas/WebglCharAtlas.ts index 14cd017d..f3858c69 100644 --- a/addons/xterm-addon-webgl/src/atlas/WebglCharAtlas.ts +++ b/addons/xterm-addon-webgl/src/atlas/WebglCharAtlas.ts @@ -6,7 +6,7 @@ import { ICharAtlasConfig } from './Types'; import { DIM_OPACITY, INVERTED_DEFAULT_COLOR } from 'browser/renderer/atlas/Constants'; import { IRasterizedGlyph, IBoundingBox, IRasterizedGlyphSet } from '../Types'; -import { DEFAULT_COLOR, DEFAULT_ATTR, FgFlags, Attributes } from 'common/buffer/Constants'; +import { DEFAULT_COLOR, DEFAULT_ATTR, FgFlags, Attributes, BgFlags } from 'common/buffer/Constants'; import { is256Color } from './CharAtlasUtils'; import { throwIfFalsy } from '../WebglUtils'; import { IColor } from 'browser/Types'; @@ -103,7 +103,7 @@ export class WebglCharAtlas implements IDisposable { protected _doWarmUp(): void { // Pre-fill with ASCII 33-126 for (let i = 33; i < 126; i++) { - const rasterizedGlyph = this._drawToCache(i, DEFAULT_ATTR, DEFAULT_COLOR, DEFAULT_COLOR); + const rasterizedGlyph = this._drawToCache(i, DEFAULT_COLOR, DEFAULT_COLOR); this._cacheMap[i] = { [DEFAULT_ATTR]: rasterizedGlyph }; @@ -131,7 +131,7 @@ export class WebglCharAtlas implements IDisposable { } let rasterizedGlyph = rasterizedGlyphSet[attr]; if (!rasterizedGlyph) { - rasterizedGlyph = this._drawToCache(chars, attr, bg, fg); + rasterizedGlyph = this._drawToCache(chars, bg, fg); rasterizedGlyphSet[attr] = rasterizedGlyph; } return rasterizedGlyph; @@ -148,7 +148,7 @@ export class WebglCharAtlas implements IDisposable { } let rasterizedGlyph = rasterizedGlyphSet[attr]; if (!rasterizedGlyph) { - rasterizedGlyph = this._drawToCache(code, attr, bg, fg); + rasterizedGlyph = this._drawToCache(code, bg, fg); rasterizedGlyphSet[attr] = rasterizedGlyph; } return rasterizedGlyph; @@ -186,18 +186,16 @@ export class WebglCharAtlas implements IDisposable { return this._config.colors.foreground; } - private _drawToCache(code: number, attr: number, bg: number, fg: number): IRasterizedGlyph; - private _drawToCache(chars: string, attr: number, bg: number, fg: number): IRasterizedGlyph; - private _drawToCache(codeOrChars: number | string, attr: number, bg: number, fg: number): IRasterizedGlyph { + private _drawToCache(code: number, bg: number, fg: number): IRasterizedGlyph; + private _drawToCache(chars: string, bg: number, fg: number): IRasterizedGlyph; + private _drawToCache(codeOrChars: number | string, bg: number, fg: number): IRasterizedGlyph { const chars = typeof codeOrChars === 'number' ? String.fromCharCode(codeOrChars) : codeOrChars; this.hasCanvasChanged = true; - const flags = attr >> 18; - - const bold = !!(flags & FLAGS.BOLD); - const dim = !!(flags & FLAGS.DIM); - const italic = !!(flags & FLAGS.ITALIC); + const bold = !!(fg & FgFlags.BOLD); + const dim = !!(bg & BgFlags.DIM); + const italic = !!(bg & BgFlags.ITALIC); this._tmpCtx.save(); From e15476704ca9fecbf93e607ce7253eedd040840e Mon Sep 17 00:00:00 2001 From: Daniel Date: Sat, 9 Nov 2019 22:43:25 -0800 Subject: [PATCH 66/92] Adopt new [bg][fg] map in char cache --- addons/xterm-addon-webgl/src/GlyphRenderer.ts | 14 ++++---- addons/xterm-addon-webgl/src/Types.d.ts | 2 +- addons/xterm-addon-webgl/src/WebglRenderer.ts | 3 +- .../src/atlas/WebglCharAtlas.ts | 32 ++++++++++++++----- 4 files changed, 34 insertions(+), 17 deletions(-) diff --git a/addons/xterm-addon-webgl/src/GlyphRenderer.ts b/addons/xterm-addon-webgl/src/GlyphRenderer.ts index bd4488dc..dd83131e 100644 --- a/addons/xterm-addon-webgl/src/GlyphRenderer.ts +++ b/addons/xterm-addon-webgl/src/GlyphRenderer.ts @@ -169,11 +169,11 @@ export class GlyphRenderer { return this._atlas ? this._atlas.beginFrame() : true; } - public updateCell(x: number, y: number, code: number, attr: number, bg: number, fg: number, chars: string): void { - this._updateCell(this._vertices.attributes, x, y, code, attr, bg, fg, chars); + public updateCell(x: number, y: number, code: number, bg: number, fg: number, chars: string): void { + this._updateCell(this._vertices.attributes, x, y, code, bg, fg, chars); } - private _updateCell(array: Float32Array, x: number, y: number, code: number | undefined, attr: number, bg: number, fg: number, chars?: string): void { + private _updateCell(array: Float32Array, x: number, y: number, code: number | undefined, bg: number, fg: number, chars?: string): void { const terminal = this._terminal; const i = (y * terminal.cols + x) * INDICES_PER_CELL; @@ -189,9 +189,9 @@ export class GlyphRenderer { throw new Error('atlas must be set before updating cell'); } if (chars && chars.length > 1) { - rasterizedGlyph = this._atlas.getRasterizedGlyphCombinedChar(chars, attr, bg, fg); + rasterizedGlyph = this._atlas.getRasterizedGlyphCombinedChar(chars, bg, fg); } else { - rasterizedGlyph = this._atlas.getRasterizedGlyph(code, attr, bg, fg); + rasterizedGlyph = this._atlas.getRasterizedGlyph(code, bg, fg); } // Fill empty if no glyph was found @@ -269,9 +269,9 @@ export class GlyphRenderer { line = terminal.buffer.getLine(row); } const chars = line!.getCell(x)!.char; - this._updateCell(this._vertices.selectionAttributes, x, y, model.cells[offset], attr, bg, fg, chars); + this._updateCell(this._vertices.selectionAttributes, x, y, model.cells[offset], bg, fg, chars); } else { - this._updateCell(this._vertices.selectionAttributes, x, y, model.cells[offset], attr, bg, fg); + this._updateCell(this._vertices.selectionAttributes, x, y, model.cells[offset], bg, fg); } } } diff --git a/addons/xterm-addon-webgl/src/Types.d.ts b/addons/xterm-addon-webgl/src/Types.d.ts index 6ebc8d64..f01092ee 100644 --- a/addons/xterm-addon-webgl/src/Types.d.ts +++ b/addons/xterm-addon-webgl/src/Types.d.ts @@ -4,7 +4,7 @@ */ export interface IRasterizedGlyphSet { - [flags: number]: IRasterizedGlyph; + [bg: number]: { [fg: number]: IRasterizedGlyph } | undefined; } /** diff --git a/addons/xterm-addon-webgl/src/WebglRenderer.ts b/addons/xterm-addon-webgl/src/WebglRenderer.ts index ef20f614..afdb7fa4 100644 --- a/addons/xterm-addon-webgl/src/WebglRenderer.ts +++ b/addons/xterm-addon-webgl/src/WebglRenderer.ts @@ -304,11 +304,12 @@ export class WebglRenderer extends Disposable implements IRenderer { } this._model.cells[i ] = code; + // TODO: Remove attr from model this._model.cells[i + 1] = attr; this._model.cells[i + 2] = bg; this._model.cells[i + 3] = fg; - this._glyphRenderer.updateCell(x, y, code, attr, bg, fg, chars); + this._glyphRenderer.updateCell(x, y, code, bg, fg, chars); } } this._rectangleRenderer.updateBackgrounds(this._model); diff --git a/addons/xterm-addon-webgl/src/atlas/WebglCharAtlas.ts b/addons/xterm-addon-webgl/src/atlas/WebglCharAtlas.ts index f3858c69..9d8eb286 100644 --- a/addons/xterm-addon-webgl/src/atlas/WebglCharAtlas.ts +++ b/addons/xterm-addon-webgl/src/atlas/WebglCharAtlas.ts @@ -4,7 +4,7 @@ */ import { ICharAtlasConfig } from './Types'; -import { DIM_OPACITY, INVERTED_DEFAULT_COLOR } from 'browser/renderer/atlas/Constants'; +import { DIM_OPACITY } from 'browser/renderer/atlas/Constants'; import { IRasterizedGlyph, IBoundingBox, IRasterizedGlyphSet } from '../Types'; import { DEFAULT_COLOR, DEFAULT_ATTR, FgFlags, Attributes, BgFlags } from 'common/buffer/Constants'; import { is256Color } from './CharAtlasUtils'; @@ -105,7 +105,9 @@ export class WebglCharAtlas implements IDisposable { for (let i = 33; i < 126; i++) { const rasterizedGlyph = this._drawToCache(i, DEFAULT_COLOR, DEFAULT_COLOR); this._cacheMap[i] = { - [DEFAULT_ATTR]: rasterizedGlyph + [DEFAULT_COLOR]: { + [DEFAULT_COLOR]: rasterizedGlyph + } }; } } @@ -123,16 +125,23 @@ export class WebglCharAtlas implements IDisposable { return false; } - public getRasterizedGlyphCombinedChar(chars: string, attr: number, bg: number, fg: number): IRasterizedGlyph { + public getRasterizedGlyphCombinedChar(chars: string, bg: number, fg: number): IRasterizedGlyph { let rasterizedGlyphSet = this._cacheMapCombined[chars]; if (!rasterizedGlyphSet) { rasterizedGlyphSet = {}; this._cacheMapCombined[chars] = rasterizedGlyphSet; } - let rasterizedGlyph = rasterizedGlyphSet[attr]; + let rasterizedGlyph: IRasterizedGlyph | undefined; + const rasterizedGlyphSetBg = rasterizedGlyphSet[bg]; + if (rasterizedGlyphSetBg) { + rasterizedGlyph = rasterizedGlyphSetBg[fg]; + } if (!rasterizedGlyph) { rasterizedGlyph = this._drawToCache(chars, bg, fg); - rasterizedGlyphSet[attr] = rasterizedGlyph; + if (!rasterizedGlyphSet[bg]) { + rasterizedGlyphSet[bg] = {}; + } + rasterizedGlyphSet[bg]![fg] = rasterizedGlyph; } return rasterizedGlyph; } @@ -140,16 +149,23 @@ export class WebglCharAtlas implements IDisposable { /** * Gets the glyphs texture coords, drawing the texture if it's not already */ - public getRasterizedGlyph(code: number, attr: number, bg: number, fg: number): IRasterizedGlyph { + public getRasterizedGlyph(code: number, bg: number, fg: number): IRasterizedGlyph { let rasterizedGlyphSet = this._cacheMap[code]; if (!rasterizedGlyphSet) { rasterizedGlyphSet = {}; this._cacheMap[code] = rasterizedGlyphSet; } - let rasterizedGlyph = rasterizedGlyphSet[attr]; + let rasterizedGlyph: IRasterizedGlyph | undefined; + const rasterizedGlyphSetBg = rasterizedGlyphSet[bg]; + if (rasterizedGlyphSetBg) { + rasterizedGlyph = rasterizedGlyphSetBg[fg]; + } if (!rasterizedGlyph) { rasterizedGlyph = this._drawToCache(code, bg, fg); - rasterizedGlyphSet[attr] = rasterizedGlyph; + if (!rasterizedGlyphSet[bg]) { + rasterizedGlyphSet[bg] = {}; + } + rasterizedGlyphSet[bg]![fg] = rasterizedGlyph; } return rasterizedGlyph; } From 9dd2ccb1ad87e8af70d970eb481316097cf8b9dc Mon Sep 17 00:00:00 2001 From: Daniel Date: Sat, 9 Nov 2019 22:48:28 -0800 Subject: [PATCH 67/92] Fix selection color to set correct mode --- addons/xterm-addon-webgl/src/GlyphRenderer.ts | 6 +++--- addons/xterm-addon-webgl/src/WebglRenderer.ts | 13 +++++++------ 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/addons/xterm-addon-webgl/src/GlyphRenderer.ts b/addons/xterm-addon-webgl/src/GlyphRenderer.ts index dd83131e..00b21629 100644 --- a/addons/xterm-addon-webgl/src/GlyphRenderer.ts +++ b/addons/xterm-addon-webgl/src/GlyphRenderer.ts @@ -10,7 +10,7 @@ import { INDICIES_PER_CELL } from './WebglRenderer'; import { COMBINED_CHAR_BIT_MASK } from './RenderModel'; import { fill } from 'common/TypedArrayUtils'; import { slice } from './TypedArray'; -import { NULL_CELL_CODE, WHITESPACE_CELL_CODE } from 'common/buffer/Constants'; +import { NULL_CELL_CODE, WHITESPACE_CELL_CODE, Attributes } from 'common/buffer/Constants'; import { getLuminance } from './ColorUtils'; import { Terminal, IBufferLine } from 'xterm'; import { IColorSet } from 'browser/Types'; @@ -223,8 +223,8 @@ export class GlyphRenderer { // TODO: Make fg and bg configurable, currently since the buffer doesn't // support truecolor the char atlas cannot store it. const lumi = getLuminance(this._colors.background); - const fg = lumi > 0.5 ? 7 : 0; - const bg = lumi > 0.5 ? 0 : 7; + const fg = (lumi > 0.5 ? 7 : 0) | Attributes.CM_P16; + const bg = (lumi > 0.5 ? 0 : 7) | Attributes.CM_P16; if (columnSelectMode) { const startCol = model.selection.startCol; diff --git a/addons/xterm-addon-webgl/src/WebglRenderer.ts b/addons/xterm-addon-webgl/src/WebglRenderer.ts index afdb7fa4..a9a2f860 100644 --- a/addons/xterm-addon-webgl/src/WebglRenderer.ts +++ b/addons/xterm-addon-webgl/src/WebglRenderer.ts @@ -271,15 +271,15 @@ export class WebglRenderer extends Disposable implements IRenderer { this._model.lineLengths[y] = x + 1; } - // Nothing has changed, no updates needed - if (this._model.cells[i] === code && this._model.cells[i + 1] === attr) { - continue; - } - - // Resolve bg and fg and cache in the model + // Resolve bg and fg let bg = this._workCell.bg; let fg = this._workCell.fg; + // Nothing has changed, no updates needed + if (this._model.cells[i] === code && this._model.cells[i + 2] === bg && this._model.cells[i + 3] === fg) { + continue; + } + // If inverse flag is on, the foreground should become the background. if (this._workCell.isInverse()) { const temp = bg; @@ -303,6 +303,7 @@ export class WebglRenderer extends Disposable implements IRenderer { code = code | COMBINED_CHAR_BIT_MASK; } + // Cache the results in the model this._model.cells[i ] = code; // TODO: Remove attr from model this._model.cells[i + 1] = attr; From a2bd0e8eaac09bfcf6eea556685f821a048f5e7d Mon Sep 17 00:00:00 2001 From: Daniel Date: Sat, 9 Nov 2019 22:50:22 -0800 Subject: [PATCH 68/92] Remove attr compat from webgl --- .../xterm-addon-webgl/src/CharDataCompat.ts | 28 ------------------- addons/xterm-addon-webgl/src/WebglRenderer.ts | 7 ++--- 2 files changed, 2 insertions(+), 33 deletions(-) delete mode 100644 addons/xterm-addon-webgl/src/CharDataCompat.ts diff --git a/addons/xterm-addon-webgl/src/CharDataCompat.ts b/addons/xterm-addon-webgl/src/CharDataCompat.ts deleted file mode 100644 index 13613c88..00000000 --- a/addons/xterm-addon-webgl/src/CharDataCompat.ts +++ /dev/null @@ -1,28 +0,0 @@ -/** - * Copyright (c) 2019 The xterm.js authors. All rights reserved. - * @license MIT - */ - -import { CellData } from 'common/buffer/CellData'; -import { FLAGS } from './Constants'; -import { IBufferLine } from 'common/Types'; - -export function getCompatAttr(bufferLine: IBufferLine, index: number): number { - // TODO: Need to move WebGL over to the new system and remove this block - const cell = new CellData(); - bufferLine.loadCell(index, cell); - const oldBg = cell.getBgColor() === -1 ? 256 : cell.getBgColor(); - const oldFg = cell.getFgColor() === -1 ? 256 : cell.getFgColor(); - const oldAttr = - (cell.isBold() ? FLAGS.BOLD : 0) | - (cell.isUnderline() ? FLAGS.UNDERLINE : 0) | - (cell.isBlink() ? FLAGS.BLINK : 0) | - (cell.isInverse() ? FLAGS.INVERSE : 0) | - (cell.isDim() ? FLAGS.DIM : 0) | - (cell.isItalic() ? FLAGS.ITALIC : 0); - const attrCompat = - oldBg | - (oldFg << 9) | - (oldAttr << 18); - return attrCompat; -} diff --git a/addons/xterm-addon-webgl/src/WebglRenderer.ts b/addons/xterm-addon-webgl/src/WebglRenderer.ts index a9a2f860..9acbc8df 100644 --- a/addons/xterm-addon-webgl/src/WebglRenderer.ts +++ b/addons/xterm-addon-webgl/src/WebglRenderer.ts @@ -14,14 +14,12 @@ import { IWebGL2RenderingContext } from './Types'; import { INVERTED_DEFAULT_COLOR } from 'browser/renderer/atlas/Constants'; import { RenderModel, COMBINED_CHAR_BIT_MASK } from './RenderModel'; import { Disposable } from 'common/Lifecycle'; -import { DEFAULT_COLOR, CHAR_DATA_CHAR_INDEX, CHAR_DATA_CODE_INDEX, NULL_CELL_CODE, FgFlags } from 'common/buffer/Constants'; +import { DEFAULT_COLOR, NULL_CELL_CODE, FgFlags } from 'common/buffer/Constants'; import { Terminal, IEvent } from 'xterm'; import { getLuminance } from './ColorUtils'; import { IRenderLayer } from './renderLayer/Types'; import { IRenderDimensions, IRenderer, IRequestRefreshRowsEvent } from 'browser/renderer/Types'; import { IColorSet } from 'browser/Types'; -import { FLAGS } from './Constants'; -import { getCompatAttr } from './CharDataCompat'; import { EventEmitter } from 'common/EventEmitter'; import { CellData } from 'common/buffer/CellData'; @@ -264,7 +262,6 @@ export class WebglRenderer extends Disposable implements IRenderer { const chars = this._workCell.getChars(); let code = this._workCell.getCode(); - const attr = getCompatAttr(line, x); // charData[CHAR_DATA_ATTR_INDEX]; const i = ((y * terminal.cols) + x) * INDICIES_PER_CELL; if (code !== NULL_CELL_CODE) { @@ -306,7 +303,7 @@ export class WebglRenderer extends Disposable implements IRenderer { // Cache the results in the model this._model.cells[i ] = code; // TODO: Remove attr from model - this._model.cells[i + 1] = attr; + this._model.cells[i + 1] = 0; this._model.cells[i + 2] = bg; this._model.cells[i + 3] = fg; From 2b5d229aabfa63e4d6e4d28b9fd66c754ee686c1 Mon Sep 17 00:00:00 2001 From: Daniel Date: Sat, 9 Nov 2019 22:56:42 -0800 Subject: [PATCH 69/92] Reduce size of model to 3 by removing attr --- addons/xterm-addon-webgl/src/Constants.ts | 12 ++---------- addons/xterm-addon-webgl/src/GlyphRenderer.ts | 3 --- addons/xterm-addon-webgl/src/RectangleRenderer.ts | 7 +++---- addons/xterm-addon-webgl/src/RenderModel.ts | 2 +- addons/xterm-addon-webgl/src/WebglRenderer.ts | 13 +++++++------ .../xterm-addon-webgl/src/atlas/WebglCharAtlas.ts | 3 +-- 6 files changed, 14 insertions(+), 26 deletions(-) diff --git a/addons/xterm-addon-webgl/src/Constants.ts b/addons/xterm-addon-webgl/src/Constants.ts index ab18b97a..bf39ec98 100644 --- a/addons/xterm-addon-webgl/src/Constants.ts +++ b/addons/xterm-addon-webgl/src/Constants.ts @@ -3,13 +3,5 @@ * @license MIT */ -// TODO: Should be removed after chardata workaround is fixed -export const enum FLAGS { - BOLD = 1, - UNDERLINE = 2, - BLINK = 4, - INVERSE = 8, - INVISIBLE = 16, - DIM = 32, - ITALIC = 64 -} +export const MODEL_BG_OFFSET = 2; +export const MODEL_FG_OFFSET = 3; diff --git a/addons/xterm-addon-webgl/src/GlyphRenderer.ts b/addons/xterm-addon-webgl/src/GlyphRenderer.ts index 00b21629..a28d5c10 100644 --- a/addons/xterm-addon-webgl/src/GlyphRenderer.ts +++ b/addons/xterm-addon-webgl/src/GlyphRenderer.ts @@ -260,9 +260,6 @@ export class GlyphRenderer { let line: IBufferLine | undefined; for (let x = startCol; x < endCol; x++) { const offset = (y * this._terminal.cols + x) * INDICIES_PER_CELL; - // Because the cache uses attr as a lookup key it needs to contain the selection colors as well - let attr = model.cells[offset + 1]; - attr = attr & ~0x3ffff | bg << 9 | fg; const code = model.cells[offset]; if (code & COMBINED_CHAR_BIT_MASK) { if (!line) { diff --git a/addons/xterm-addon-webgl/src/RectangleRenderer.ts b/addons/xterm-addon-webgl/src/RectangleRenderer.ts index 70b5ee3c..3a5f6417 100644 --- a/addons/xterm-addon-webgl/src/RectangleRenderer.ts +++ b/addons/xterm-addon-webgl/src/RectangleRenderer.ts @@ -6,12 +6,11 @@ import { createProgram, expandFloat32Array, PROJECTION_MATRIX, throwIfFalsy } from './WebglUtils'; import { IRenderModel, IWebGLVertexArrayObject, IWebGL2RenderingContext, ISelectionRenderModel } from './Types'; import { fill } from 'common/TypedArrayUtils'; -import { INVERTED_DEFAULT_COLOR } from 'browser/renderer/atlas/Constants'; -import { is256Color } from './atlas/CharAtlasUtils'; import { DEFAULT_COLOR, Attributes, FgFlags } from 'common/buffer/Constants'; import { Terminal } from 'xterm'; import { IColorSet, IColor } from 'browser/Types'; import { IRenderDimensions } from 'browser/renderer/Types'; +import { MODEL_FG_OFFSET, MODEL_BG_OFFSET } from './Constants'; const enum VertexAttribLocations { POSITION = 0, @@ -251,8 +250,8 @@ export class RectangleRenderer { let currentFg = DEFAULT_COLOR; for (let x = 0; x < terminal.cols; x++) { const modelIndex = ((y * terminal.cols) + x) * 4; - const bg = model.cells[modelIndex + 2]; - const fg = model.cells[modelIndex + 1]; + const bg = model.cells[modelIndex + MODEL_BG_OFFSET]; + const fg = model.cells[modelIndex + MODEL_FG_OFFSET]; if (bg !== currentBg) { // A rectangle needs to be drawn if going from non-default to another color // TODO: DEFAULT_COLOR probably isn't right anymore? diff --git a/addons/xterm-addon-webgl/src/RenderModel.ts b/addons/xterm-addon-webgl/src/RenderModel.ts index a9ee24e9..f55111d4 100644 --- a/addons/xterm-addon-webgl/src/RenderModel.ts +++ b/addons/xterm-addon-webgl/src/RenderModel.ts @@ -6,7 +6,7 @@ import { IRenderModel, ISelectionRenderModel } from './Types'; import { fill } from 'common/TypedArrayUtils'; -export const RENDER_MODEL_INDICIES_PER_CELL = 4; +export const RENDER_MODEL_INDICIES_PER_CELL = 3; export const COMBINED_CHAR_BIT_MASK = 0x80000000; diff --git a/addons/xterm-addon-webgl/src/WebglRenderer.ts b/addons/xterm-addon-webgl/src/WebglRenderer.ts index 9acbc8df..8bbd1b64 100644 --- a/addons/xterm-addon-webgl/src/WebglRenderer.ts +++ b/addons/xterm-addon-webgl/src/WebglRenderer.ts @@ -22,6 +22,7 @@ import { IRenderDimensions, IRenderer, IRequestRefreshRowsEvent } from 'browser/ import { IColorSet } from 'browser/Types'; import { EventEmitter } from 'common/EventEmitter'; import { CellData } from 'common/buffer/CellData'; +import { MODEL_BG_OFFSET, MODEL_FG_OFFSET } from './Constants'; export const INDICIES_PER_CELL = 4; @@ -273,7 +274,9 @@ export class WebglRenderer extends Disposable implements IRenderer { let fg = this._workCell.fg; // Nothing has changed, no updates needed - if (this._model.cells[i] === code && this._model.cells[i + 2] === bg && this._model.cells[i + 3] === fg) { + if (this._model.cells[i] === code && + this._model.cells[i + MODEL_BG_OFFSET] === bg && + this._model.cells[i + MODEL_FG_OFFSET] === fg) { continue; } @@ -301,11 +304,9 @@ export class WebglRenderer extends Disposable implements IRenderer { } // Cache the results in the model - this._model.cells[i ] = code; - // TODO: Remove attr from model - this._model.cells[i + 1] = 0; - this._model.cells[i + 2] = bg; - this._model.cells[i + 3] = fg; + this._model.cells[i] = code; + this._model.cells[i + MODEL_BG_OFFSET] = bg; + this._model.cells[i + MODEL_FG_OFFSET] = fg; this._glyphRenderer.updateCell(x, y, code, bg, fg, chars); } diff --git a/addons/xterm-addon-webgl/src/atlas/WebglCharAtlas.ts b/addons/xterm-addon-webgl/src/atlas/WebglCharAtlas.ts index 9d8eb286..b9c4a827 100644 --- a/addons/xterm-addon-webgl/src/atlas/WebglCharAtlas.ts +++ b/addons/xterm-addon-webgl/src/atlas/WebglCharAtlas.ts @@ -6,11 +6,10 @@ import { ICharAtlasConfig } from './Types'; import { DIM_OPACITY } from 'browser/renderer/atlas/Constants'; import { IRasterizedGlyph, IBoundingBox, IRasterizedGlyphSet } from '../Types'; -import { DEFAULT_COLOR, DEFAULT_ATTR, FgFlags, Attributes, BgFlags } from 'common/buffer/Constants'; +import { DEFAULT_COLOR, FgFlags, Attributes, BgFlags } from 'common/buffer/Constants'; import { is256Color } from './CharAtlasUtils'; import { throwIfFalsy } from '../WebglUtils'; import { IColor } from 'browser/Types'; -import { FLAGS } from '../Constants'; import { IDisposable } from 'xterm'; // In practice we're probably never going to exhaust a texture this large. For debugging purposes, From 39f0e17edf4ff0a64d51eea31f7e4c3a01db09a6 Mon Sep 17 00:00:00 2001 From: Daniel Date: Sat, 9 Nov 2019 23:05:39 -0800 Subject: [PATCH 70/92] Consolidate constants, fix rectangle rendering --- addons/xterm-addon-webgl/src/Constants.ts | 7 ------- addons/xterm-addon-webgl/src/GlyphRenderer.ts | 5 ++--- addons/xterm-addon-webgl/src/RectangleRenderer.ts | 8 ++++---- addons/xterm-addon-webgl/src/RenderModel.ts | 2 ++ addons/xterm-addon-webgl/src/WebglRenderer.ts | 15 ++++++--------- 5 files changed, 14 insertions(+), 23 deletions(-) delete mode 100644 addons/xterm-addon-webgl/src/Constants.ts diff --git a/addons/xterm-addon-webgl/src/Constants.ts b/addons/xterm-addon-webgl/src/Constants.ts deleted file mode 100644 index bf39ec98..00000000 --- a/addons/xterm-addon-webgl/src/Constants.ts +++ /dev/null @@ -1,7 +0,0 @@ -/** - * Copyright (c) 2019 The xterm.js authors. All rights reserved. - * @license MIT - */ - -export const MODEL_BG_OFFSET = 2; -export const MODEL_FG_OFFSET = 3; diff --git a/addons/xterm-addon-webgl/src/GlyphRenderer.ts b/addons/xterm-addon-webgl/src/GlyphRenderer.ts index a28d5c10..7ec80907 100644 --- a/addons/xterm-addon-webgl/src/GlyphRenderer.ts +++ b/addons/xterm-addon-webgl/src/GlyphRenderer.ts @@ -6,8 +6,7 @@ import { createProgram, PROJECTION_MATRIX, throwIfFalsy } from './WebglUtils'; import { WebglCharAtlas } from './atlas/WebglCharAtlas'; import { IWebGL2RenderingContext, IWebGLVertexArrayObject, IRenderModel, IRasterizedGlyph } from './Types'; -import { INDICIES_PER_CELL } from './WebglRenderer'; -import { COMBINED_CHAR_BIT_MASK } from './RenderModel'; +import { COMBINED_CHAR_BIT_MASK, RENDER_MODEL_INDICIES_PER_CELL } from './RenderModel'; import { fill } from 'common/TypedArrayUtils'; import { slice } from './TypedArray'; import { NULL_CELL_CODE, WHITESPACE_CELL_CODE, Attributes } from 'common/buffer/Constants'; @@ -259,7 +258,7 @@ export class GlyphRenderer { const row = y + terminal.buffer.viewportY; let line: IBufferLine | undefined; for (let x = startCol; x < endCol; x++) { - const offset = (y * this._terminal.cols + x) * INDICIES_PER_CELL; + const offset = (y * this._terminal.cols + x) * RENDER_MODEL_INDICIES_PER_CELL; const code = model.cells[offset]; if (code & COMBINED_CHAR_BIT_MASK) { if (!line) { diff --git a/addons/xterm-addon-webgl/src/RectangleRenderer.ts b/addons/xterm-addon-webgl/src/RectangleRenderer.ts index 3a5f6417..0a088034 100644 --- a/addons/xterm-addon-webgl/src/RectangleRenderer.ts +++ b/addons/xterm-addon-webgl/src/RectangleRenderer.ts @@ -10,7 +10,7 @@ import { DEFAULT_COLOR, Attributes, FgFlags } from 'common/buffer/Constants'; import { Terminal } from 'xterm'; import { IColorSet, IColor } from 'browser/Types'; import { IRenderDimensions } from 'browser/renderer/Types'; -import { MODEL_FG_OFFSET, MODEL_BG_OFFSET } from './Constants'; +import { RENDER_MODEL_BG_OFFSET, RENDER_MODEL_FG_OFFSET, RENDER_MODEL_INDICIES_PER_CELL } from './RenderModel'; const enum VertexAttribLocations { POSITION = 0, @@ -249,9 +249,9 @@ export class RectangleRenderer { let currentBg = DEFAULT_COLOR; let currentFg = DEFAULT_COLOR; for (let x = 0; x < terminal.cols; x++) { - const modelIndex = ((y * terminal.cols) + x) * 4; - const bg = model.cells[modelIndex + MODEL_BG_OFFSET]; - const fg = model.cells[modelIndex + MODEL_FG_OFFSET]; + const modelIndex = ((y * terminal.cols) + x) * RENDER_MODEL_INDICIES_PER_CELL; + const bg = model.cells[modelIndex + RENDER_MODEL_BG_OFFSET]; + const fg = model.cells[modelIndex + RENDER_MODEL_FG_OFFSET]; if (bg !== currentBg) { // A rectangle needs to be drawn if going from non-default to another color // TODO: DEFAULT_COLOR probably isn't right anymore? diff --git a/addons/xterm-addon-webgl/src/RenderModel.ts b/addons/xterm-addon-webgl/src/RenderModel.ts index f55111d4..2b48047f 100644 --- a/addons/xterm-addon-webgl/src/RenderModel.ts +++ b/addons/xterm-addon-webgl/src/RenderModel.ts @@ -7,6 +7,8 @@ import { IRenderModel, ISelectionRenderModel } from './Types'; import { fill } from 'common/TypedArrayUtils'; export const RENDER_MODEL_INDICIES_PER_CELL = 3; +export const RENDER_MODEL_BG_OFFSET = 1; +export const RENDER_MODEL_FG_OFFSET = 2; export const COMBINED_CHAR_BIT_MASK = 0x80000000; diff --git a/addons/xterm-addon-webgl/src/WebglRenderer.ts b/addons/xterm-addon-webgl/src/WebglRenderer.ts index 8bbd1b64..4d6428f5 100644 --- a/addons/xterm-addon-webgl/src/WebglRenderer.ts +++ b/addons/xterm-addon-webgl/src/WebglRenderer.ts @@ -12,7 +12,7 @@ import { WebglCharAtlas } from './atlas/WebglCharAtlas'; import { RectangleRenderer } from './RectangleRenderer'; import { IWebGL2RenderingContext } from './Types'; import { INVERTED_DEFAULT_COLOR } from 'browser/renderer/atlas/Constants'; -import { RenderModel, COMBINED_CHAR_BIT_MASK } from './RenderModel'; +import { RenderModel, COMBINED_CHAR_BIT_MASK, RENDER_MODEL_BG_OFFSET, RENDER_MODEL_FG_OFFSET, RENDER_MODEL_INDICIES_PER_CELL } from './RenderModel'; import { Disposable } from 'common/Lifecycle'; import { DEFAULT_COLOR, NULL_CELL_CODE, FgFlags } from 'common/buffer/Constants'; import { Terminal, IEvent } from 'xterm'; @@ -22,9 +22,6 @@ import { IRenderDimensions, IRenderer, IRequestRefreshRowsEvent } from 'browser/ import { IColorSet } from 'browser/Types'; import { EventEmitter } from 'common/EventEmitter'; import { CellData } from 'common/buffer/CellData'; -import { MODEL_BG_OFFSET, MODEL_FG_OFFSET } from './Constants'; - -export const INDICIES_PER_CELL = 4; export class WebglRenderer extends Disposable implements IRenderer { private _renderLayers: IRenderLayer[]; @@ -263,7 +260,7 @@ export class WebglRenderer extends Disposable implements IRenderer { const chars = this._workCell.getChars(); let code = this._workCell.getCode(); - const i = ((y * terminal.cols) + x) * INDICIES_PER_CELL; + const i = ((y * terminal.cols) + x) * RENDER_MODEL_INDICIES_PER_CELL; if (code !== NULL_CELL_CODE) { this._model.lineLengths[y] = x + 1; @@ -275,8 +272,8 @@ export class WebglRenderer extends Disposable implements IRenderer { // Nothing has changed, no updates needed if (this._model.cells[i] === code && - this._model.cells[i + MODEL_BG_OFFSET] === bg && - this._model.cells[i + MODEL_FG_OFFSET] === fg) { + this._model.cells[i + RENDER_MODEL_BG_OFFSET] === bg && + this._model.cells[i + RENDER_MODEL_FG_OFFSET] === fg) { continue; } @@ -305,8 +302,8 @@ export class WebglRenderer extends Disposable implements IRenderer { // Cache the results in the model this._model.cells[i] = code; - this._model.cells[i + MODEL_BG_OFFSET] = bg; - this._model.cells[i + MODEL_FG_OFFSET] = fg; + this._model.cells[i + RENDER_MODEL_BG_OFFSET] = bg; + this._model.cells[i + RENDER_MODEL_FG_OFFSET] = fg; this._glyphRenderer.updateCell(x, y, code, bg, fg, chars); } From 3773b22439b6630aac5c838a225e93cae91b7694 Mon Sep 17 00:00:00 2001 From: Daniel Date: Sat, 9 Nov 2019 23:41:00 -0800 Subject: [PATCH 71/92] Support true color... mostly --- .../src/RectangleRenderer.ts | 22 +++++++++------ .../src/atlas/WebglCharAtlas.ts | 27 ++++++++++++++++--- 2 files changed, 38 insertions(+), 11 deletions(-) diff --git a/addons/xterm-addon-webgl/src/RectangleRenderer.ts b/addons/xterm-addon-webgl/src/RectangleRenderer.ts index 0a088034..4e711846 100644 --- a/addons/xterm-addon-webgl/src/RectangleRenderer.ts +++ b/addons/xterm-addon-webgl/src/RectangleRenderer.ts @@ -11,6 +11,7 @@ import { Terminal } from 'xterm'; import { IColorSet, IColor } from 'browser/Types'; import { IRenderDimensions } from 'browser/renderer/Types'; import { RENDER_MODEL_BG_OFFSET, RENDER_MODEL_FG_OFFSET, RENDER_MODEL_INDICIES_PER_CELL } from './RenderModel'; +import { AttributeData } from 'common/buffer/AttributeData'; const enum VertexAttribLocations { POSITION = 0, @@ -274,26 +275,31 @@ export class RectangleRenderer { } private _updateRectangle(vertices: IVertices, offset: number, fg: number, bg: number, startX: number, endX: number, y: number): void { - let color: IColor | null = null; + let rgba: number | undefined; + const colorMode = bg & Attributes.CM_MASK; if (fg & FgFlags.INVERSE) { // Inverted color - color = this._colors.foreground; + rgba = this._colors.foreground.rgba; } else if ((bg & Attributes.CM_MASK) === Attributes.CM_P16 || (bg & Attributes.CM_MASK) === Attributes.CM_P256) { // 256 palette - color = this._colors.ansi[bg & Attributes.PCOLOR_MASK]; + rgba = this._colors.ansi[bg & Attributes.PCOLOR_MASK].rgba; + } else if (colorMode === Attributes.CM_RGB) { + // True color + // TODO: Use a switch instead? + rgba = (bg & Attributes.RGB_MASK) << 8; } else { - // TODO: Support true color // Default color - color = this._colors.background; + rgba = this._colors.background.rgba; } if (vertices.attributes.length < offset + 4) { vertices.attributes = expandFloat32Array(vertices.attributes, this._terminal.rows * this._terminal.cols * INDICES_PER_RECTANGLE); } const x1 = startX * this._dimensions.scaledCellWidth; const y1 = y * this._dimensions.scaledCellHeight; - const r = ((color.rgba >> 24) & 0xFF) / 255; - const g = ((color.rgba >> 16) & 0xFF) / 255; - const b = ((color.rgba >> 8 ) & 0xFF) / 255; + const r = ((rgba >> 24) & 0xFF) / 255; + const g = ((rgba >> 16) & 0xFF) / 255; + const b = ((rgba >> 8 ) & 0xFF) / 255; + console.log(r, g, b); this._addRectangle(vertices.attributes, offset, x1, y1, (endX - startX) * this._dimensions.scaledCellWidth, this._dimensions.scaledCellHeight, r, g, b, 1); } diff --git a/addons/xterm-addon-webgl/src/atlas/WebglCharAtlas.ts b/addons/xterm-addon-webgl/src/atlas/WebglCharAtlas.ts index b9c4a827..00b24988 100644 --- a/addons/xterm-addon-webgl/src/atlas/WebglCharAtlas.ts +++ b/addons/xterm-addon-webgl/src/atlas/WebglCharAtlas.ts @@ -11,6 +11,7 @@ import { is256Color } from './CharAtlasUtils'; import { throwIfFalsy } from '../WebglUtils'; import { IColor } from 'browser/Types'; import { IDisposable } from 'xterm'; +import { AttributeData } from 'common/buffer/AttributeData'; // 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. @@ -186,18 +187,33 @@ export class WebglCharAtlas implements IDisposable { return this._config.colors.foreground; } else if (is256Color(bg)) { return this._getColorFromAnsiIndex(bg & Attributes.PCOLOR_MASK); + } else if ((bg & Attributes.CM_MASK) === Attributes.CM_RGB) { + // TODO: Use a switch for the color mode? + // True color + const rgb = bg & Attributes.RGB_MASK; + const arr = AttributeData.toColorRGB(rgb); + // TODO: This object creation is slow + return { + rgba: rgb << 255, + css: `#${toPaddedHex(arr[0])}${toPaddedHex(arr[1])}${toPaddedHex(arr[2])}` + }; } - // TODO: Support true color return this._config.colors.background; } - private _getForegroundColor(fg: number): IColor { + private _getForegroundColor(fg: number): { css: string } { + // TODO: Just return the string value if (fg & FgFlags.INVERSE) { return this._config.colors.background; } else if (is256Color(fg)) { return this._getColorFromAnsiIndex(fg & Attributes.PCOLOR_MASK); + } else if ((fg & Attributes.CM_MASK) === Attributes.CM_RGB) { + // TODO: Use a switch for the color mode? + // TODO: True color + const rgb = fg & Attributes.RGB_MASK; + const arr = AttributeData.toColorRGB(rgb); + return { css: `#${toPaddedHex(arr[0])}${toPaddedHex(arr[1])}${toPaddedHex(arr[2])}` }; } - // TODO: Support true color return this._config.colors.foreground; } @@ -411,3 +427,8 @@ function clearColor(imageData: ImageData, color: IColor): boolean { } return isEmpty; } + +function toPaddedHex(c: number): string { + const s = c.toString(16); + return s.length < 2 ? '0' + s : s; +} From a2da8f9fbb3dd5a66bcf63f81222c15848168f42 Mon Sep 17 00:00:00 2001 From: Daniel Date: Sun, 10 Nov 2019 00:23:08 -0800 Subject: [PATCH 72/92] Fix true color glyphs not having background cleared --- addons/xterm-addon-webgl/src/atlas/WebglCharAtlas.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/xterm-addon-webgl/src/atlas/WebglCharAtlas.ts b/addons/xterm-addon-webgl/src/atlas/WebglCharAtlas.ts index 00b24988..0dc3b974 100644 --- a/addons/xterm-addon-webgl/src/atlas/WebglCharAtlas.ts +++ b/addons/xterm-addon-webgl/src/atlas/WebglCharAtlas.ts @@ -194,7 +194,7 @@ export class WebglCharAtlas implements IDisposable { const arr = AttributeData.toColorRGB(rgb); // TODO: This object creation is slow return { - rgba: rgb << 255, + rgba: rgb << 8, css: `#${toPaddedHex(arr[0])}${toPaddedHex(arr[1])}${toPaddedHex(arr[2])}` }; } From b13e9066bdc028045457fed73851d408dd0b06aa Mon Sep 17 00:00:00 2001 From: Daniel Date: Sun, 10 Nov 2019 00:29:50 -0800 Subject: [PATCH 73/92] Use a switch for color mode conditions --- .../src/RectangleRenderer.ts | 23 ++++---- .../src/atlas/WebglCharAtlas.ts | 54 +++++++++++-------- 2 files changed, 45 insertions(+), 32 deletions(-) diff --git a/addons/xterm-addon-webgl/src/RectangleRenderer.ts b/addons/xterm-addon-webgl/src/RectangleRenderer.ts index 4e711846..ed7e8f0c 100644 --- a/addons/xterm-addon-webgl/src/RectangleRenderer.ts +++ b/addons/xterm-addon-webgl/src/RectangleRenderer.ts @@ -280,17 +280,21 @@ export class RectangleRenderer { if (fg & FgFlags.INVERSE) { // Inverted color rgba = this._colors.foreground.rgba; - } else if ((bg & Attributes.CM_MASK) === Attributes.CM_P16 || (bg & Attributes.CM_MASK) === Attributes.CM_P256) { - // 256 palette - rgba = this._colors.ansi[bg & Attributes.PCOLOR_MASK].rgba; - } else if (colorMode === Attributes.CM_RGB) { - // True color - // TODO: Use a switch instead? - rgba = (bg & Attributes.RGB_MASK) << 8; } else { - // Default color - rgba = this._colors.background.rgba; + switch (colorMode) { + case Attributes.CM_P16: + case Attributes.CM_P256: + rgba = this._colors.ansi[bg & Attributes.PCOLOR_MASK].rgba; + break; + case Attributes.CM_RGB: + rgba = (bg & Attributes.RGB_MASK) << 8; + break; + case Attributes.CM_DEFAULT: + default: + rgba = this._colors.background.rgba; + } } + if (vertices.attributes.length < offset + 4) { vertices.attributes = expandFloat32Array(vertices.attributes, this._terminal.rows * this._terminal.cols * INDICES_PER_RECTANGLE); } @@ -299,7 +303,6 @@ export class RectangleRenderer { const r = ((rgba >> 24) & 0xFF) / 255; const g = ((rgba >> 16) & 0xFF) / 255; const b = ((rgba >> 8 ) & 0xFF) / 255; - console.log(r, g, b); this._addRectangle(vertices.attributes, offset, x1, y1, (endX - startX) * this._dimensions.scaledCellWidth, this._dimensions.scaledCellHeight, r, g, b, 1); } diff --git a/addons/xterm-addon-webgl/src/atlas/WebglCharAtlas.ts b/addons/xterm-addon-webgl/src/atlas/WebglCharAtlas.ts index 0dc3b974..9a923863 100644 --- a/addons/xterm-addon-webgl/src/atlas/WebglCharAtlas.ts +++ b/addons/xterm-addon-webgl/src/atlas/WebglCharAtlas.ts @@ -185,36 +185,46 @@ export class WebglCharAtlas implements IDisposable { return TRANSPARENT_COLOR; } else if (fg & FgFlags.INVERSE) { return this._config.colors.foreground; - } else if (is256Color(bg)) { - return this._getColorFromAnsiIndex(bg & Attributes.PCOLOR_MASK); - } else if ((bg & Attributes.CM_MASK) === Attributes.CM_RGB) { - // TODO: Use a switch for the color mode? - // True color - const rgb = bg & Attributes.RGB_MASK; - const arr = AttributeData.toColorRGB(rgb); - // TODO: This object creation is slow - return { - rgba: rgb << 8, - css: `#${toPaddedHex(arr[0])}${toPaddedHex(arr[1])}${toPaddedHex(arr[2])}` - }; } - return this._config.colors.background; + + const colorMode = bg & Attributes.CM_MASK; + switch (colorMode) { + case Attributes.CM_P16: + case Attributes.CM_P256: + return this._getColorFromAnsiIndex(bg & Attributes.PCOLOR_MASK); + case Attributes.CM_RGB: + const rgb = bg & Attributes.RGB_MASK; + const arr = AttributeData.toColorRGB(rgb); + // TODO: This object creation is slow + return { + rgba: rgb << 8, + css: `#${toPaddedHex(arr[0])}${toPaddedHex(arr[1])}${toPaddedHex(arr[2])}` + }; + case Attributes.CM_DEFAULT: + default: + return this._config.colors.background; + } } private _getForegroundColor(fg: number): { css: string } { // TODO: Just return the string value if (fg & FgFlags.INVERSE) { return this._config.colors.background; - } else if (is256Color(fg)) { - return this._getColorFromAnsiIndex(fg & Attributes.PCOLOR_MASK); - } else if ((fg & Attributes.CM_MASK) === Attributes.CM_RGB) { - // TODO: Use a switch for the color mode? - // TODO: True color - const rgb = fg & Attributes.RGB_MASK; - const arr = AttributeData.toColorRGB(rgb); - return { css: `#${toPaddedHex(arr[0])}${toPaddedHex(arr[1])}${toPaddedHex(arr[2])}` }; } - return this._config.colors.foreground; + + const colorMode = fg & Attributes.CM_MASK; + switch (colorMode) { + case Attributes.CM_P16: + case Attributes.CM_P256: + return this._getColorFromAnsiIndex(fg & Attributes.PCOLOR_MASK); + case Attributes.CM_RGB: + const rgb = fg & Attributes.RGB_MASK; + const arr = AttributeData.toColorRGB(rgb); + return { css: `#${toPaddedHex(arr[0])}${toPaddedHex(arr[1])}${toPaddedHex(arr[2])}` }; + case Attributes.CM_DEFAULT: + default: + return this._config.colors.foreground; + } } private _drawToCache(code: number, bg: number, fg: number): IRasterizedGlyph; From 149cc76803c79d525a69bea4862d872eae67e91c Mon Sep 17 00:00:00 2001 From: Daniel Date: Sun, 10 Nov 2019 00:30:58 -0800 Subject: [PATCH 74/92] Avoid extra object creation for foreground fill --- .../xterm-addon-webgl/src/atlas/WebglCharAtlas.ts | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/addons/xterm-addon-webgl/src/atlas/WebglCharAtlas.ts b/addons/xterm-addon-webgl/src/atlas/WebglCharAtlas.ts index 9a923863..69acfdbf 100644 --- a/addons/xterm-addon-webgl/src/atlas/WebglCharAtlas.ts +++ b/addons/xterm-addon-webgl/src/atlas/WebglCharAtlas.ts @@ -206,24 +206,23 @@ export class WebglCharAtlas implements IDisposable { } } - private _getForegroundColor(fg: number): { css: string } { - // TODO: Just return the string value + private _getForegroundCss(fg: number): string { if (fg & FgFlags.INVERSE) { - return this._config.colors.background; + return this._config.colors.background.css; } const colorMode = fg & Attributes.CM_MASK; switch (colorMode) { case Attributes.CM_P16: case Attributes.CM_P256: - return this._getColorFromAnsiIndex(fg & Attributes.PCOLOR_MASK); + return this._getColorFromAnsiIndex(fg & Attributes.PCOLOR_MASK).css; case Attributes.CM_RGB: const rgb = fg & Attributes.RGB_MASK; const arr = AttributeData.toColorRGB(rgb); - return { css: `#${toPaddedHex(arr[0])}${toPaddedHex(arr[1])}${toPaddedHex(arr[2])}` }; + return `#${toPaddedHex(arr[0])}${toPaddedHex(arr[1])}${toPaddedHex(arr[2])}`; case Attributes.CM_DEFAULT: default: - return this._config.colors.foreground; + return this._config.colors.foreground.css; } } @@ -256,7 +255,7 @@ export class WebglCharAtlas implements IDisposable { `${fontStyle} ${fontWeight} ${this._config.fontSize * this._config.devicePixelRatio}px ${this._config.fontFamily}`; this._tmpCtx.textBaseline = 'top'; - this._tmpCtx.fillStyle = this._getForegroundColor(fg).css; + this._tmpCtx.fillStyle = this._getForegroundCss(fg); // Apply alpha to dim the character if (dim) { From b56e29a35bfc7ff26c7fd7e702df744fecaa4a6e Mon Sep 17 00:00:00 2001 From: Daniel Date: Sun, 10 Nov 2019 00:39:46 -0800 Subject: [PATCH 75/92] Import clean up --- addons/xterm-addon-webgl/src/RectangleRenderer.ts | 1 - addons/xterm-addon-webgl/src/atlas/CharAtlasUtils.ts | 2 +- addons/xterm-addon-webgl/src/atlas/WebglCharAtlas.ts | 1 - 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/addons/xterm-addon-webgl/src/RectangleRenderer.ts b/addons/xterm-addon-webgl/src/RectangleRenderer.ts index ed7e8f0c..7746acb1 100644 --- a/addons/xterm-addon-webgl/src/RectangleRenderer.ts +++ b/addons/xterm-addon-webgl/src/RectangleRenderer.ts @@ -11,7 +11,6 @@ import { Terminal } from 'xterm'; import { IColorSet, IColor } from 'browser/Types'; import { IRenderDimensions } from 'browser/renderer/Types'; import { RENDER_MODEL_BG_OFFSET, RENDER_MODEL_FG_OFFSET, RENDER_MODEL_INDICIES_PER_CELL } from './RenderModel'; -import { AttributeData } from 'common/buffer/AttributeData'; const enum VertexAttribLocations { POSITION = 0, diff --git a/addons/xterm-addon-webgl/src/atlas/CharAtlasUtils.ts b/addons/xterm-addon-webgl/src/atlas/CharAtlasUtils.ts index 821b53a4..1fd4c4f6 100644 --- a/addons/xterm-addon-webgl/src/atlas/CharAtlasUtils.ts +++ b/addons/xterm-addon-webgl/src/atlas/CharAtlasUtils.ts @@ -4,7 +4,7 @@ */ import { ICharAtlasConfig } from './Types'; -import { DEFAULT_COLOR, Attributes } from 'common/buffer/Constants'; +import { Attributes } from 'common/buffer/Constants'; import { Terminal, FontWeight } from 'xterm'; import { IColorSet, IColor } from 'browser/Types'; diff --git a/addons/xterm-addon-webgl/src/atlas/WebglCharAtlas.ts b/addons/xterm-addon-webgl/src/atlas/WebglCharAtlas.ts index 69acfdbf..c364c3c1 100644 --- a/addons/xterm-addon-webgl/src/atlas/WebglCharAtlas.ts +++ b/addons/xterm-addon-webgl/src/atlas/WebglCharAtlas.ts @@ -7,7 +7,6 @@ import { ICharAtlasConfig } from './Types'; import { DIM_OPACITY } from 'browser/renderer/atlas/Constants'; import { IRasterizedGlyph, IBoundingBox, IRasterizedGlyphSet } from '../Types'; import { DEFAULT_COLOR, FgFlags, Attributes, BgFlags } from 'common/buffer/Constants'; -import { is256Color } from './CharAtlasUtils'; import { throwIfFalsy } from '../WebglUtils'; import { IColor } from 'browser/Types'; import { IDisposable } from 'xterm'; From 2cde01139781642f4b4d03ddedcc480f35137fa5 Mon Sep 17 00:00:00 2001 From: Daniel Date: Sun, 10 Nov 2019 00:49:10 -0800 Subject: [PATCH 76/92] Add retries to onRender test I've seen this flaky locally and in CI --- test/api/Terminal.api.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/api/Terminal.api.ts b/test/api/Terminal.api.ts index 0b05736d..40deecf2 100644 --- a/test/api/Terminal.api.ts +++ b/test/api/Terminal.api.ts @@ -356,7 +356,8 @@ describe('API Integration Tests', function(): void { await pollFor(page, `window.callCount`, 2); }); - it('onRender', async () => { + it('onRender', async function (): Promise { + this.retries(3); await openTerminal(); await timeout(20); // Ensure all init events are fired await page.evaluate(` From 811307562ba9b4eca27f930f51096d3bbeb7a2fc Mon Sep 17 00:00:00 2001 From: Daniel Date: Sun, 10 Nov 2019 00:56:43 -0800 Subject: [PATCH 77/92] Use actual default color for rectangle renderer --- addons/xterm-addon-webgl/src/RectangleRenderer.ts | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/addons/xterm-addon-webgl/src/RectangleRenderer.ts b/addons/xterm-addon-webgl/src/RectangleRenderer.ts index 7746acb1..d5e245c0 100644 --- a/addons/xterm-addon-webgl/src/RectangleRenderer.ts +++ b/addons/xterm-addon-webgl/src/RectangleRenderer.ts @@ -6,7 +6,7 @@ import { createProgram, expandFloat32Array, PROJECTION_MATRIX, throwIfFalsy } from './WebglUtils'; import { IRenderModel, IWebGLVertexArrayObject, IWebGL2RenderingContext, ISelectionRenderModel } from './Types'; import { fill } from 'common/TypedArrayUtils'; -import { DEFAULT_COLOR, Attributes, FgFlags } from 'common/buffer/Constants'; +import { Attributes, FgFlags } from 'common/buffer/Constants'; import { Terminal } from 'xterm'; import { IColorSet, IColor } from 'browser/Types'; import { IRenderDimensions } from 'browser/renderer/Types'; @@ -246,16 +246,15 @@ export class RectangleRenderer { for (let y = 0; y < terminal.rows; y++) { let currentStartX = -1; - let currentBg = DEFAULT_COLOR; - let currentFg = DEFAULT_COLOR; + let currentBg = 0; + let currentFg = 0; for (let x = 0; x < terminal.cols; x++) { const modelIndex = ((y * terminal.cols) + x) * RENDER_MODEL_INDICIES_PER_CELL; const bg = model.cells[modelIndex + RENDER_MODEL_BG_OFFSET]; const fg = model.cells[modelIndex + RENDER_MODEL_FG_OFFSET]; if (bg !== currentBg) { // A rectangle needs to be drawn if going from non-default to another color - // TODO: DEFAULT_COLOR probably isn't right anymore? - if (currentBg !== DEFAULT_COLOR) { + if (currentBg !== 0) { const offset = rectangleCount++ * INDICES_PER_RECTANGLE; this._updateRectangle(vertices, offset, currentFg, currentBg, currentStartX, x, y); } @@ -265,7 +264,7 @@ export class RectangleRenderer { } } // Finish rectangle if it's still going - if (currentBg !== DEFAULT_COLOR) { + if (currentBg !== 0) { const offset = rectangleCount++ * INDICES_PER_RECTANGLE; this._updateRectangle(vertices, offset, currentFg, currentBg, currentStartX, terminal.cols, y); } From 01628362f566524e55aa15f58f3ac1fd5178336c Mon Sep 17 00:00:00 2001 From: Daniel Date: Sun, 10 Nov 2019 01:19:27 -0800 Subject: [PATCH 78/92] Support selection color --- addons/xterm-addon-webgl/src/ColorUtils.ts | 14 ----------- addons/xterm-addon-webgl/src/GlyphRenderer.ts | 23 ++++++++----------- addons/xterm-addon-webgl/src/WebglRenderer.ts | 14 ----------- 3 files changed, 9 insertions(+), 42 deletions(-) delete mode 100644 addons/xterm-addon-webgl/src/ColorUtils.ts diff --git a/addons/xterm-addon-webgl/src/ColorUtils.ts b/addons/xterm-addon-webgl/src/ColorUtils.ts deleted file mode 100644 index 80372e65..00000000 --- a/addons/xterm-addon-webgl/src/ColorUtils.ts +++ /dev/null @@ -1,14 +0,0 @@ -/** - * @license MIT - * Copyright (c) 2018 The xterm.js authors. All rights reserved. - */ - -import { IColor } from 'browser/Types'; - -export function getLuminance(color: IColor): number { - // Coefficients taken from: https://www.w3.org/TR/AERT/#color-contrast - const r = color.rgba >> 24 & 0xff; - const g = color.rgba >> 16 & 0xff; - const b = color.rgba >> 8 & 0xff; - return (0.299 * r + 0.587 * g + 0.114 * b) / 255; -} diff --git a/addons/xterm-addon-webgl/src/GlyphRenderer.ts b/addons/xterm-addon-webgl/src/GlyphRenderer.ts index 7ec80907..3ee9cdb5 100644 --- a/addons/xterm-addon-webgl/src/GlyphRenderer.ts +++ b/addons/xterm-addon-webgl/src/GlyphRenderer.ts @@ -6,11 +6,10 @@ import { createProgram, PROJECTION_MATRIX, throwIfFalsy } from './WebglUtils'; import { WebglCharAtlas } from './atlas/WebglCharAtlas'; import { IWebGL2RenderingContext, IWebGLVertexArrayObject, IRenderModel, IRasterizedGlyph } from './Types'; -import { COMBINED_CHAR_BIT_MASK, RENDER_MODEL_INDICIES_PER_CELL } from './RenderModel'; +import { COMBINED_CHAR_BIT_MASK, RENDER_MODEL_INDICIES_PER_CELL, RENDER_MODEL_FG_OFFSET } from './RenderModel'; import { fill } from 'common/TypedArrayUtils'; import { slice } from './TypedArray'; import { NULL_CELL_CODE, WHITESPACE_CELL_CODE, Attributes } from 'common/buffer/Constants'; -import { getLuminance } from './ColorUtils'; import { Terminal, IBufferLine } from 'xterm'; import { IColorSet } from 'browser/Types'; import { IRenderDimensions } from 'browser/renderer/Types'; @@ -219,41 +218,37 @@ export class GlyphRenderer { this._vertices.selectionAttributes = slice(this._vertices.attributes, 0); - // TODO: Make fg and bg configurable, currently since the buffer doesn't - // support truecolor the char atlas cannot store it. - const lumi = getLuminance(this._colors.background); - const fg = (lumi > 0.5 ? 7 : 0) | Attributes.CM_P16; - const bg = (lumi > 0.5 ? 0 : 7) | Attributes.CM_P16; + const bg = (this._colors.selection.rgba >>> 8) | Attributes.CM_RGB; if (columnSelectMode) { const startCol = model.selection.startCol; const width = model.selection.endCol - startCol; const height = model.selection.viewportCappedEndRow - model.selection.viewportCappedStartRow + 1; for (let y = model.selection.viewportCappedStartRow; y < model.selection.viewportCappedStartRow + height; y++) { - this._updateSelectionRange(startCol, startCol + width, y, model, bg, fg); + this._updateSelectionRange(startCol, startCol + width, y, model, bg); } } else { // Draw first row const startCol = model.selection.viewportStartRow === model.selection.viewportCappedStartRow ? model.selection.startCol : 0; const startRowEndCol = model.selection.viewportCappedStartRow === model.selection.viewportCappedEndRow ? model.selection.endCol : terminal.cols; - this._updateSelectionRange(startCol, startRowEndCol, model.selection.viewportCappedStartRow, model, bg, fg); + this._updateSelectionRange(startCol, startRowEndCol, model.selection.viewportCappedStartRow, model, bg); // Draw middle rows const middleRowsCount = Math.max(model.selection.viewportCappedEndRow - model.selection.viewportCappedStartRow - 1, 0); for (let y = model.selection.viewportCappedStartRow + 1; y <= model.selection.viewportCappedStartRow + middleRowsCount; y++) { - this._updateSelectionRange(0, startRowEndCol, y, model, bg, fg); + this._updateSelectionRange(0, startRowEndCol, y, model, bg); } // Draw final row if (model.selection.viewportCappedStartRow !== model.selection.viewportCappedEndRow) { // Only draw viewportEndRow if it's not the same as viewportStartRow const endCol = model.selection.viewportEndRow === model.selection.viewportCappedEndRow ? model.selection.endCol : terminal.cols; - this._updateSelectionRange(0, endCol, model.selection.viewportCappedEndRow, model, bg, fg); + this._updateSelectionRange(0, endCol, model.selection.viewportCappedEndRow, model, bg); } } } - private _updateSelectionRange(startCol: number, endCol: number, y: number, model: IRenderModel, bg: number, fg: number): void { + private _updateSelectionRange(startCol: number, endCol: number, y: number, model: IRenderModel, bg: number): void { const terminal = this._terminal; const row = y + terminal.buffer.viewportY; let line: IBufferLine | undefined; @@ -265,9 +260,9 @@ export class GlyphRenderer { line = terminal.buffer.getLine(row); } const chars = line!.getCell(x)!.char; - this._updateCell(this._vertices.selectionAttributes, x, y, model.cells[offset], bg, fg, chars); + this._updateCell(this._vertices.selectionAttributes, x, y, model.cells[offset], bg, model.cells[offset + RENDER_MODEL_FG_OFFSET], chars); } else { - this._updateCell(this._vertices.selectionAttributes, x, y, model.cells[offset], bg, fg); + this._updateCell(this._vertices.selectionAttributes, x, y, model.cells[offset], bg, model.cells[offset + RENDER_MODEL_FG_OFFSET]); } } } diff --git a/addons/xterm-addon-webgl/src/WebglRenderer.ts b/addons/xterm-addon-webgl/src/WebglRenderer.ts index 4d6428f5..5eae61db 100644 --- a/addons/xterm-addon-webgl/src/WebglRenderer.ts +++ b/addons/xterm-addon-webgl/src/WebglRenderer.ts @@ -16,7 +16,6 @@ import { RenderModel, COMBINED_CHAR_BIT_MASK, RENDER_MODEL_BG_OFFSET, RENDER_MOD import { Disposable } from 'common/Lifecycle'; import { DEFAULT_COLOR, NULL_CELL_CODE, FgFlags } from 'common/buffer/Constants'; import { Terminal, IEvent } from 'xterm'; -import { getLuminance } from './ColorUtils'; import { IRenderLayer } from './renderLayer/Types'; import { IRenderDimensions, IRenderer, IRequestRefreshRowsEvent } from 'browser/renderer/Types'; import { IColorSet } from 'browser/Types'; @@ -52,8 +51,6 @@ export class WebglRenderer extends Disposable implements IRenderer { this._core = (this._terminal)._core; - this._applyBgLuminanceBasedSelection(); - this._renderLayers = [ new LinkRenderLayer(this._core.screenElement, 2, this._colors, this._core), new CursorRenderLayer(this._core.screenElement, 3, this._colors, this._onRequestRefreshRows) @@ -101,20 +98,9 @@ export class WebglRenderer extends Disposable implements IRenderer { super.dispose(); } - private _applyBgLuminanceBasedSelection(): void { - // HACK: This is needed until webgl renderer adds support for selection colors - if (getLuminance(this._colors.background) > 0.5) { - this._colors.selection = { css: '#000', rgba: 255 }; - } else { - this._colors.selection = { css: '#fff', rgba: 4294967295 }; - } - } - public setColors(colors: IColorSet): void { this._colors = colors; - this._applyBgLuminanceBasedSelection(); - // Clear layers and force a full render this._renderLayers.forEach(l => { l.setColors(this._terminal, this._colors); From 8bf837795ffff9f70b17e0714f13e173bd77cb7f Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sun, 10 Nov 2019 10:29:50 -0800 Subject: [PATCH 79/92] Add tests for webgl colors 16-255 --- .../src/WebglRenderer.api.ts | 81 +++++++++++++++---- 1 file changed, 67 insertions(+), 14 deletions(-) diff --git a/addons/xterm-addon-webgl/src/WebglRenderer.api.ts b/addons/xterm-addon-webgl/src/WebglRenderer.api.ts index 51dae37a..3f7dee35 100644 --- a/addons/xterm-addon-webgl/src/WebglRenderer.api.ts +++ b/addons/xterm-addon-webgl/src/WebglRenderer.api.ts @@ -7,6 +7,7 @@ import * as puppeteer from 'puppeteer'; import { ITerminalOptions } from '../../../src/Types'; import { ITheme } from 'xterm'; import { assert } from 'chai'; +import deepEqual = require('deep-equal'); const APP = 'http://127.0.0.1:3000/test'; @@ -15,7 +16,7 @@ let page: puppeteer.Page; const width = 800; const height = 600; -describe('WebGL Renderer Integration Tests', function(): void { +describe.only('WebGL Renderer Integration Tests', function(): void { it('dispose removes renderer canvases', async () => { await setupBrowser(); assert.equal(await page.evaluate(`document.querySelectorAll('.xterm canvas').length`), 3); @@ -29,7 +30,7 @@ describe('WebGL Renderer Integration Tests', function(): void { after(async () => browser.close()); beforeEach(async () => page.evaluate(`window.term.reset()`)); - it('foreground colors normal', async () => { + it('foreground 0-15', async () => { const theme: ITheme = { black: '#010203', red: '#040506', @@ -52,7 +53,7 @@ describe('WebGL Renderer Integration Tests', function(): void { await pollFor(page, () => getCellColor(8, 1), [22, 23, 24, 255]); }); - it('foreground colors bright', async () => { + it('foreground 0-15 bright', async () => { const theme: ITheme = { brightBlack: '#010203', brightRed: '#040506', @@ -75,7 +76,7 @@ describe('WebGL Renderer Integration Tests', function(): void { await pollFor(page, () => getCellColor(8, 1), [22, 23, 24, 255]); }); - it('background colors normal', async () => { + it('background 0-15 normal', async () => { const theme: ITheme = { black: '#010203', red: '#040506', @@ -98,7 +99,7 @@ describe('WebGL Renderer Integration Tests', function(): void { await pollFor(page, () => getCellColor(8, 1), [22, 23, 24, 255]); }); - it('background colors bright', async () => { + it('background 0-15 bright', async () => { const theme: ITheme = { brightBlack: '#010203', brightRed: '#040506', @@ -120,6 +121,46 @@ describe('WebGL Renderer Integration Tests', function(): void { await pollFor(page, () => getCellColor(7, 1), [19, 20, 21, 255]); await pollFor(page, () => getCellColor(8, 1), [22, 23, 24, 255]); }); + + it('background 16-255', async function(): Promise { + let data = ''; + for (let x = 0; x < 240 / 16; x++) { + for (let y = 0; y < 16; y++) { + data += `\\x1b[48;5;${16 + x * 16 + y}m \x1b[0m`; + } + data += '\\r\\n'; + } + await writeSync(data); + for (let y = 0; y < 240 / 16; y++) { + for (let x = 0; x < 16; x++) { + const cssColor = COLORS_16_TO_255[y * 16 + x]; + const r = parseInt(cssColor.substr(1, 2), 16); + const g = parseInt(cssColor.substr(3, 2), 16); + const b = parseInt(cssColor.substr(5, 2), 16); + await pollFor(page, () => getCellColor(x + 1, y + 1), [r, g, b, 255]); + } + } + }); + + it('foreground 16-255', async function(): Promise { + let data = ''; + for (let x = 0; x < 240 / 16; x++) { + for (let y = 0; y < 16; y++) { + data += `\\x1b[38;5;${16 + x * 16 + y}m█\x1b[0m`; + } + data += '\\r\\n'; + } + await writeSync(data); + for (let y = 0; y < 240 / 16; y++) { + for (let x = 0; x < 16; x++) { + const cssColor = COLORS_16_TO_255[y * 16 + x]; + const r = parseInt(cssColor.substr(1, 2), 16); + const g = parseInt(cssColor.substr(3, 2), 16); + const b = parseInt(cssColor.substr(5, 2), 16); + await pollFor(page, () => getCellColor(x + 1, y + 1), [r, g, b, 255]); + } + } + }); }); }); @@ -168,20 +209,32 @@ async function setupBrowser(): Promise { `); } -async function pollFor(page: puppeteer.Page, evalOrFn: string | (() => Promise), val: T, preFn?: () => Promise): Promise { +export async function pollFor(page: puppeteer.Page, evalOrFn: string | (() => Promise), val: T, preFn?: () => Promise): Promise { if (preFn) { await preFn(); } const result = typeof evalOrFn === 'string' ? await page.evaluate(evalOrFn) : await evalOrFn(); - let equal = false; - if (typeof result === 'object') { - equal = Object.keys(result).every(e => result[e] === (val as any)[e]); - } else { - equal = result === val; - } - if (!equal) { + if (!deepEqual(result, val)) { return new Promise(r => { - setTimeout(() => r(pollFor(page, evalOrFn, val, preFn)), 10); + setTimeout(() => r(pollFor(page, evalOrFn, val, preFn)), 1); }); } } + +const COLORS_16_TO_255 = [ + '#000000', '#00005f', '#000087', '#0000af', '#0000d7', '#0000ff', '#005f00', '#005f5f', '#005f87', '#005faf', '#005fd7', '#005fff', '#008700', '#00875f', '#008787', '#0087af', + '#0087d7', '#0087ff', '#00af00', '#00af5f', '#00af87', '#00afaf', '#00afd7', '#00afff', '#00d700', '#00d75f', '#00d787', '#00d7af', '#00d7d7', '#00d7ff', '#00ff00', '#00ff5f', + '#00ff87', '#00ffaf', '#00ffd7', '#00ffff', '#5f0000', '#5f005f', '#5f0087', '#5f00af', '#5f00d7', '#5f00ff', '#5f5f00', '#5f5f5f', '#5f5f87', '#5f5faf', '#5f5fd7', '#5f5fff', + '#5f8700', '#5f875f', '#5f8787', '#5f87af', '#5f87d7', '#5f87ff', '#5faf00', '#5faf5f', '#5faf87', '#5fafaf', '#5fafd7', '#5fafff', '#5fd700', '#5fd75f', '#5fd787', '#5fd7af', + '#5fd7d7', '#5fd7ff', '#5fff00', '#5fff5f', '#5fff87', '#5fffaf', '#5fffd7', '#5fffff', '#870000', '#87005f', '#870087', '#8700af', '#8700d7', '#8700ff', '#875f00', '#875f5f', + '#875f87', '#875faf', '#875fd7', '#875fff', '#878700', '#87875f', '#878787', '#8787af', '#8787d7', '#8787ff', '#87af00', '#87af5f', '#87af87', '#87afaf', '#87afd7', '#87afff', + '#87d700', '#87d75f', '#87d787', '#87d7af', '#87d7d7', '#87d7ff', '#87ff00', '#87ff5f', '#87ff87', '#87ffaf', '#87ffd7', '#87ffff', '#af0000', '#af005f', '#af0087', '#af00af', + '#af00d7', '#af00ff', '#af5f00', '#af5f5f', '#af5f87', '#af5faf', '#af5fd7', '#af5fff', '#af8700', '#af875f', '#af8787', '#af87af', '#af87d7', '#af87ff', '#afaf00', '#afaf5f', + '#afaf87', '#afafaf', '#afafd7', '#afafff', '#afd700', '#afd75f', '#afd787', '#afd7af', '#afd7d7', '#afd7ff', '#afff00', '#afff5f', '#afff87', '#afffaf', '#afffd7', '#afffff', + '#d70000', '#d7005f', '#d70087', '#d700af', '#d700d7', '#d700ff', '#d75f00', '#d75f5f', '#d75f87', '#d75faf', '#d75fd7', '#d75fff', '#d78700', '#d7875f', '#d78787', '#d787af', + '#d787d7', '#d787ff', '#d7af00', '#d7af5f', '#d7af87', '#d7afaf', '#d7afd7', '#d7afff', '#d7d700', '#d7d75f', '#d7d787', '#d7d7af', '#d7d7d7', '#d7d7ff', '#d7ff00', '#d7ff5f', + '#d7ff87', '#d7ffaf', '#d7ffd7', '#d7ffff', '#ff0000', '#ff005f', '#ff0087', '#ff00af', '#ff00d7', '#ff00ff', '#ff5f00', '#ff5f5f', '#ff5f87', '#ff5faf', '#ff5fd7', '#ff5fff', + '#ff8700', '#ff875f', '#ff8787', '#ff87af', '#ff87d7', '#ff87ff', '#ffaf00', '#ffaf5f', '#ffaf87', '#ffafaf', '#ffafd7', '#ffafff', '#ffd700', '#ffd75f', '#ffd787', '#ffd7af', + '#ffd7d7', '#ffd7ff', '#ffff00', '#ffff5f', '#ffff87', '#ffffaf', '#ffffd7', '#ffffff', '#080808', '#121212', '#1c1c1c', '#262626', '#303030', '#3a3a3a', '#444444', '#4e4e4e', + '#585858', '#626262', '#6c6c6c', '#767676', '#808080', '#8a8a8a', '#949494', '#9e9e9e', '#a8a8a8', '#b2b2b2', '#bcbcbc', '#c6c6c6', '#d0d0d0', '#dadada', '#e4e4e4', '#eeeeee' +]; From 753ad30dbc0b48b96483d31fbdbf38d18c1f5d3d Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sun, 10 Nov 2019 10:38:19 -0800 Subject: [PATCH 80/92] Add true color fg tests for webgl --- .../src/WebglRenderer.api.ts | 88 +++++++++++++++++-- 1 file changed, 80 insertions(+), 8 deletions(-) diff --git a/addons/xterm-addon-webgl/src/WebglRenderer.api.ts b/addons/xterm-addon-webgl/src/WebglRenderer.api.ts index 3f7dee35..71add30a 100644 --- a/addons/xterm-addon-webgl/src/WebglRenderer.api.ts +++ b/addons/xterm-addon-webgl/src/WebglRenderer.api.ts @@ -122,11 +122,11 @@ describe.only('WebGL Renderer Integration Tests', function(): void { await pollFor(page, () => getCellColor(8, 1), [22, 23, 24, 255]); }); - it('background 16-255', async function(): Promise { + it('background 16-255', async () => { let data = ''; - for (let x = 0; x < 240 / 16; x++) { - for (let y = 0; y < 16; y++) { - data += `\\x1b[48;5;${16 + x * 16 + y}m \x1b[0m`; + for (let y = 0; y < 240 / 16; y++) { + for (let x = 0; x < 16; x++) { + data += `\\x1b[48;5;${16 + y * 16 + x}m \x1b[0m`; } data += '\\r\\n'; } @@ -142,11 +142,11 @@ describe.only('WebGL Renderer Integration Tests', function(): void { } }); - it('foreground 16-255', async function(): Promise { + it('foreground 16-255', async () => { let data = ''; - for (let x = 0; x < 240 / 16; x++) { - for (let y = 0; y < 16; y++) { - data += `\\x1b[38;5;${16 + x * 16 + y}m█\x1b[0m`; + for (let y = 0; y < 240 / 16; y++) { + for (let x = 0; x < 16; x++) { + data += `\\x1b[38;5;${16 + y * 16 + x}m█\x1b[0m`; } data += '\\r\\n'; } @@ -161,6 +161,78 @@ describe.only('WebGL Renderer Integration Tests', function(): void { } } }); + + it('foreground true color red', async () => { + let data = ''; + for (let y = 0; y < 16; y++) { + for (let x = 0; x < 16; x++) { + const i = y * 16 + x; + data += `\\x1b[38;2;${i};0;0m█\x1b[0m`; + } + data += '\\r\\n'; + } + await writeSync(data); + for (let y = 0; y < 16; y++) { + for (let x = 0; x < 16; x++) { + const i = y * 16 + x; + await pollFor(page, () => getCellColor(x + 1, y + 1), [i, 0, 0, 255]); + } + } + }); + + it('foreground true color green', async () => { + let data = ''; + for (let y = 0; y < 16; y++) { + for (let x = 0; x < 16; x++) { + const i = y * 16 + x; + data += `\\x1b[38;2;0;${i};0m█\x1b[0m`; + } + data += '\\r\\n'; + } + await writeSync(data); + for (let y = 0; y < 16; y++) { + for (let x = 0; x < 16; x++) { + const i = y * 16 + x; + await pollFor(page, () => getCellColor(x + 1, y + 1), [0, i, 0, 255]); + } + } + }); + + it('foreground true color blue', async () => { + let data = ''; + for (let y = 0; y < 16; y++) { + for (let x = 0; x < 16; x++) { + const i = y * 16 + x; + data += `\\x1b[38;2;0;0;${i}m█\x1b[0m`; + } + data += '\\r\\n'; + } + await writeSync(data); + for (let y = 0; y < 16; y++) { + for (let x = 0; x < 16; x++) { + const i = y * 16 + x; + await pollFor(page, () => getCellColor(x + 1, y + 1), [0, 0, i, 255]); + } + } + }); + + it('foreground true color grey', async () => { + let data = ''; + for (let y = 0; y < 16; y++) { + for (let x = 0; x < 16; x++) { + const i = y * 16 + x; + data += `\\x1b[38;2;${i};${i};${i}m█\x1b[0m`; + } + data += '\\r\\n'; + } + await writeSync(data); + for (let y = 0; y < 16; y++) { + for (let x = 0; x < 16; x++) { + const i = y * 16 + x; + await pollFor(page, () => getCellColor(x + 1, y + 1), [i, i, i, 255]); + } + } + }); }); }); From 350ca8d5fb08000857e6a792f098560d257fac6b Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sun, 10 Nov 2019 10:39:56 -0800 Subject: [PATCH 81/92] Organize tests --- .../src/WebglRenderer.api.ts | 54 +++++++++---------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/addons/xterm-addon-webgl/src/WebglRenderer.api.ts b/addons/xterm-addon-webgl/src/WebglRenderer.api.ts index 71add30a..bb2f5969 100644 --- a/addons/xterm-addon-webgl/src/WebglRenderer.api.ts +++ b/addons/xterm-addon-webgl/src/WebglRenderer.api.ts @@ -53,6 +53,29 @@ describe.only('WebGL Renderer Integration Tests', function(): void { await pollFor(page, () => getCellColor(8, 1), [22, 23, 24, 255]); }); + it('background 0-15', async () => { + const theme: ITheme = { + black: '#010203', + red: '#040506', + green: '#070809', + yellow: '#0a0b0c', + blue: '#0d0e0f', + magenta: '#101112', + cyan: '#131415', + white: '#161718' + }; + await page.evaluate(`window.term.setOption('theme', ${JSON.stringify(theme)});`); + await writeSync(`\\x1b[40m \\x1b[41m \\x1b[42m \\x1b[43m \\x1b[44m \\x1b[45m \\x1b[46m \\x1b[47m `); + await pollFor(page, () => getCellColor(1, 1), [1, 2, 3, 255]); + await pollFor(page, () => getCellColor(2, 1), [4, 5, 6, 255]); + await pollFor(page, () => getCellColor(3, 1), [7, 8, 9, 255]); + await pollFor(page, () => getCellColor(4, 1), [10, 11, 12, 255]); + await pollFor(page, () => getCellColor(5, 1), [13, 14, 15, 255]); + await pollFor(page, () => getCellColor(6, 1), [16, 17, 18, 255]); + await pollFor(page, () => getCellColor(7, 1), [19, 20, 21, 255]); + await pollFor(page, () => getCellColor(8, 1), [22, 23, 24, 255]); + }); + it('foreground 0-15 bright', async () => { const theme: ITheme = { brightBlack: '#010203', @@ -76,29 +99,6 @@ describe.only('WebGL Renderer Integration Tests', function(): void { await pollFor(page, () => getCellColor(8, 1), [22, 23, 24, 255]); }); - it('background 0-15 normal', async () => { - const theme: ITheme = { - black: '#010203', - red: '#040506', - green: '#070809', - yellow: '#0a0b0c', - blue: '#0d0e0f', - magenta: '#101112', - cyan: '#131415', - white: '#161718' - }; - await page.evaluate(`window.term.setOption('theme', ${JSON.stringify(theme)});`); - await writeSync(`\\x1b[40m \\x1b[41m \\x1b[42m \\x1b[43m \\x1b[44m \\x1b[45m \\x1b[46m \\x1b[47m `); - await pollFor(page, () => getCellColor(1, 1), [1, 2, 3, 255]); - await pollFor(page, () => getCellColor(2, 1), [4, 5, 6, 255]); - await pollFor(page, () => getCellColor(3, 1), [7, 8, 9, 255]); - await pollFor(page, () => getCellColor(4, 1), [10, 11, 12, 255]); - await pollFor(page, () => getCellColor(5, 1), [13, 14, 15, 255]); - await pollFor(page, () => getCellColor(6, 1), [16, 17, 18, 255]); - await pollFor(page, () => getCellColor(7, 1), [19, 20, 21, 255]); - await pollFor(page, () => getCellColor(8, 1), [22, 23, 24, 255]); - }); - it('background 0-15 bright', async () => { const theme: ITheme = { brightBlack: '#010203', @@ -122,11 +122,11 @@ describe.only('WebGL Renderer Integration Tests', function(): void { await pollFor(page, () => getCellColor(8, 1), [22, 23, 24, 255]); }); - it('background 16-255', async () => { + it('foreground 16-255', async () => { let data = ''; for (let y = 0; y < 240 / 16; y++) { for (let x = 0; x < 16; x++) { - data += `\\x1b[48;5;${16 + y * 16 + x}m \x1b[0m`; + data += `\\x1b[38;5;${16 + y * 16 + x}m█\x1b[0m`; } data += '\\r\\n'; } @@ -142,11 +142,11 @@ describe.only('WebGL Renderer Integration Tests', function(): void { } }); - it('foreground 16-255', async () => { + it('background 16-255', async () => { let data = ''; for (let y = 0; y < 240 / 16; y++) { for (let x = 0; x < 16; x++) { - data += `\\x1b[38;5;${16 + y * 16 + x}m█\x1b[0m`; + data += `\\x1b[48;5;${16 + y * 16 + x}m \x1b[0m`; } data += '\\r\\n'; } From 2b2bd91e2ef5a8208e569add26b8e91679ea7908 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sun, 10 Nov 2019 10:41:15 -0800 Subject: [PATCH 82/92] Add background true color tests --- .../src/WebglRenderer.api.ts | 74 ++++++++++++++++++- 1 file changed, 73 insertions(+), 1 deletion(-) diff --git a/addons/xterm-addon-webgl/src/WebglRenderer.api.ts b/addons/xterm-addon-webgl/src/WebglRenderer.api.ts index bb2f5969..304973fe 100644 --- a/addons/xterm-addon-webgl/src/WebglRenderer.api.ts +++ b/addons/xterm-addon-webgl/src/WebglRenderer.api.ts @@ -16,7 +16,7 @@ let page: puppeteer.Page; const width = 800; const height = 600; -describe.only('WebGL Renderer Integration Tests', function(): void { +describe('WebGL Renderer Integration Tests', function(): void { it('dispose removes renderer canvases', async () => { await setupBrowser(); assert.equal(await page.evaluate(`document.querySelectorAll('.xterm canvas').length`), 3); @@ -180,6 +180,24 @@ describe.only('WebGL Renderer Integration Tests', function(): void { } }); + it('background true color red', async () => { + let data = ''; + for (let y = 0; y < 16; y++) { + for (let x = 0; x < 16; x++) { + const i = y * 16 + x; + data += `\\x1b[48;2;${i};0;0m \x1b[0m`; + } + data += '\\r\\n'; + } + await writeSync(data); + for (let y = 0; y < 16; y++) { + for (let x = 0; x < 16; x++) { + const i = y * 16 + x; + await pollFor(page, () => getCellColor(x + 1, y + 1), [i, 0, 0, 255]); + } + } + }); + it('foreground true color green', async () => { let data = ''; for (let y = 0; y < 16; y++) { @@ -198,6 +216,24 @@ describe.only('WebGL Renderer Integration Tests', function(): void { } }); + it('background true color green', async () => { + let data = ''; + for (let y = 0; y < 16; y++) { + for (let x = 0; x < 16; x++) { + const i = y * 16 + x; + data += `\\x1b[48;2;0;${i};0m \x1b[0m`; + } + data += '\\r\\n'; + } + await writeSync(data); + for (let y = 0; y < 16; y++) { + for (let x = 0; x < 16; x++) { + const i = y * 16 + x; + await pollFor(page, () => getCellColor(x + 1, y + 1), [0, i, 0, 255]); + } + } + }); + it('foreground true color blue', async () => { let data = ''; for (let y = 0; y < 16; y++) { @@ -216,6 +252,24 @@ describe.only('WebGL Renderer Integration Tests', function(): void { } }); + it('background true color blue', async () => { + let data = ''; + for (let y = 0; y < 16; y++) { + for (let x = 0; x < 16; x++) { + const i = y * 16 + x; + data += `\\x1b[48;2;0;0;${i}m \x1b[0m`; + } + data += '\\r\\n'; + } + await writeSync(data); + for (let y = 0; y < 16; y++) { + for (let x = 0; x < 16; x++) { + const i = y * 16 + x; + await pollFor(page, () => getCellColor(x + 1, y + 1), [0, 0, i, 255]); + } + } + }); + it('foreground true color grey', async () => { let data = ''; for (let y = 0; y < 16; y++) { @@ -233,6 +287,24 @@ describe.only('WebGL Renderer Integration Tests', function(): void { } } }); + + it('background true color grey', async () => { + let data = ''; + for (let y = 0; y < 16; y++) { + for (let x = 0; x < 16; x++) { + const i = y * 16 + x; + data += `\\x1b[48;2;${i};${i};${i}m \x1b[0m`; + } + data += '\\r\\n'; + } + await writeSync(data); + for (let y = 0; y < 16; y++) { + for (let x = 0; x < 16; x++) { + const i = y * 16 + x; + await pollFor(page, () => getCellColor(x + 1, y + 1), [i, i, i, 255]); + } + } + }); }); }); From 53b0149764a94471c6a3dcb32510505cf837f928 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sun, 10 Nov 2019 11:11:26 -0800 Subject: [PATCH 83/92] Add selectionOpaque and factor out color into new file --- .../src/atlas/CharAtlasUtils.ts | 1 + src/browser/Color.test.ts | 31 ++++++++++ src/browser/Color.ts | 39 +++++++++++++ src/browser/ColorManager.ts | 56 ++++++++----------- src/browser/Types.d.ts | 2 + 5 files changed, 96 insertions(+), 33 deletions(-) create mode 100644 src/browser/Color.test.ts create mode 100644 src/browser/Color.ts diff --git a/addons/xterm-addon-webgl/src/atlas/CharAtlasUtils.ts b/addons/xterm-addon-webgl/src/atlas/CharAtlasUtils.ts index 1fd4c4f6..03867b41 100644 --- a/addons/xterm-addon-webgl/src/atlas/CharAtlasUtils.ts +++ b/addons/xterm-addon-webgl/src/atlas/CharAtlasUtils.ts @@ -21,6 +21,7 @@ export function generateConfig(scaledCharWidth: number, scaledCharHeight: number cursor: NULL_COLOR, cursorAccent: NULL_COLOR, selection: NULL_COLOR, + selectionOpaque: NULL_COLOR, // 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() diff --git a/src/browser/Color.test.ts b/src/browser/Color.test.ts new file mode 100644 index 00000000..fc75d4b9 --- /dev/null +++ b/src/browser/Color.test.ts @@ -0,0 +1,31 @@ +/** + * Copyright (c) 2017 The xterm.js authors. All rights reserved. + * @license MIT + */ + +import { assert } from 'chai'; +import { blend } from 'browser/Color'; + +describe('Color', () => { + describe('blend', () => { + it('should blend colors based on the alpha channel', () => { + assert.deepEqual(blend({ css: '#000000', rgba: 0x000000FF }, { css: '#FFFFFF00', rgba: 0xFFFFFF00 }), { css: '#000000', rgba: 0x000000FF }); + assert.deepEqual(blend({ css: '#000000', rgba: 0x000000FF }, { css: '#FFFFFF10', rgba: 0xFFFFFF10 }), { css: '#101010', rgba: 0x101010FF }); + assert.deepEqual(blend({ css: '#000000', rgba: 0x000000FF }, { css: '#FFFFFF20', rgba: 0xFFFFFF20 }), { css: '#202020', rgba: 0x202020FF }); + assert.deepEqual(blend({ css: '#000000', rgba: 0x000000FF }, { css: '#FFFFFF30', rgba: 0xFFFFFF30 }), { css: '#303030', rgba: 0x303030FF }); + assert.deepEqual(blend({ css: '#000000', rgba: 0x000000FF }, { css: '#FFFFFF40', rgba: 0xFFFFFF40 }), { css: '#404040', rgba: 0x404040FF }); + assert.deepEqual(blend({ css: '#000000', rgba: 0x000000FF }, { css: '#FFFFFF50', rgba: 0xFFFFFF50 }), { css: '#505050', rgba: 0x505050FF }); + assert.deepEqual(blend({ css: '#000000', rgba: 0x000000FF }, { css: '#FFFFFF60', rgba: 0xFFFFFF60 }), { css: '#606060', rgba: 0x606060FF }); + assert.deepEqual(blend({ css: '#000000', rgba: 0x000000FF }, { css: '#FFFFFF70', rgba: 0xFFFFFF70 }), { css: '#707070', rgba: 0x707070FF }); + assert.deepEqual(blend({ css: '#000000', rgba: 0x000000FF }, { css: '#FFFFFF80', rgba: 0xFFFFFF80 }), { css: '#808080', rgba: 0x808080FF }); + assert.deepEqual(blend({ css: '#000000', rgba: 0x000000FF }, { css: '#FFFFFF90', rgba: 0xFFFFFF90 }), { css: '#909090', rgba: 0x909090FF }); + assert.deepEqual(blend({ css: '#000000', rgba: 0x000000FF }, { css: '#FFFFFFA0', rgba: 0xFFFFFFA0 }), { css: '#a0a0a0', rgba: 0xA0A0A0FF }); + assert.deepEqual(blend({ css: '#000000', rgba: 0x000000FF }, { css: '#FFFFFFB0', rgba: 0xFFFFFFB0 }), { css: '#b0b0b0', rgba: 0xB0B0B0FF }); + assert.deepEqual(blend({ css: '#000000', rgba: 0x000000FF }, { css: '#FFFFFFC0', rgba: 0xFFFFFFC0 }), { css: '#c0c0c0', rgba: 0xC0C0C0FF }); + assert.deepEqual(blend({ css: '#000000', rgba: 0x000000FF }, { css: '#FFFFFFD0', rgba: 0xFFFFFFD0 }), { css: '#d0d0d0', rgba: 0xD0D0D0FF }); + assert.deepEqual(blend({ css: '#000000', rgba: 0x000000FF }, { css: '#FFFFFFE0', rgba: 0xFFFFFFE0 }), { css: '#e0e0e0', rgba: 0xE0E0E0FF }); + assert.deepEqual(blend({ css: '#000000', rgba: 0x000000FF }, { css: '#FFFFFFF0', rgba: 0xFFFFFFF0 }), { css: '#f0f0f0', rgba: 0xF0F0F0FF }); + assert.deepEqual(blend({ css: '#000000', rgba: 0x000000FF }, { css: '#FFFFFFFF', rgba: 0xFFFFFFFF }), { css: '#FFFFFFFF', rgba: 0xFFFFFFFF }); + }); + }); +}); diff --git a/src/browser/Color.ts b/src/browser/Color.ts new file mode 100644 index 00000000..31087116 --- /dev/null +++ b/src/browser/Color.ts @@ -0,0 +1,39 @@ +import { IColor } from './Types'; + +export function blend(bg: IColor, fg: IColor): IColor { + const a = (fg.rgba & 0xFF) / 255; + if (a === 1) { + return { + css: fg.css, + rgba: fg.rgba + }; + } + const fgR = (fg.rgba >> 24) & 0xFF; + const fgG = (fg.rgba >> 16) & 0xFF; + const fgB = (fg.rgba >> 8) & 0xFF; + const bgR = (bg.rgba >> 24) & 0xFF; + const bgG = (bg.rgba >> 16) & 0xFF; + const bgB = (bg.rgba >> 8) & 0xFF; + const r = bgR + Math.round((fgR - bgR) * a); + const g = bgG + Math.round((fgG - bgG) * a); + const b = bgB + Math.round((fgB - bgB) * a); + const css = toCss(r, g, b); + const rgba = ((r << 24) | (g << 16) | (b << 8) | 0xFF) >>> 0; + return { css, rgba }; +} + +export function fromCss(css: string): IColor { + return { + css, + rgba: parseInt(css.slice(1), 16) << 8 | 0xFF + }; +} + +export function toPaddedHex(c: number): string { + const s = c.toString(16); + return s.length < 2 ? '0' + s : s; +} + +export function toCss(r: number, g: number, b: number): string { + return `#${toPaddedHex(r)}${toPaddedHex(g)}${toPaddedHex(b)}`; +} diff --git a/src/browser/ColorManager.ts b/src/browser/ColorManager.ts index 70d21a7a..a1bfc105 100644 --- a/src/browser/ColorManager.ts +++ b/src/browser/ColorManager.ts @@ -5,11 +5,12 @@ import { IColorManager, IColor, IColorSet } from 'browser/Types'; import { ITheme } from 'common/services/Services'; +import { fromCss, toPaddedHex, toCss, blend } from 'browser/Color'; -const DEFAULT_FOREGROUND = fromHex('#ffffff'); -const DEFAULT_BACKGROUND = fromHex('#000000'); -const DEFAULT_CURSOR = fromHex('#ffffff'); -const DEFAULT_CURSOR_ACCENT = fromHex('#000000'); +const DEFAULT_FOREGROUND = fromCss('#ffffff'); +const DEFAULT_BACKGROUND = fromCss('#000000'); +const DEFAULT_CURSOR = fromCss('#ffffff'); +const DEFAULT_CURSOR_ACCENT = fromCss('#000000'); const DEFAULT_SELECTION = { css: 'rgba(255, 255, 255, 0.3)', rgba: 0xFFFFFF77 @@ -20,23 +21,23 @@ const DEFAULT_SELECTION = { export const DEFAULT_ANSI_COLORS = (() => { const colors = [ // dark: - fromHex('#2e3436'), - fromHex('#cc0000'), - fromHex('#4e9a06'), - fromHex('#c4a000'), - fromHex('#3465a4'), - fromHex('#75507b'), - fromHex('#06989a'), - fromHex('#d3d7cf'), + fromCss('#2e3436'), + fromCss('#cc0000'), + fromCss('#4e9a06'), + fromCss('#c4a000'), + fromCss('#3465a4'), + fromCss('#75507b'), + fromCss('#06989a'), + fromCss('#d3d7cf'), // bright: - fromHex('#555753'), - fromHex('#ef2929'), - fromHex('#8ae234'), - fromHex('#fce94f'), - fromHex('#729fcf'), - fromHex('#ad7fa8'), - fromHex('#34e2e2'), - fromHex('#eeeeec') + fromCss('#555753'), + fromCss('#ef2929'), + fromCss('#8ae234'), + fromCss('#fce94f'), + fromCss('#729fcf'), + fromCss('#ad7fa8'), + fromCss('#34e2e2'), + fromCss('#eeeeec') ]; // Fill in the remaining 240 ANSI colors. @@ -47,7 +48,7 @@ export const DEFAULT_ANSI_COLORS = (() => { const g = v[(i / 6) % 6 | 0]; const b = v[i % 6]; colors.push({ - css: `#${toPaddedHex(r)}${toPaddedHex(g)}${toPaddedHex(b)}`, + css: toCss(r, g, b), // Use >>> 0 to force a conversion to an unsigned int rgba: ((r << 24) | (g << 16) | (b << 8) | 0xFF) >>> 0 }); @@ -66,18 +67,6 @@ export const DEFAULT_ANSI_COLORS = (() => { return colors; })(); -function fromHex(css: string): IColor { - return { - css, - rgba: parseInt(css.slice(1), 16) << 8 | 0xFF - }; -} - -function toPaddedHex(c: number): string { - const s = c.toString(16); - return s.length < 2 ? '0' + s : s; -} - /** * Manages the source of truth for a terminal's colors. */ @@ -103,6 +92,7 @@ export class ColorManager implements IColorManager { cursor: DEFAULT_CURSOR, cursorAccent: DEFAULT_CURSOR_ACCENT, selection: DEFAULT_SELECTION, + selectionOpaque: blend(DEFAULT_BACKGROUND, DEFAULT_SELECTION), ansi: DEFAULT_ANSI_COLORS.slice() }; } diff --git a/src/browser/Types.d.ts b/src/browser/Types.d.ts index aa7a4c86..ca852b6a 100644 --- a/src/browser/Types.d.ts +++ b/src/browser/Types.d.ts @@ -21,6 +21,8 @@ export interface IColorSet { cursor: IColor; cursorAccent: IColor; selection: IColor; + /** The selection blended on top of background. */ + selectionOpaque: IColor; ansi: IColor[]; } From 51c77568a8ad2dc1db4bb0fdd1f52183a2dab7b3 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sun, 10 Nov 2019 11:17:36 -0800 Subject: [PATCH 84/92] Add tests for Color functions --- src/browser/Color.test.ts | 68 ++++++++++++++++++++++++++++++++++++++- src/browser/Color.ts | 2 +- 2 files changed, 68 insertions(+), 2 deletions(-) diff --git a/src/browser/Color.test.ts b/src/browser/Color.test.ts index fc75d4b9..5542611f 100644 --- a/src/browser/Color.test.ts +++ b/src/browser/Color.test.ts @@ -4,7 +4,7 @@ */ import { assert } from 'chai'; -import { blend } from 'browser/Color'; +import { blend, fromCss, toPaddedHex, toCss } from 'browser/Color'; describe('Color', () => { describe('blend', () => { @@ -28,4 +28,70 @@ describe('Color', () => { assert.deepEqual(blend({ css: '#000000', rgba: 0x000000FF }, { css: '#FFFFFFFF', rgba: 0xFFFFFFFF }), { css: '#FFFFFFFF', rgba: 0xFFFFFFFF }); }); }); + + describe('fromCss', () => { + it('should covert a CSS string to an IColor', () => { + assert.deepEqual(fromCss('#000000'), { css: '#000000', rgba: 0x000000FF }); + assert.deepEqual(fromCss('#101010'), { css: '#101010', rgba: 0x101010FF }); + assert.deepEqual(fromCss('#202020'), { css: '#202020', rgba: 0x202020FF }); + assert.deepEqual(fromCss('#303030'), { css: '#303030', rgba: 0x303030FF }); + assert.deepEqual(fromCss('#404040'), { css: '#404040', rgba: 0x404040FF }); + assert.deepEqual(fromCss('#505050'), { css: '#505050', rgba: 0x505050FF }); + assert.deepEqual(fromCss('#606060'), { css: '#606060', rgba: 0x606060FF }); + assert.deepEqual(fromCss('#707070'), { css: '#707070', rgba: 0x707070FF }); + assert.deepEqual(fromCss('#808080'), { css: '#808080', rgba: 0x808080FF }); + assert.deepEqual(fromCss('#909090'), { css: '#909090', rgba: 0x909090FF }); + assert.deepEqual(fromCss('#a0a0a0'), { css: '#a0a0a0', rgba: 0xa0a0a0FF }); + assert.deepEqual(fromCss('#b0b0b0'), { css: '#b0b0b0', rgba: 0xb0b0b0FF }); + assert.deepEqual(fromCss('#c0c0c0'), { css: '#c0c0c0', rgba: 0xc0c0c0FF }); + assert.deepEqual(fromCss('#d0d0d0'), { css: '#d0d0d0', rgba: 0xd0d0d0FF }); + assert.deepEqual(fromCss('#e0e0e0'), { css: '#e0e0e0', rgba: 0xe0e0e0FF }); + assert.deepEqual(fromCss('#f0f0f0'), { css: '#f0f0f0', rgba: 0xf0f0f0FF }); + assert.deepEqual(fromCss('#ffffff'), { css: '#ffffff', rgba: 0xffffffFF }); + }); + }); + + describe('toPaddedHex', () => { + it('should convert numbers to 2-digit hex values', () => { + assert.equal(toPaddedHex(0x00), '00'); + assert.equal(toPaddedHex(0x10), '10'); + assert.equal(toPaddedHex(0x20), '20'); + assert.equal(toPaddedHex(0x30), '30'); + assert.equal(toPaddedHex(0x40), '40'); + assert.equal(toPaddedHex(0x50), '50'); + assert.equal(toPaddedHex(0x60), '60'); + assert.equal(toPaddedHex(0x70), '70'); + assert.equal(toPaddedHex(0x80), '80'); + assert.equal(toPaddedHex(0x90), '90'); + assert.equal(toPaddedHex(0xa0), 'a0'); + assert.equal(toPaddedHex(0xb0), 'b0'); + assert.equal(toPaddedHex(0xc0), 'c0'); + assert.equal(toPaddedHex(0xd0), 'd0'); + assert.equal(toPaddedHex(0xe0), 'e0'); + assert.equal(toPaddedHex(0xf0), 'f0'); + assert.equal(toPaddedHex(0xff), 'ff'); + }); + }); + + describe('toCss', () => { + it('should convert an rgb array to css hex string', () => { + assert.equal(toCss(0x00, 0x00, 0x00), '#000000'); + assert.equal(toCss(0x10, 0x10, 0x10), '#101010'); + assert.equal(toCss(0x20, 0x20, 0x20), '#202020'); + assert.equal(toCss(0x30, 0x30, 0x30), '#303030'); + assert.equal(toCss(0x40, 0x40, 0x40), '#404040'); + assert.equal(toCss(0x50, 0x50, 0x50), '#505050'); + assert.equal(toCss(0x60, 0x60, 0x60), '#606060'); + assert.equal(toCss(0x70, 0x70, 0x70), '#707070'); + assert.equal(toCss(0x80, 0x80, 0x80), '#808080'); + assert.equal(toCss(0x90, 0x90, 0x90), '#909090'); + assert.equal(toCss(0xa0, 0xa0, 0xa0), '#a0a0a0'); + assert.equal(toCss(0xb0, 0xb0, 0xb0), '#b0b0b0'); + assert.equal(toCss(0xc0, 0xc0, 0xc0), '#c0c0c0'); + assert.equal(toCss(0xd0, 0xd0, 0xd0), '#d0d0d0'); + assert.equal(toCss(0xe0, 0xe0, 0xe0), '#e0e0e0'); + assert.equal(toCss(0xf0, 0xf0, 0xf0), '#f0f0f0'); + assert.equal(toCss(0xff, 0xff, 0xff), '#ffffff'); + }); + }); }); diff --git a/src/browser/Color.ts b/src/browser/Color.ts index 31087116..9771f656 100644 --- a/src/browser/Color.ts +++ b/src/browser/Color.ts @@ -25,7 +25,7 @@ export function blend(bg: IColor, fg: IColor): IColor { export function fromCss(css: string): IColor { return { css, - rgba: parseInt(css.slice(1), 16) << 8 | 0xFF + rgba: (parseInt(css.slice(1), 16) << 8 | 0xFF) >>> 0 }; } From 2da77dd5f8dfe729bcfdd8499d5070b559566052 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sun, 10 Nov 2019 11:23:34 -0800 Subject: [PATCH 85/92] Use opaque selection in webgl, fix selection default rgba --- addons/xterm-addon-webgl/src/GlyphRenderer.ts | 2 +- addons/xterm-addon-webgl/src/RectangleRenderer.ts | 2 +- src/browser/Color.ts | 5 +++++ src/browser/ColorManager.ts | 3 ++- 4 files changed, 9 insertions(+), 3 deletions(-) diff --git a/addons/xterm-addon-webgl/src/GlyphRenderer.ts b/addons/xterm-addon-webgl/src/GlyphRenderer.ts index 3ee9cdb5..3d4d43d1 100644 --- a/addons/xterm-addon-webgl/src/GlyphRenderer.ts +++ b/addons/xterm-addon-webgl/src/GlyphRenderer.ts @@ -218,7 +218,7 @@ export class GlyphRenderer { this._vertices.selectionAttributes = slice(this._vertices.attributes, 0); - const bg = (this._colors.selection.rgba >>> 8) | Attributes.CM_RGB; + const bg = (this._colors.selectionOpaque.rgba >>> 8) | Attributes.CM_RGB; if (columnSelectMode) { const startCol = model.selection.startCol; diff --git a/addons/xterm-addon-webgl/src/RectangleRenderer.ts b/addons/xterm-addon-webgl/src/RectangleRenderer.ts index d5e245c0..8dcc5c08 100644 --- a/addons/xterm-addon-webgl/src/RectangleRenderer.ts +++ b/addons/xterm-addon-webgl/src/RectangleRenderer.ts @@ -155,7 +155,7 @@ export class RectangleRenderer { private _updateCachedColors(): void { this._bgFloat = this._colorToFloat32Array(this._colors.background); - this._selectionFloat = this._colorToFloat32Array(this._colors.selection); + this._selectionFloat = this._colorToFloat32Array(this._colors.selectionOpaque); } private _updateViewportRectangle(): void { diff --git a/src/browser/Color.ts b/src/browser/Color.ts index 9771f656..5e86d5e9 100644 --- a/src/browser/Color.ts +++ b/src/browser/Color.ts @@ -1,3 +1,8 @@ +/** + * Copyright (c) 2019 The xterm.js authors. All rights reserved. + * @license MIT + */ + import { IColor } from './Types'; export function blend(bg: IColor, fg: IColor): IColor { diff --git a/src/browser/ColorManager.ts b/src/browser/ColorManager.ts index a1bfc105..170fead6 100644 --- a/src/browser/ColorManager.ts +++ b/src/browser/ColorManager.ts @@ -13,7 +13,7 @@ const DEFAULT_CURSOR = fromCss('#ffffff'); const DEFAULT_CURSOR_ACCENT = fromCss('#000000'); const DEFAULT_SELECTION = { css: 'rgba(255, 255, 255, 0.3)', - rgba: 0xFFFFFF77 + rgba: 0xFFFFFF4D }; // An IIFE to generate DEFAULT_ANSI_COLORS. Do not mutate DEFAULT_ANSI_COLORS, instead make a copy @@ -108,6 +108,7 @@ export class ColorManager implements IColorManager { this.colors.cursor = this._parseColor(theme.cursor, DEFAULT_CURSOR, true); this.colors.cursorAccent = this._parseColor(theme.cursorAccent, DEFAULT_CURSOR_ACCENT, true); this.colors.selection = this._parseColor(theme.selection, DEFAULT_SELECTION, true); + this.colors.selectionOpaque = blend(this.colors.background, this.colors.selection); this.colors.ansi[0] = this._parseColor(theme.black, DEFAULT_ANSI_COLORS[0]); this.colors.ansi[1] = this._parseColor(theme.red, DEFAULT_ANSI_COLORS[1]); this.colors.ansi[2] = this._parseColor(theme.green, DEFAULT_ANSI_COLORS[2]); From 734bd8c96a43f7ed38e1bdf377b014533d4a7928 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sun, 10 Nov 2019 11:30:48 -0800 Subject: [PATCH 86/92] Factor out toRgba function and test --- src/browser/Color.test.ts | 43 ++++++++++++++++++++++++++++++++++++- src/browser/Color.ts | 7 +++++- src/browser/ColorManager.ts | 12 +++++------ 3 files changed, 53 insertions(+), 9 deletions(-) diff --git a/src/browser/Color.test.ts b/src/browser/Color.test.ts index 5542611f..44cd52f3 100644 --- a/src/browser/Color.test.ts +++ b/src/browser/Color.test.ts @@ -4,7 +4,7 @@ */ import { assert } from 'chai'; -import { blend, fromCss, toPaddedHex, toCss } from 'browser/Color'; +import { blend, fromCss, toPaddedHex, toCss, toRgba } from 'browser/Color'; describe('Color', () => { describe('blend', () => { @@ -94,4 +94,45 @@ describe('Color', () => { assert.equal(toCss(0xff, 0xff, 0xff), '#ffffff'); }); }); + + describe('toRgba', () => { + it('should convert an rgb array to an rgba number', () => { + assert.equal(toRgba(0x00, 0x00, 0x00), 0x000000FF); + assert.equal(toRgba(0x10, 0x10, 0x10), 0x101010FF); + assert.equal(toRgba(0x20, 0x20, 0x20), 0x202020FF); + assert.equal(toRgba(0x30, 0x30, 0x30), 0x303030FF); + assert.equal(toRgba(0x40, 0x40, 0x40), 0x404040FF); + assert.equal(toRgba(0x50, 0x50, 0x50), 0x505050FF); + assert.equal(toRgba(0x60, 0x60, 0x60), 0x606060FF); + assert.equal(toRgba(0x70, 0x70, 0x70), 0x707070FF); + assert.equal(toRgba(0x80, 0x80, 0x80), 0x808080FF); + assert.equal(toRgba(0x90, 0x90, 0x90), 0x909090FF); + assert.equal(toRgba(0xa0, 0xa0, 0xa0), 0xa0a0a0FF); + assert.equal(toRgba(0xb0, 0xb0, 0xb0), 0xb0b0b0FF); + assert.equal(toRgba(0xc0, 0xc0, 0xc0), 0xc0c0c0FF); + assert.equal(toRgba(0xd0, 0xd0, 0xd0), 0xd0d0d0FF); + assert.equal(toRgba(0xe0, 0xe0, 0xe0), 0xe0e0e0FF); + assert.equal(toRgba(0xf0, 0xf0, 0xf0), 0xf0f0f0FF); + assert.equal(toRgba(0xff, 0xff, 0xff), 0xffffffFF); + }); + it('should convert an rgba array to an rgba number', () => { + assert.equal(toRgba(0x00, 0x00, 0x00, 0x00), 0x00000000); + assert.equal(toRgba(0x10, 0x10, 0x10, 0x10), 0x10101010); + assert.equal(toRgba(0x20, 0x20, 0x20, 0x20), 0x20202020); + assert.equal(toRgba(0x30, 0x30, 0x30, 0x30), 0x30303030); + assert.equal(toRgba(0x40, 0x40, 0x40, 0x40), 0x40404040); + assert.equal(toRgba(0x50, 0x50, 0x50, 0x50), 0x50505050); + assert.equal(toRgba(0x60, 0x60, 0x60, 0x60), 0x60606060); + assert.equal(toRgba(0x70, 0x70, 0x70, 0x70), 0x70707070); + assert.equal(toRgba(0x80, 0x80, 0x80, 0x80), 0x80808080); + assert.equal(toRgba(0x90, 0x90, 0x90, 0x90), 0x90909090); + assert.equal(toRgba(0xa0, 0xa0, 0xa0, 0xa0), 0xa0a0a0a0); + assert.equal(toRgba(0xb0, 0xb0, 0xb0, 0xb0), 0xb0b0b0b0); + assert.equal(toRgba(0xc0, 0xc0, 0xc0, 0xc0), 0xc0c0c0c0); + assert.equal(toRgba(0xd0, 0xd0, 0xd0, 0xd0), 0xd0d0d0d0); + assert.equal(toRgba(0xe0, 0xe0, 0xe0, 0xe0), 0xe0e0e0e0); + assert.equal(toRgba(0xf0, 0xf0, 0xf0, 0xf0), 0xf0f0f0f0); + assert.equal(toRgba(0xff, 0xff, 0xff, 0xff), 0xffffffff); + }); + }); }); diff --git a/src/browser/Color.ts b/src/browser/Color.ts index 5e86d5e9..51c1a446 100644 --- a/src/browser/Color.ts +++ b/src/browser/Color.ts @@ -23,7 +23,7 @@ export function blend(bg: IColor, fg: IColor): IColor { const g = bgG + Math.round((fgG - bgG) * a); const b = bgB + Math.round((fgB - bgB) * a); const css = toCss(r, g, b); - const rgba = ((r << 24) | (g << 16) | (b << 8) | 0xFF) >>> 0; + const rgba = toRgba(r, g, b); return { css, rgba }; } @@ -42,3 +42,8 @@ export function toPaddedHex(c: number): string { export function toCss(r: number, g: number, b: number): string { return `#${toPaddedHex(r)}${toPaddedHex(g)}${toPaddedHex(b)}`; } + +export function toRgba(r: number, g: number, b: number, a: number = 0xFF): number { + // >>> 0 forces an unsigned int + return (r << 24 | g << 16 | b << 8 | a) >>> 0; +} diff --git a/src/browser/ColorManager.ts b/src/browser/ColorManager.ts index 170fead6..c6354f71 100644 --- a/src/browser/ColorManager.ts +++ b/src/browser/ColorManager.ts @@ -5,7 +5,7 @@ import { IColorManager, IColor, IColorSet } from 'browser/Types'; import { ITheme } from 'common/services/Services'; -import { fromCss, toPaddedHex, toCss, blend } from 'browser/Color'; +import { fromCss, toCss, blend, toRgba } from 'browser/Color'; const DEFAULT_FOREGROUND = fromCss('#ffffff'); const DEFAULT_BACKGROUND = fromCss('#000000'); @@ -49,18 +49,16 @@ export const DEFAULT_ANSI_COLORS = (() => { const b = v[i % 6]; colors.push({ css: toCss(r, g, b), - // Use >>> 0 to force a conversion to an unsigned int - rgba: ((r << 24) | (g << 16) | (b << 8) | 0xFF) >>> 0 + rgba: toRgba(r, g, b) }); } // Generate greys (232-255) for (let i = 0; i < 24; i++) { const c = 8 + i * 10; - const ch = toPaddedHex(c); colors.push({ - css: `#${ch}${ch}${ch}`, - rgba: ((c << 24) | (c << 16) | (c << 8) | 0xFF) >>> 0 + css: toCss(c, c, c), + rgba: toRgba(c, c, c) }); } @@ -175,7 +173,7 @@ export class ColorManager implements IColorManager { return { css, - rgba: (data[0] << 24 | data[1] << 16 | data[2] << 8 | data[3]) >>> 0 + rgba: toRgba(data[0], data[1], data[2], data[3]) }; } } From e89d79b879a4aa8550a7957e78aeafd584effa56 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sun, 10 Nov 2019 11:33:41 -0800 Subject: [PATCH 87/92] Remove unused code --- .../src/renderLayer/BaseRenderLayer.ts | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/addons/xterm-addon-webgl/src/renderLayer/BaseRenderLayer.ts b/addons/xterm-addon-webgl/src/renderLayer/BaseRenderLayer.ts index 0e2d645e..29487ad0 100644 --- a/addons/xterm-addon-webgl/src/renderLayer/BaseRenderLayer.ts +++ b/addons/xterm-addon-webgl/src/renderLayer/BaseRenderLayer.ts @@ -4,7 +4,6 @@ */ import { IRenderLayer } from './Types'; -import { IGlyphIdentifier } from '../atlas/Types'; import { acquireCharAtlas } from '../atlas/CharAtlasCache'; import { Terminal } from 'xterm'; import { IColorSet } from 'browser/Types'; @@ -25,19 +24,6 @@ export abstract class BaseRenderLayer implements IRenderLayer { protected _charAtlas: WebglCharAtlas | undefined; - /** - * An object that's reused when drawing glyphs in order to reduce GC. - */ - private _currentGlyphIdentifier: IGlyphIdentifier = { - chars: '', - code: 0, - bg: 0, - fg: 0, - bold: false, - dim: false, - italic: false - }; - constructor( private _container: HTMLElement, id: string, From 450a6262181e1bfc42fb1197f7760d90b1ca3d3e Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sun, 10 Nov 2019 11:48:32 -0800 Subject: [PATCH 88/92] Correct import in Color.ts --- src/browser/Color.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/browser/Color.ts b/src/browser/Color.ts index 51c1a446..a8ce2d16 100644 --- a/src/browser/Color.ts +++ b/src/browser/Color.ts @@ -3,7 +3,7 @@ * @license MIT */ -import { IColor } from './Types'; +import { IColor } from 'browser/Types'; export function blend(bg: IColor, fg: IColor): IColor { const a = (fg.rgba & 0xFF) / 255; From 6b7e29ea32d5ca4f8421d113835aaefd8b64adb9 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sun, 10 Nov 2019 12:06:20 -0800 Subject: [PATCH 89/92] Whitespace to force a webgl release --- addons/xterm-addon-webgl/src/atlas/WebglCharAtlas.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/addons/xterm-addon-webgl/src/atlas/WebglCharAtlas.ts b/addons/xterm-addon-webgl/src/atlas/WebglCharAtlas.ts index c364c3c1..16b7ba07 100644 --- a/addons/xterm-addon-webgl/src/atlas/WebglCharAtlas.ts +++ b/addons/xterm-addon-webgl/src/atlas/WebglCharAtlas.ts @@ -440,3 +440,4 @@ function toPaddedHex(c: number): string { const s = c.toString(16); return s.length < 2 ? '0' + s : s; } + From 94afd1fa38d31c07838d0960075b3994f610dcd7 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sun, 10 Nov 2019 21:38:43 -0800 Subject: [PATCH 90/92] Generate coverage report for pipelines --- azure-pipelines.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 4daa0961..0f61dfe1 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -25,7 +25,9 @@ jobs: displayName: Cache node modules - script: yarn --frozen-lockfile displayName: 'Install dependencies and build' - - script: yarn test-unit-coverage --forbid-only + - script: | + yarn test-unit-coverage --forbid-only + ./node_modules/.bin/nyc report --reporter=cobertura displayName: 'Unit tests' - script: yarn lint displayName: 'Lint' From 25f9487239aad2a50368f79d19692c0007234670 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Mon, 11 Nov 2019 10:24:54 -0800 Subject: [PATCH 91/92] Prefer non-relative imports Fixes some auto imports in VS Code, see microsoft/TypeScript#32144 --- .vscode/settings.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 3c8a2dc2..20e4915c 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,4 @@ { - "typescript.preferences.quoteStyle": "single" + "typescript.preferences.quoteStyle": "single", + "typescript.preferences.importModuleSpecifier": "non-relative" } From dfc1664d6e29d2ff7ed7a1bab6a8560e673b837c Mon Sep 17 00:00:00 2001 From: Andrew Sverdrup Date: Mon, 11 Nov 2019 13:23:59 -0800 Subject: [PATCH 92/92] Update Codevolve to Next Tech We changed our name several months back. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5cf2848f..d2944003 100644 --- a/README.md +++ b/README.md @@ -110,7 +110,7 @@ Xterm.js is used in several world-class applications to provide great terminal e - [**WebSSH2**](https://github.com/billchurch/WebSSH2): A web based SSH2 client using xterm.js, socket.io, and ssh2. - [**Spyder Terminal**](https://github.com/spyder-ide/spyder-terminal): A full fledged system terminal embedded on Spyder IDE. - [**Cloud Commander**](https://cloudcmd.io "Cloud Commander"): Orthodox web file manager with console and editor. -- [**Codevolve**](https://www.codevolve.com "Codevolve"): Online platform for interactive coding and web development courses. Live container-backed terminal uses xterm.js. +- [**Next Tech**](https://next.tech "Next Tech"): Online platform for interactive coding and web development courses. Live container-backed terminal uses xterm.js. - [**RStudio**](https://www.rstudio.com/products/RStudio "RStudio"): RStudio is an integrated development environment (IDE) for R. - [**Terminal for Atom**](https://github.com/jsmecham/atom-terminal-tab): A simple terminal for the Atom text editor. - [**Eclipse Orion**](https://orionhub.org): A modern, open source software development environment that runs in the cloud. Code, deploy and run in the cloud.