Compare commits

...
42 Commits
Author SHA1 Message Date
Daniel Imms c3261beaea Merge pull request #3622 from Tyriar/4_17_release
v4.17.0
2022-02-03 07:10:07 -08:00
Daniel Imms 345b8d910b v4.17.0 2022-02-03 07:04:07 -08:00
Daniel Imms 922ee7c48d Merge pull request #3612 from syssecfsu/master
Update README.md to link to WiTTY
2022-02-03 06:48:55 -08:00
Daniel Imms 131e11b52b Merge pull request #3620 from xtermjs/Tyriar-patch-1
Add keywords to package.json
2022-02-01 08:26:20 -08:00
Daniel Imms 5a3e0612a7 Merge pull request #3621 from felixse/set-text-baseline-for-legacy-edge
fix text baseline for Legacy Edge
2022-01-31 07:41:10 -08:00
Felix Seidl b246318090 fix text baseline for legacy edge 2022-01-30 21:21:07 +01:00
Daniel Imms d807bdac5e Add keywords to addons too 2022-01-30 05:47:58 -08:00
Daniel Imms 1695baa5b0 Add keywords to package.json
I noticed we have no search tags on npmjs.com
2022-01-30 05:43:20 -08:00
syssecfsu c71252cd23 Update README.md to link to WiTTY 2022-01-22 18:45:30 -05:00
Daniel Imms 6c18b7f2cc Merge pull request #3609 from erulabs/master
Update README.md - add realworld usage @ KubeSail
2022-01-20 07:03:44 -08:00
Seandon Mooy 5e77ad5cb4 Update README.md - add realworld usage @ KubeSail
❤️ 🦞
2022-01-19 23:21:36 -08:00
Daniel Imms e1578813a7 Merge pull request #3606 from xtermjs/dependabot/npm_and_yarn/addons/xterm-addon-ligatures/follow-redirects-1.14.7
Bump follow-redirects from 1.14.3 to 1.14.7 in /addons/xterm-addon-ligatures
2022-01-14 07:18:25 -08:00
dependabot[bot] 21811cb37d Bump follow-redirects in /addons/xterm-addon-ligatures
Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.14.3 to 1.14.7.
- [Release notes](https://github.com/follow-redirects/follow-redirects/releases)
- [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.14.3...v1.14.7)

---
updated-dependencies:
- dependency-name: follow-redirects
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-14 08:59:31 +00:00
Daniel Imms 265227edcb Merge pull request #3604 from Tyriar/exception_3602
Prevent exception being thrown in reflow smaller
2022-01-11 08:25:28 -08:00
Daniel Imms 1d3c1964f9 Merge branch 'master' into exception_3602 2022-01-11 08:12:24 -08:00
Daniel Imms 58b7a4a2d7 Merge pull request #3603 from Tyriar/test_task
Set default vscode test task
2022-01-11 08:12:17 -08:00
Daniel Imms a6b0734fe8 Prevent exception being thrown in reflow smaller
Fixes #3602
2022-01-11 08:11:36 -08:00
Daniel Imms 55cd34c7a5 Set default vscode test task
This allows running the test task via a command/keybinding
2022-01-11 07:50:17 -08:00
Daniel Imms 0e8aeebedb Merge pull request #3599 from silamon/options5
Add options to headless typings
2022-01-11 07:36:30 -08:00
Daniel Imms 1c4d41db86 Merge branch 'master' into options5 2022-01-11 07:27:48 -08:00
Daniel Imms 63c281e405 Merge pull request #3559 from silamon/options3
Resolve perf regression from OptionsService.options
2022-01-11 07:26:43 -08:00
Simon Lamon f03d0858cc fix linting error 2022-01-11 11:58:53 +00:00
Simon Lamon 8bdeb14bc6 add options to headless typings 2022-01-08 10:14:33 +00:00
Simon Lamon ad7fe8f881 fix tests again 2022-01-08 08:51:29 +00:00
Simon Lamon 1dcc1d1baf fix test and replace options with rawOptions 2022-01-08 08:20:50 +00:00
Daniel Imms 0029bc3b42 Merge pull request #3598 from LabhanshAgrawal/ligatures
Bump font-ligatures to 1.4.1
2022-01-07 09:14:21 -08:00
Labhansh Agrawal 0b73a8d469 Bump font-ligatures to 1.4.1 2022-01-07 11:51:24 +05:30
Daniel Imms 3a2b97e0f7 Merge pull request #3597 from Aaaaash/chore/add-opensumi
Add opensumi to the list of users
2022-01-06 07:42:03 -08:00
大表哥 9bcc98beb9 Add opensumi to the list of users 2022-01-06 10:10:10 +08:00
Daniel Imms 1fb11707a5 Merge pull request #3596 from wesbos/patch-1
Extend TLD matching to 14 chars
2022-01-05 13:54:05 -08:00
Wes Bos 84548d5d7b Update WebLinksAddon.ts 2022-01-05 16:29:39 -05:00
Wes Bos 6f666f56c1 Extend TLD matching to 14 chars
I ran into this issue with hyper wasn't highlighting my `.localhost` domains, which is a legitimate TLD and used by caddy server to allow SSL certs locally.

Then I went down the rabbit hole of TLDs and found currently the longest TLDs are 14 chars which include .BANANAREPUBLIC, .COOKINGCHANNEL, .KERRYLOGISTICS, .WEATHERCHANNEL

I'm not regex expert, but I *think* this should work? 😀
2022-01-05 16:28:05 -05:00
Daniel Imms 2866977941 Merge pull request #3595 from xtermjs/Tyriar-patch-1
Fix dependents link
2022-01-05 12:58:50 -08:00
Daniel Imms c087592e2c Fix dependents link 2022-01-05 12:58:29 -08:00
Simon Lamon 65182176b9 remove proxy 2021-12-25 14:02:09 +00:00
Daniel Imms c8123d911e Merge pull request #3591 from Tyriar/3590
Disable macOS integration tests
2021-12-22 11:00:40 -08:00
Daniel Imms cacda99707 Disable macOS integration tests
Fixes #3590
2021-12-22 10:45:49 -08:00
Simon Lamon bac4eba35e Remove the test referencing publicOptions 2021-12-21 19:41:56 +00:00
Simon Lamon 625df61489 clean up 2021-12-21 19:29:18 +00:00
Simon Lamon ccca58bb95 Merge branch 'master' into options3 2021-12-21 19:22:41 +00:00
Daniel Imms 9557b9132b Merge branch 'master' into options3 2021-12-17 11:13:23 -08:00
Simon Lamon d6d540e748 use a proxy 2021-11-20 14:58:07 +00:00
44 changed files with 353 additions and 161 deletions
+4 -1
View File
@@ -11,7 +11,10 @@
{ {
"type": "npm", "type": "npm",
"script": "test", "script": "test",
"group": "test", "group":{
"kind": "test",
"isDefault": true
},
"problemMatcher": [] "problemMatcher": []
}, },
{ {
+4 -1
View File
@@ -185,7 +185,10 @@ Xterm.js is used in several world-class applications to provide great terminal e
- [**gdbgui**](https://github.com/cs01/gdbgui): Browser-based frontend to gdb (gnu debugger) - [**gdbgui**](https://github.com/cs01/gdbgui): Browser-based frontend to gdb (gnu debugger)
- [**goormIDE**](https://ide.goorm.io/): Run almost every programming languages with real-time collaboration, live pair programming, and built-in messenger. - [**goormIDE**](https://ide.goorm.io/): Run almost every programming languages with real-time collaboration, live pair programming, and built-in messenger.
- [**FleetDeck**](https://fleetdeck.io): Remote desktop & virtual terminal - [**FleetDeck**](https://fleetdeck.io): Remote desktop & virtual terminal
- [And much more...](https://github.com/xtermjs/xterm.js/network/dependents) - [**OpenSumi**](https://github.com/opensumi/core): A framework helps you quickly build Cloud or Desktop IDE products.
- [**KubeSail**](https://kubesail.com): The Self-Hosting Company - uses xterm to allow users to exec into kubernetes pods and build github apps
- [**WiTTY**](https://github.com/syssecfsu/witty): Web-based interactive terminal emulator that allows users to easily record, share, and replay console sessions.
- [And much more...](https://github.com/xtermjs/xterm.js/network/dependents?package_id=UGFja2FnZS0xNjYzMjc4OQ%3D%3D)
Do you use xterm.js in your application as well? Please [open a Pull Request](https://github.com/sourcelair/xterm.js/pulls) to include it here. We would love to have it on our list. Note: Please add any new contributions to the end of the list only. Do you use xterm.js in your application as well? Please [open a Pull Request](https://github.com/sourcelair/xterm.js/pulls) to include it here. We would love to have it on our list. Note: Please add any new contributions to the end of the list only.
+5
View File
@@ -9,6 +9,11 @@
"types": "typings/xterm-addon-attach.d.ts", "types": "typings/xterm-addon-attach.d.ts",
"repository": "https://github.com/xtermjs/xterm.js", "repository": "https://github.com/xtermjs/xterm.js",
"license": "MIT", "license": "MIT",
"keywords": [
"terminal",
"xterm",
"xterm.js"
],
"scripts": { "scripts": {
"build": "../../node_modules/.bin/tsc -p .", "build": "../../node_modules/.bin/tsc -p .",
"prepackage": "npm run build", "prepackage": "npm run build",
+5
View File
@@ -9,6 +9,11 @@
"types": "typings/xterm-addon-fit.d.ts", "types": "typings/xterm-addon-fit.d.ts",
"repository": "https://github.com/xtermjs/xterm.js", "repository": "https://github.com/xtermjs/xterm.js",
"license": "MIT", "license": "MIT",
"keywords": [
"terminal",
"xterm",
"xterm.js"
],
"scripts": { "scripts": {
"build": "../../node_modules/.bin/tsc -p .", "build": "../../node_modules/.bin/tsc -p .",
"prepackage": "npm run build", "prepackage": "npm run build",
+4 -4
View File
@@ -1,6 +1,6 @@
{ {
"name": "xterm-addon-ligatures", "name": "xterm-addon-ligatures",
"version": "0.5.1", "version": "0.5.2",
"description": "Add support for programming ligatures to xterm.js", "description": "Add support for programming ligatures to xterm.js",
"author": { "author": {
"name": "The xterm.js authors", "name": "The xterm.js authors",
@@ -25,14 +25,14 @@
"keywords": [ "keywords": [
"font", "font",
"ligature", "ligature",
"terminal",
"xterm", "xterm",
"xterm.js", "xterm.js"
"terminal"
], ],
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"font-finder": "^1.1.0", "font-finder": "^1.1.0",
"font-ligatures": "^1.4.0" "font-ligatures": "^1.4.1"
}, },
"devDependencies": { "devDependencies": {
"@types/sinon": "^5.0.1", "@types/sinon": "^5.0.1",
+7 -7
View File
@@ -67,9 +67,9 @@ fd-slicer@~1.1.0:
pend "~1.2.0" pend "~1.2.0"
follow-redirects@^1.14.0: follow-redirects@^1.14.0:
version "1.14.3" version "1.14.7"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.3.tgz#6ada78118d8d24caee595595accdc0ac6abd022e" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.7.tgz#2004c02eb9436eee9a21446a6477debf17e81685"
integrity sha512-3MkHxknWMUtb23apkgz/83fDoe+y+qr0TdgacGIA7bew+QLBo3vdgEN2xEsuXNivpFy4CyDhBBZnNZOtalmenw== integrity sha512-+hbxoLbFMbRKDwohX8GkTataGqO6Jb7jGwpAlwgy2bIz25XtRm7KEzJM76R1WiNT5SwZkX4Y75SwBolkpmE7iQ==
font-finder@^1.0.3: font-finder@^1.0.3:
version "1.0.4" version "1.0.4"
@@ -86,10 +86,10 @@ font-finder@^1.1.0:
get-system-fonts "^2.0.0" get-system-fonts "^2.0.0"
promise-stream-reader "^1.0.1" promise-stream-reader "^1.0.1"
font-ligatures@^1.4.0: font-ligatures@^1.4.1:
version "1.4.0" version "1.4.1"
resolved "https://registry.npmjs.org/font-ligatures/-/font-ligatures-1.4.0.tgz#6a7b370d96be1358dddfad67830e82fbfd59e6dc" resolved "https://registry.npmjs.org/font-ligatures/-/font-ligatures-1.4.1.tgz#e8c9df7cf6bee80c0cca4a9a578618c7b1f5d8de"
integrity sha512-n7DFnnEpJ0NrVoLqZIL4tMGVs+CnFwQc92m80LWyrbgAFO4x234+t2/H9o4eOYA1eh6ta9dZAEEsJAwsBdNezA== integrity sha512-7W6zlfyhvCqShZ5ReUWqmSd9vBaUudW0Hxis+tqUjtHhsPU+L3Grf8mcZAtCiXHTzorhwdRTId2WeH/88gdFkw==
dependencies: dependencies:
font-finder "^1.0.3" font-finder "^1.0.3"
lru-cache "^6.0.0" lru-cache "^6.0.0"
+5
View File
@@ -9,6 +9,11 @@
"types": "typings/xterm-addon-search.d.ts", "types": "typings/xterm-addon-search.d.ts",
"repository": "https://github.com/xtermjs/xterm.js", "repository": "https://github.com/xtermjs/xterm.js",
"license": "MIT", "license": "MIT",
"keywords": [
"terminal",
"xterm",
"xterm.js"
],
"scripts": { "scripts": {
"build": "../../node_modules/.bin/tsc -p .", "build": "../../node_modules/.bin/tsc -p .",
"prepackage": "npm run build", "prepackage": "npm run build",
@@ -9,6 +9,11 @@
"types": "typings/xterm-addon-serialize.d.ts", "types": "typings/xterm-addon-serialize.d.ts",
"repository": "https://github.com/xtermjs/xterm.js", "repository": "https://github.com/xtermjs/xterm.js",
"license": "MIT", "license": "MIT",
"keywords": [
"terminal",
"xterm",
"xterm.js"
],
"scripts": { "scripts": {
"build": "../../node_modules/.bin/tsc -p .", "build": "../../node_modules/.bin/tsc -p .",
"prepackage": "npm run build", "prepackage": "npm run build",
@@ -9,6 +9,11 @@
"types": "typings/xterm-addon-unicode11.d.ts", "types": "typings/xterm-addon-unicode11.d.ts",
"repository": "https://github.com/xtermjs/xterm.js", "repository": "https://github.com/xtermjs/xterm.js",
"license": "MIT", "license": "MIT",
"keywords": [
"terminal",
"xterm",
"xterm.js"
],
"scripts": { "scripts": {
"build": "../../node_modules/.bin/tsc -p .", "build": "../../node_modules/.bin/tsc -p .",
"prepackage": "npm run build", "prepackage": "npm run build",
+6 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "xterm-addon-web-links", "name": "xterm-addon-web-links",
"version": "0.5.0", "version": "0.5.1",
"author": { "author": {
"name": "The xterm.js authors", "name": "The xterm.js authors",
"url": "https://xtermjs.org/" "url": "https://xtermjs.org/"
@@ -9,6 +9,11 @@
"types": "typings/xterm-addon-web-links.d.ts", "types": "typings/xterm-addon-web-links.d.ts",
"repository": "https://github.com/xtermjs/xterm.js", "repository": "https://github.com/xtermjs/xterm.js",
"license": "MIT", "license": "MIT",
"keywords": [
"terminal",
"xterm",
"xterm.js"
],
"scripts": { "scripts": {
"build": "../../node_modules/.bin/tsc -p .", "build": "../../node_modules/.bin/tsc -p .",
"prepackage": "npm run build", "prepackage": "npm run build",
@@ -10,7 +10,7 @@ const protocolClause = '(https?:\\/\\/)';
const domainCharacterSet = '[\\da-z\\.-]+'; const domainCharacterSet = '[\\da-z\\.-]+';
const negatedDomainCharacterSet = '[^\\da-z\\.-]+'; const negatedDomainCharacterSet = '[^\\da-z\\.-]+';
const domainBodyClause = '(' + domainCharacterSet + ')'; const domainBodyClause = '(' + domainCharacterSet + ')';
const tldClause = '([a-z\\.]{2,6})'; const tldClause = '([a-z\\.]{2,18})';
const ipClause = '((\\d{1,3}\\.){3}\\d{1,3})'; const ipClause = '((\\d{1,3}\\.){3}\\d{1,3})';
const localHostClause = '(localhost)'; const localHostClause = '(localhost)';
const portClause = '(:\\d{1,5})'; const portClause = '(:\\d{1,5})';
+6
View File
@@ -9,6 +9,12 @@
"types": "typings/xterm-addon-webgl.d.ts", "types": "typings/xterm-addon-webgl.d.ts",
"repository": "https://github.com/xtermjs/xterm.js", "repository": "https://github.com/xtermjs/xterm.js",
"license": "MIT", "license": "MIT",
"keywords": [
"terminal",
"webgl",
"xterm",
"xterm.js"
],
"scripts": { "scripts": {
"build": "../../node_modules/.bin/tsc -p .", "build": "../../node_modules/.bin/tsc -p .",
"prepackage": "npm run build", "prepackage": "npm run build",
+18 -17
View File
@@ -105,22 +105,23 @@ jobs:
- script: xvfb-run --auto-servernum -- bash -c "yarn test-api-firefox --headless --forbid-only" - script: xvfb-run --auto-servernum -- bash -c "yarn test-api-firefox --headless --forbid-only"
displayName: 'Integration tests (Firefox)' displayName: 'Integration tests (Firefox)'
- job: macOS_IntegrationTests # Integration tests are too flaky on macOS https://github.com/xtermjs/xterm.js/issues/3590
pool: # - job: macOS_IntegrationTests
vmImage: 'macOS-10.15' # pool:
steps: # vmImage: 'macOS-10.15'
- task: NodeTool@0 # steps:
inputs: # - task: NodeTool@0
versionSpec: '14.x' # inputs:
displayName: 'Install Node.js' # versionSpec: '14.x'
- script: yarn --frozen-lockfile # displayName: 'Install Node.js'
displayName: 'Install dependencies and build' # - script: yarn --frozen-lockfile
- script: yarn test-api-chromium --headless --forbid-only # displayName: 'Install dependencies and build'
displayName: 'Integration tests (Chromium)' # - script: yarn test-api-chromium --headless --forbid-only
- script: yarn test-api-firefox --headless --forbid-only # displayName: 'Integration tests (Chromium)'
displayName: 'Integration tests (Firefox)' # - script: yarn test-api-firefox --headless --forbid-only
- script: yarn test-api-webkit --headless --forbid-only # displayName: 'Integration tests (Firefox)'
displayName: 'Integration tests (Webkit)' # - script: yarn test-api-webkit --headless --forbid-only
# displayName: 'Integration tests (Webkit)'
- job: Windows_IntegrationTests - job: Windows_IntegrationTests
pool: pool:
@@ -143,7 +144,7 @@ jobs:
- macOS - macOS
- Windows - Windows
- Linux_IntegrationTests - Linux_IntegrationTests
- macOS_IntegrationTests # - macOS_IntegrationTests
- Windows_IntegrationTests - Windows_IntegrationTests
condition: and(succeeded(), or(eq(variables['Build.SourceBranch'], 'refs/heads/master'), eq(variables['FORCE_RELEASE'], 'true'))) condition: and(succeeded(), or(eq(variables['Build.SourceBranch'], 'refs/heads/master'), eq(variables['FORCE_RELEASE'], 'true')))
pool: pool:
+16 -1
View File
@@ -1,12 +1,27 @@
{ {
"name": "xterm", "name": "xterm",
"description": "Full xterm terminal, in your browser", "description": "Full xterm terminal, in your browser",
"version": "4.16.0", "version": "4.17.0",
"main": "lib/xterm.js", "main": "lib/xterm.js",
"style": "css/xterm.css", "style": "css/xterm.css",
"types": "typings/xterm.d.ts", "types": "typings/xterm.d.ts",
"repository": "https://github.com/xtermjs/xterm.js", "repository": "https://github.com/xtermjs/xterm.js",
"license": "MIT", "license": "MIT",
"keywords": [
"cli",
"command-line",
"console",
"pty",
"shell",
"ssh",
"styles",
"terminal-emulator",
"terminal",
"tty",
"vt100",
"webgl",
"xterm"
],
"scripts": { "scripts": {
"prepackage": "npm run build", "prepackage": "npm run build",
"package": "webpack", "package": "webpack",
+1 -1
View File
@@ -150,7 +150,7 @@ export class MouseZoneManager extends Disposable implements IMouseZoneManager {
} }
// Restart the tooltip timeout // Restart the tooltip timeout
this._tooltipTimeout = window.setTimeout(() => this._onTooltip(e), this._optionsService.options.linkTooltipHoverDuration); this._tooltipTimeout = window.setTimeout(() => this._onTooltip(e), this._optionsService.rawOptions.linkTooltipHoverDuration);
} }
private _onTooltip(e: MouseEvent): void { private _onTooltip(e: MouseEvent): void {
+5 -5
View File
@@ -298,7 +298,7 @@ export class Terminal extends CoreTerminal implements ITerminal {
this.viewport?.syncScrollArea(); this.viewport?.syncScrollArea();
break; break;
case 'screenReaderMode': case 'screenReaderMode':
if (this.optionsService.options.screenReaderMode) { if (this.optionsService.rawOptions.screenReaderMode) {
if (!this._accessibilityManager && this._renderService) { if (!this._accessibilityManager && this._renderService) {
this._accessibilityManager = new AccessibilityManager(this, this._renderService); this._accessibilityManager = new AccessibilityManager(this, this._renderService);
} }
@@ -309,7 +309,7 @@ export class Terminal extends CoreTerminal implements ITerminal {
break; break;
case 'tabStopWidth': this.buffers.setupTabStops(); break; case 'tabStopWidth': this.buffers.setupTabStops(); break;
case 'theme': case 'theme':
this._setTheme(this.optionsService.options.theme); this._setTheme(this.optionsService.rawOptions.theme);
break; break;
} }
} }
@@ -753,7 +753,7 @@ export class Terminal extends CoreTerminal implements ITerminal {
this.register(this.coreMouseService.onProtocolChange(events => { this.register(this.coreMouseService.onProtocolChange(events => {
// apply global changes on events // apply global changes on events
if (events) { if (events) {
if (this.optionsService.options.logLevel === 'debug') { if (this.optionsService.rawOptions.logLevel === 'debug') {
this._logService.debug('Binding to mouse events:', this.coreMouseService.explainEvents(events)); this._logService.debug('Binding to mouse events:', this.coreMouseService.explainEvents(events));
} }
this.element!.classList.add('enable-mouse-events'); this.element!.classList.add('enable-mouse-events');
@@ -1125,7 +1125,7 @@ export class Terminal extends CoreTerminal implements ITerminal {
// other listeners. When screen reader mode is enabled, this could cause issues if the event // other listeners. When screen reader mode is enabled, this could cause issues if the event
// is handled at a higher level, this is a compromise in order to echo keys to the screen // is handled at a higher level, this is a compromise in order to echo keys to the screen
// reader. // reader.
if (!this.optionsService.options.screenReaderMode) { if (!this.optionsService.rawOptions.screenReaderMode) {
return this.cancel(event, true); return this.cancel(event, true);
} }
@@ -1220,7 +1220,7 @@ export class Terminal extends CoreTerminal implements ITerminal {
protected _inputEvent(ev: InputEvent): boolean { protected _inputEvent(ev: InputEvent): boolean {
// Only support emoji IMEs when screen reader mode is disabled as the event must bubble up to // Only support emoji IMEs when screen reader mode is disabled as the event must bubble up to
// support reading out character input which can doubling up input characters // support reading out character input which can doubling up input characters
if (ev.data && ev.inputType === 'insertText' && !ev.composed && !this.optionsService.options.screenReaderMode) { if (ev.data && ev.inputType === 'insertText' && !ev.composed && !this.optionsService.rawOptions.screenReaderMode) {
if (this._keyPressHandled) { if (this._keyPressHandled) {
return false; return false;
} }
+5 -5
View File
@@ -110,7 +110,7 @@ export class Viewport extends Disposable implements IViewport {
} }
// Update scroll bar width // Update scroll bar width
if (this._optionsService.options.scrollback === 0) { if (this._optionsService.rawOptions.scrollback === 0) {
this.scrollBarWidth = 0; this.scrollBarWidth = 0;
} else { } else {
this.scrollBarWidth = (this._viewportElement.offsetWidth - this._scrollArea.offsetWidth) || FALLBACK_SCROLL_BAR_WIDTH; this.scrollBarWidth = (this._viewportElement.offsetWidth - this._scrollArea.offsetWidth) || FALLBACK_SCROLL_BAR_WIDTH;
@@ -153,7 +153,7 @@ export class Viewport extends Disposable implements IViewport {
} }
// If the scroll bar visibility changed // If the scroll bar visibility changed
if (this._lastHadScrollBar !== (this._optionsService.options.scrollback > 0)) { if (this._lastHadScrollBar !== (this._optionsService.rawOptions.scrollback > 0)) {
this._refresh(immediate); this._refresh(immediate);
} }
} }
@@ -259,15 +259,15 @@ export class Viewport extends Disposable implements IViewport {
} }
private _applyScrollModifier(amount: number, ev: WheelEvent): number { private _applyScrollModifier(amount: number, ev: WheelEvent): number {
const modifier = this._optionsService.options.fastScrollModifier; const modifier = this._optionsService.rawOptions.fastScrollModifier;
// Multiply the scroll speed when the modifier is down // Multiply the scroll speed when the modifier is down
if ((modifier === 'alt' && ev.altKey) || if ((modifier === 'alt' && ev.altKey) ||
(modifier === 'ctrl' && ev.ctrlKey) || (modifier === 'ctrl' && ev.ctrlKey) ||
(modifier === 'shift' && ev.shiftKey)) { (modifier === 'shift' && ev.shiftKey)) {
return amount * this._optionsService.options.fastScrollSensitivity * this._optionsService.options.scrollSensitivity; return amount * this._optionsService.rawOptions.fastScrollSensitivity * this._optionsService.rawOptions.scrollSensitivity;
} }
return amount * this._optionsService.options.scrollSensitivity; return amount * this._optionsService.rawOptions.scrollSensitivity;
} }
/** /**
+2 -2
View File
@@ -217,8 +217,8 @@ export class CompositionHelper {
this._compositionView.style.top = cursorTop + 'px'; this._compositionView.style.top = cursorTop + 'px';
this._compositionView.style.height = cellHeight + 'px'; this._compositionView.style.height = cellHeight + 'px';
this._compositionView.style.lineHeight = cellHeight + 'px'; this._compositionView.style.lineHeight = cellHeight + 'px';
this._compositionView.style.fontFamily = this._optionsService.options.fontFamily; this._compositionView.style.fontFamily = this._optionsService.rawOptions.fontFamily;
this._compositionView.style.fontSize = this._optionsService.options.fontSize + 'px'; this._compositionView.style.fontSize = this._optionsService.rawOptions.fontSize + 'px';
// Sync the textarea to the exact position of the composition view so the IME knows where the // Sync the textarea to the exact position of the composition view so the IME knows where the
// text is. // text is.
const compositionViewBounds = this._compositionView.getBoundingClientRect(); const compositionViewBounds = this._compositionView.getBoundingClientRect();
+15 -11
View File
@@ -30,17 +30,21 @@ export class Terminal implements ITerminalApi {
this._core = new TerminalCore(options); this._core = new TerminalCore(options);
this._addonManager = new AddonManager(); this._addonManager = new AddonManager();
this._publicOptions = {}; this._publicOptions = { ... this._core.options };
const getter = (propName: string): any => {
return this._core.options[propName];
};
const setter = (propName: string, value: any): void => {
this._checkReadonlyOptions(propName);
this._core.options[propName] = value;
};
for (const propName in this._core.options) { for (const propName in this._core.options) {
Object.defineProperty(this._publicOptions, propName, { const desc = {
get: () => { get: getter.bind(this, propName),
return this._core.options[propName]; set: setter.bind(this, propName)
}, };
set: (value: any) => { Object.defineProperty(this._publicOptions, propName, desc);
this._checkReadonlyOptions(propName);
this._core.options[propName] = value;
}
});
} }
} }
@@ -54,7 +58,7 @@ export class Terminal implements ITerminalApi {
} }
private _checkProposedApi(): void { private _checkProposedApi(): void {
if (!this._core.optionsService.options.allowProposedApi) { if (!this._core.optionsService.rawOptions.allowProposedApi) {
throw new Error('You must set the allowProposedApi option to true to use proposed API'); throw new Error('You must set the allowProposedApi option to true to use proposed API');
} }
} }
+11 -11
View File
@@ -112,7 +112,7 @@ export abstract class BaseRenderLayer implements IRenderLayer {
if (this._scaledCharWidth <= 0 && this._scaledCharHeight <= 0) { if (this._scaledCharWidth <= 0 && this._scaledCharHeight <= 0) {
return; return;
} }
this._charAtlas = acquireCharAtlas(this._optionsService.options, this._rendererId, colorSet, this._scaledCharWidth, this._scaledCharHeight); this._charAtlas = acquireCharAtlas(this._optionsService.rawOptions, this._rendererId, colorSet, this._scaledCharWidth, this._scaledCharHeight);
this._charAtlas.warmUp(); this._charAtlas.warmUp();
} }
@@ -267,7 +267,7 @@ export abstract class BaseRenderLayer implements IRenderLayer {
// Draw custom characters if applicable // Draw custom characters if applicable
let drawSuccess = false; let drawSuccess = false;
if (this._optionsService.options.customGlyphs !== false) { if (this._optionsService.rawOptions.customGlyphs !== false) {
drawSuccess = tryDrawCustomChar(this._ctx, cell.getChars(), x * this._scaledCellWidth, y * this._scaledCellHeight, this._scaledCellWidth, this._scaledCellHeight); drawSuccess = tryDrawCustomChar(this._ctx, cell.getChars(), x * this._scaledCellWidth, y * this._scaledCellHeight, this._scaledCellWidth, this._scaledCellHeight);
} }
@@ -315,7 +315,7 @@ export abstract class BaseRenderLayer implements IRenderLayer {
fg = (cell.isFgDefault()) ? DEFAULT_COLOR : cell.getFgColor(); fg = (cell.isFgDefault()) ? DEFAULT_COLOR : cell.getFgColor();
} }
const drawInBrightColor = this._optionsService.options.drawBoldTextInBrightColors && cell.isBold() && fg < 8; const drawInBrightColor = this._optionsService.rawOptions.drawBoldTextInBrightColors && cell.isBold() && fg < 8;
fg += drawInBrightColor ? 8 : 0; fg += drawInBrightColor ? 8 : 0;
this._currentGlyphIdentifier.chars = cell.getChars() || WHITESPACE_CELL_CHAR; this._currentGlyphIdentifier.chars = cell.getChars() || WHITESPACE_CELL_CHAR;
@@ -356,7 +356,7 @@ export abstract class BaseRenderLayer implements IRenderLayer {
this._ctx.fillStyle = `rgb(${AttributeData.toColorRGB(cell.getBgColor()).join(',')})`; this._ctx.fillStyle = `rgb(${AttributeData.toColorRGB(cell.getBgColor()).join(',')})`;
} else { } else {
let bg = cell.getBgColor(); let bg = cell.getBgColor();
if (this._optionsService.options.drawBoldTextInBrightColors && cell.isBold() && bg < 8) { if (this._optionsService.rawOptions.drawBoldTextInBrightColors && cell.isBold() && bg < 8) {
bg += 8; bg += 8;
} }
this._ctx.fillStyle = this._colors.ansi[bg].css; this._ctx.fillStyle = this._colors.ansi[bg].css;
@@ -370,7 +370,7 @@ export abstract class BaseRenderLayer implements IRenderLayer {
this._ctx.fillStyle = `rgb(${AttributeData.toColorRGB(cell.getFgColor()).join(',')})`; this._ctx.fillStyle = `rgb(${AttributeData.toColorRGB(cell.getFgColor()).join(',')})`;
} else { } else {
let fg = cell.getFgColor(); let fg = cell.getFgColor();
if (this._optionsService.options.drawBoldTextInBrightColors && cell.isBold() && fg < 8) { if (this._optionsService.rawOptions.drawBoldTextInBrightColors && cell.isBold() && fg < 8) {
fg += 8; fg += 8;
} }
this._ctx.fillStyle = this._colors.ansi[fg].css; this._ctx.fillStyle = this._colors.ansi[fg].css;
@@ -386,7 +386,7 @@ export abstract class BaseRenderLayer implements IRenderLayer {
// Draw custom characters if applicable // Draw custom characters if applicable
let drawSuccess = false; let drawSuccess = false;
if (this._optionsService.options.customGlyphs !== false) { if (this._optionsService.rawOptions.customGlyphs !== false) {
drawSuccess = tryDrawCustomChar(this._ctx, cell.getChars(), x * this._scaledCellWidth, y * this._scaledCellHeight, this._scaledCellWidth, this._scaledCellHeight); drawSuccess = tryDrawCustomChar(this._ctx, cell.getChars(), x * this._scaledCellWidth, y * this._scaledCellHeight, this._scaledCellWidth, this._scaledCellHeight);
} }
@@ -421,14 +421,14 @@ export abstract class BaseRenderLayer implements IRenderLayer {
* @param isBold If we should use the bold fontWeight. * @param isBold If we should use the bold fontWeight.
*/ */
protected _getFont(isBold: boolean, isItalic: boolean): string { protected _getFont(isBold: boolean, isItalic: boolean): string {
const fontWeight = isBold ? this._optionsService.options.fontWeightBold : this._optionsService.options.fontWeight; const fontWeight = isBold ? this._optionsService.rawOptions.fontWeightBold : this._optionsService.rawOptions.fontWeight;
const fontStyle = isItalic ? 'italic' : ''; const fontStyle = isItalic ? 'italic' : '';
return `${fontStyle} ${fontWeight} ${this._optionsService.options.fontSize * window.devicePixelRatio}px ${this._optionsService.options.fontFamily}`; return `${fontStyle} ${fontWeight} ${this._optionsService.rawOptions.fontSize * window.devicePixelRatio}px ${this._optionsService.rawOptions.fontFamily}`;
} }
private _getContrastColor(cell: CellData): IColor | undefined { private _getContrastColor(cell: CellData): IColor | undefined {
if (this._optionsService.options.minimumContrastRatio === 1) { if (this._optionsService.rawOptions.minimumContrastRatio === 1) {
return undefined; return undefined;
} }
@@ -455,7 +455,7 @@ export abstract class BaseRenderLayer implements IRenderLayer {
const bgRgba = this._resolveBackgroundRgba(bgColorMode, bgColor, isInverse); const bgRgba = this._resolveBackgroundRgba(bgColorMode, bgColor, isInverse);
const fgRgba = this._resolveForegroundRgba(fgColorMode, fgColor, isInverse, isBold); const fgRgba = this._resolveForegroundRgba(fgColorMode, fgColor, isInverse, isBold);
const result = rgba.ensureContrastRatio(bgRgba, fgRgba, this._optionsService.options.minimumContrastRatio); const result = rgba.ensureContrastRatio(bgRgba, fgRgba, this._optionsService.rawOptions.minimumContrastRatio);
if (!result) { if (!result) {
this._colors.contrastCache.setColor(cell.bg, cell.fg, null); this._colors.contrastCache.setColor(cell.bg, cell.fg, null);
@@ -495,7 +495,7 @@ export abstract class BaseRenderLayer implements IRenderLayer {
switch (fgColorMode) { switch (fgColorMode) {
case Attributes.CM_P16: case Attributes.CM_P16:
case Attributes.CM_P256: case Attributes.CM_P256:
if (this._optionsService.options.drawBoldTextInBrightColors && bold && fgColor < 8) { if (this._optionsService.rawOptions.drawBoldTextInBrightColors && bold && fgColor < 8) {
fgColor += 8; fgColor += 8;
} }
return this._colors.ansi[fgColor].rgba; return this._colors.ansi[fgColor].rgba;
+6 -6
View File
@@ -94,7 +94,7 @@ export class CursorRenderLayer extends BaseRenderLayer {
} }
public onOptionsChanged(): void { public onOptionsChanged(): void {
if (this._optionsService.options.cursorBlink) { if (this._optionsService.rawOptions.cursorBlink) {
if (!this._cursorBlinkStateManager) { if (!this._cursorBlinkStateManager) {
this._cursorBlinkStateManager = new CursorBlinkStateManager(this._coreBrowserService.isFocused, () => { this._cursorBlinkStateManager = new CursorBlinkStateManager(this._coreBrowserService.isFocused, () => {
this._render(true); this._render(true);
@@ -148,7 +148,7 @@ export class CursorRenderLayer extends BaseRenderLayer {
this._clearCursor(); this._clearCursor();
this._ctx.save(); this._ctx.save();
this._ctx.fillStyle = this._colors.cursor.css; this._ctx.fillStyle = this._colors.cursor.css;
const cursorStyle = this._optionsService.options.cursorStyle; const cursorStyle = this._optionsService.rawOptions.cursorStyle;
if (cursorStyle && cursorStyle !== 'block') { if (cursorStyle && cursorStyle !== 'block') {
this._cursorRenderers[cursorStyle](cursorX, viewportRelativeCursorY, this._cell); this._cursorRenderers[cursorStyle](cursorX, viewportRelativeCursorY, this._cell);
} else { } else {
@@ -174,7 +174,7 @@ export class CursorRenderLayer extends BaseRenderLayer {
if (this._state.x === cursorX && if (this._state.x === cursorX &&
this._state.y === viewportRelativeCursorY && this._state.y === viewportRelativeCursorY &&
this._state.isFocused === this._coreBrowserService.isFocused && this._state.isFocused === this._coreBrowserService.isFocused &&
this._state.style === this._optionsService.options.cursorStyle && this._state.style === this._optionsService.rawOptions.cursorStyle &&
this._state.width === this._cell.getWidth()) { this._state.width === this._cell.getWidth()) {
return; return;
} }
@@ -182,13 +182,13 @@ export class CursorRenderLayer extends BaseRenderLayer {
} }
this._ctx.save(); this._ctx.save();
this._cursorRenderers[this._optionsService.options.cursorStyle || 'block'](cursorX, viewportRelativeCursorY, this._cell); this._cursorRenderers[this._optionsService.rawOptions.cursorStyle || 'block'](cursorX, viewportRelativeCursorY, this._cell);
this._ctx.restore(); this._ctx.restore();
this._state.x = cursorX; this._state.x = cursorX;
this._state.y = viewportRelativeCursorY; this._state.y = viewportRelativeCursorY;
this._state.isFocused = false; this._state.isFocused = false;
this._state.style = this._optionsService.options.cursorStyle; this._state.style = this._optionsService.rawOptions.cursorStyle;
this._state.width = this._cell.getWidth(); this._state.width = this._cell.getWidth();
} }
@@ -213,7 +213,7 @@ export class CursorRenderLayer extends BaseRenderLayer {
private _renderBarCursor(x: number, y: number, cell: ICellData): void { private _renderBarCursor(x: number, y: number, cell: ICellData): void {
this._ctx.save(); this._ctx.save();
this._ctx.fillStyle = this._colors.cursor.css; this._ctx.fillStyle = this._colors.cursor.css;
this._fillLeftLineAtCell(x, y, this._optionsService.options.cursorWidth); this._fillLeftLineAtCell(x, y, this._optionsService.rawOptions.cursorWidth);
this._ctx.restore(); this._ctx.restore();
} }
+5 -5
View File
@@ -39,7 +39,7 @@ export class Renderer extends Disposable implements IRenderer {
@IOptionsService private readonly _optionsService: IOptionsService @IOptionsService private readonly _optionsService: IOptionsService
) { ) {
super(); super();
const allowTransparency = this._optionsService.options.allowTransparency; const allowTransparency = this._optionsService.rawOptions.allowTransparency;
this._renderLayers = [ this._renderLayers = [
instantiationService.createInstance(TextRenderLayer, this._screenElement, 0, this._colors, allowTransparency, this._id), instantiationService.createInstance(TextRenderLayer, this._screenElement, 0, this._colors, allowTransparency, this._id),
instantiationService.createInstance(SelectionRenderLayer, this._screenElement, 1, this._colors, this._id), instantiationService.createInstance(SelectionRenderLayer, this._screenElement, 1, this._colors, this._id),
@@ -178,18 +178,18 @@ export class Renderer extends Disposable implements IRenderer {
// will be floored because since lineHeight can never be lower then 1, there // 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 // is a guarentee that the scaled line height will always be larger than
// scaled char height. // scaled char height.
this.dimensions.scaledCellHeight = Math.floor(this.dimensions.scaledCharHeight * this._optionsService.options.lineHeight); this.dimensions.scaledCellHeight = Math.floor(this.dimensions.scaledCharHeight * this._optionsService.rawOptions.lineHeight);
// Calculate the y coordinate within a cell that text should draw from in // Calculate the y coordinate within a cell that text should draw from in
// order to draw in the center of a cell. // order to draw in the center of a cell.
this.dimensions.scaledCharTop = this._optionsService.options.lineHeight === 1 ? 0 : Math.round((this.dimensions.scaledCellHeight - this.dimensions.scaledCharHeight) / 2); this.dimensions.scaledCharTop = this._optionsService.rawOptions.lineHeight === 1 ? 0 : Math.round((this.dimensions.scaledCellHeight - this.dimensions.scaledCharHeight) / 2);
// Calculate the scaled cell width, taking the letterSpacing into account. // Calculate the scaled cell width, taking the letterSpacing into account.
this.dimensions.scaledCellWidth = this.dimensions.scaledCharWidth + Math.round(this._optionsService.options.letterSpacing); this.dimensions.scaledCellWidth = this.dimensions.scaledCharWidth + Math.round(this._optionsService.rawOptions.letterSpacing);
// Calculate the x coordinate with a cell that text should draw from in // Calculate the x coordinate with a cell that text should draw from in
// order to draw in the center of a cell. // order to draw in the center of a cell.
this.dimensions.scaledCharLeft = Math.floor(this._optionsService.options.letterSpacing / 2); this.dimensions.scaledCharLeft = Math.floor(this._optionsService.rawOptions.letterSpacing / 2);
// Recalculate the canvas dimensions; scaled* define the actual number of // Recalculate the canvas dimensions; scaled* define the actual number of
// pixel in the canvas // pixel in the canvas
+3 -3
View File
@@ -225,7 +225,7 @@ export class TextRenderLayer extends BaseRenderLayer {
this._ctx.fillStyle = `rgb(${AttributeData.toColorRGB(cell.getBgColor()).join(',')})`; this._ctx.fillStyle = `rgb(${AttributeData.toColorRGB(cell.getBgColor()).join(',')})`;
} else { } else {
let bg = cell.getBgColor(); let bg = cell.getBgColor();
if (this._optionsService.options.drawBoldTextInBrightColors && cell.isBold() && bg < 8) { if (this._optionsService.rawOptions.drawBoldTextInBrightColors && cell.isBold() && bg < 8) {
bg += 8; bg += 8;
} }
this._ctx.fillStyle = this._colors.ansi[bg].css; this._ctx.fillStyle = this._colors.ansi[bg].css;
@@ -237,7 +237,7 @@ export class TextRenderLayer extends BaseRenderLayer {
this._ctx.fillStyle = `rgb(${AttributeData.toColorRGB(cell.getFgColor()).join(',')})`; this._ctx.fillStyle = `rgb(${AttributeData.toColorRGB(cell.getFgColor()).join(',')})`;
} else { } else {
let fg = cell.getFgColor(); let fg = cell.getFgColor();
if (this._optionsService.options.drawBoldTextInBrightColors && cell.isBold() && fg < 8) { if (this._optionsService.rawOptions.drawBoldTextInBrightColors && cell.isBold() && fg < 8) {
fg += 8; fg += 8;
} }
this._ctx.fillStyle = this._colors.ansi[fg].css; this._ctx.fillStyle = this._colors.ansi[fg].css;
@@ -271,7 +271,7 @@ export class TextRenderLayer extends BaseRenderLayer {
} }
public onOptionsChanged(): void { public onOptionsChanged(): void {
this._setTransparency(this._optionsService.options.allowTransparency); this._setTransparency(this._optionsService.rawOptions.allowTransparency);
} }
/** /**
+3 -3
View File
@@ -3,13 +3,13 @@
* @license MIT * @license MIT
*/ */
import { isFirefox } from 'common/Platform'; import { isFirefox, isLegacyEdge } from 'common/Platform';
export const INVERTED_DEFAULT_COLOR = 257; export const INVERTED_DEFAULT_COLOR = 257;
export const DIM_OPACITY = 0.5; export const DIM_OPACITY = 0.5;
// The text baseline is set conditionally by browser. Using 'ideographic' for Firefox would // The text baseline is set conditionally by browser. Using 'ideographic' for Firefox or Legacy Edge would
// result in truncated text (Issue 3353). Using 'bottom' for Chrome would result in slightly // result in truncated text (Issue 3353). Using 'bottom' for Chrome would result in slightly
// unaligned Powerline fonts (PR 3356#issuecomment-850928179). // unaligned Powerline fonts (PR 3356#issuecomment-850928179).
export const TEXT_BASELINE: CanvasTextBaseline = isFirefox ? 'bottom' : 'ideographic'; export const TEXT_BASELINE: CanvasTextBaseline = isFirefox || isLegacyEdge ? 'bottom' : 'ideographic';
export const CHAR_ATLAS_CELL_SPACING = 1; export const CHAR_ATLAS_CELL_SPACING = 1;
+9 -9
View File
@@ -107,8 +107,8 @@ export class DomRenderer extends Disposable implements IRenderer {
private _updateDimensions(): void { private _updateDimensions(): void {
this.dimensions.scaledCharWidth = this._charSizeService.width * window.devicePixelRatio; this.dimensions.scaledCharWidth = this._charSizeService.width * window.devicePixelRatio;
this.dimensions.scaledCharHeight = Math.ceil(this._charSizeService.height * window.devicePixelRatio); this.dimensions.scaledCharHeight = Math.ceil(this._charSizeService.height * window.devicePixelRatio);
this.dimensions.scaledCellWidth = this.dimensions.scaledCharWidth + Math.round(this._optionsService.options.letterSpacing); this.dimensions.scaledCellWidth = this.dimensions.scaledCharWidth + Math.round(this._optionsService.rawOptions.letterSpacing);
this.dimensions.scaledCellHeight = Math.floor(this.dimensions.scaledCharHeight * this._optionsService.options.lineHeight); this.dimensions.scaledCellHeight = Math.floor(this.dimensions.scaledCharHeight * this._optionsService.rawOptions.lineHeight);
this.dimensions.scaledCharLeft = 0; this.dimensions.scaledCharLeft = 0;
this.dimensions.scaledCharTop = 0; this.dimensions.scaledCharTop = 0;
this.dimensions.scaledCanvasWidth = this.dimensions.scaledCellWidth * this._bufferService.cols; this.dimensions.scaledCanvasWidth = this.dimensions.scaledCellWidth * this._bufferService.cols;
@@ -161,16 +161,16 @@ export class DomRenderer extends Disposable implements IRenderer {
let styles = let styles =
`${this._terminalSelector} .${ROW_CONTAINER_CLASS} {` + `${this._terminalSelector} .${ROW_CONTAINER_CLASS} {` +
` color: ${this._colors.foreground.css};` + ` color: ${this._colors.foreground.css};` +
` font-family: ${this._optionsService.options.fontFamily};` + ` font-family: ${this._optionsService.rawOptions.fontFamily};` +
` font-size: ${this._optionsService.options.fontSize}px;` + ` font-size: ${this._optionsService.rawOptions.fontSize}px;` +
`}`; `}`;
// Text styles // Text styles
styles += styles +=
`${this._terminalSelector} span:not(.${BOLD_CLASS}) {` + `${this._terminalSelector} span:not(.${BOLD_CLASS}) {` +
` font-weight: ${this._optionsService.options.fontWeight};` + ` font-weight: ${this._optionsService.rawOptions.fontWeight};` +
`}` + `}` +
`${this._terminalSelector} span.${BOLD_CLASS} {` + `${this._terminalSelector} span.${BOLD_CLASS} {` +
` font-weight: ${this._optionsService.options.fontWeightBold};` + ` font-weight: ${this._optionsService.rawOptions.fontWeightBold};` +
`}` + `}` +
`${this._terminalSelector} span.${ITALIC_CLASS} {` + `${this._terminalSelector} span.${ITALIC_CLASS} {` +
` font-style: italic;` + ` font-style: italic;` +
@@ -210,7 +210,7 @@ export class DomRenderer extends Disposable implements IRenderer {
` color: ${this._colors.cursorAccent.css};` + ` color: ${this._colors.cursorAccent.css};` +
`}` + `}` +
`${this._terminalSelector} .${ROW_CONTAINER_CLASS} .${CURSOR_CLASS}.${CURSOR_STYLE_BAR_CLASS} {` + `${this._terminalSelector} .${ROW_CONTAINER_CLASS} .${CURSOR_CLASS}.${CURSOR_STYLE_BAR_CLASS} {` +
` box-shadow: ${this._optionsService.options.cursorWidth}px 0 0 ${this._colors.cursor.css} inset;` + ` box-shadow: ${this._optionsService.rawOptions.cursorWidth}px 0 0 ${this._colors.cursor.css} inset;` +
`}` + `}` +
`${this._terminalSelector} .${ROW_CONTAINER_CLASS} .${CURSOR_CLASS}.${CURSOR_STYLE_UNDERLINE_CLASS} {` + `${this._terminalSelector} .${ROW_CONTAINER_CLASS} .${CURSOR_CLASS}.${CURSOR_STYLE_UNDERLINE_CLASS} {` +
` box-shadow: 0 -1px 0 ${this._colors.cursor.css} inset;` + ` box-shadow: 0 -1px 0 ${this._colors.cursor.css} inset;` +
@@ -356,7 +356,7 @@ export class DomRenderer extends Disposable implements IRenderer {
public renderRows(start: number, end: number): void { public renderRows(start: number, end: number): void {
const cursorAbsoluteY = this._bufferService.buffer.ybase + this._bufferService.buffer.y; const cursorAbsoluteY = this._bufferService.buffer.ybase + this._bufferService.buffer.y;
const cursorX = Math.min(this._bufferService.buffer.x, this._bufferService.cols - 1); const cursorX = Math.min(this._bufferService.buffer.x, this._bufferService.cols - 1);
const cursorBlink = this._optionsService.options.cursorBlink; const cursorBlink = this._optionsService.rawOptions.cursorBlink;
for (let y = start; y <= end; y++) { for (let y = start; y <= end; y++) {
const rowElement = this._rowElements[y]; const rowElement = this._rowElements[y];
@@ -364,7 +364,7 @@ export class DomRenderer extends Disposable implements IRenderer {
const row = y + this._bufferService.buffer.ydisp; const row = y + this._bufferService.buffer.ydisp;
const lineData = this._bufferService.buffer.lines.get(row); const lineData = this._bufferService.buffer.lines.get(row);
const cursorStyle = this._optionsService.options.cursorStyle; const cursorStyle = this._optionsService.rawOptions.cursorStyle;
rowElement.appendChild(this._rowFactory.createRow(lineData!, row, row === cursorAbsoluteY, cursorStyle, cursorX, cursorBlink, this.dimensions.actualCellWidth, this._bufferService.cols)); rowElement.appendChild(this._rowFactory.createRow(lineData!, row, row === cursorAbsoluteY, cursorStyle, cursorX, cursorBlink, this.dimensions.actualCellWidth, this._bufferService.cols));
} }
} }
@@ -175,7 +175,7 @@ export class DomRendererRowFactory {
switch (fgColorMode) { switch (fgColorMode) {
case Attributes.CM_P16: case Attributes.CM_P16:
case Attributes.CM_P256: case Attributes.CM_P256:
if (cell.isBold() && fg < 8 && this._optionsService.options.drawBoldTextInBrightColors) { if (cell.isBold() && fg < 8 && this._optionsService.rawOptions.drawBoldTextInBrightColors) {
fg += 8; fg += 8;
} }
if (!this._applyMinimumContrast(charElement, this._colors.background, this._colors.ansi[fg])) { if (!this._applyMinimumContrast(charElement, this._colors.background, this._colors.ansi[fg])) {
@@ -225,7 +225,7 @@ export class DomRendererRowFactory {
} }
private _applyMinimumContrast(element: HTMLElement, bg: IColor, fg: IColor): boolean { private _applyMinimumContrast(element: HTMLElement, bg: IColor, fg: IColor): boolean {
if (this._optionsService.options.minimumContrastRatio === 1) { if (this._optionsService.rawOptions.minimumContrastRatio === 1) {
return false; return false;
} }
@@ -234,7 +234,7 @@ export class DomRendererRowFactory {
// Calculate and store in cache // Calculate and store in cache
if (adjustedColor === undefined) { if (adjustedColor === undefined) {
adjustedColor = color.ensureContrastRatio(bg, fg, this._optionsService.options.minimumContrastRatio); adjustedColor = color.ensureContrastRatio(bg, fg, this._optionsService.rawOptions.minimumContrastRatio);
this._colors.contrastCache.setColor(this._workCell.bg, this._workCell.fg, adjustedColor ?? null); this._colors.contrastCache.setColor(this._workCell.bg, this._workCell.fg, adjustedColor ?? null);
} }
+2 -2
View File
@@ -69,8 +69,8 @@ class DomMeasureStrategy implements IMeasureStrategy {
} }
public measure(): IReadonlyMeasureResult { public measure(): IReadonlyMeasureResult {
this._measureElement.style.fontFamily = this._optionsService.options.fontFamily; this._measureElement.style.fontFamily = this._optionsService.rawOptions.fontFamily;
this._measureElement.style.fontSize = `${this._optionsService.options.fontSize}px`; this._measureElement.style.fontSize = `${this._optionsService.rawOptions.fontSize}px`;
// Note that this triggers a synchronous layout // Note that this triggers a synchronous layout
const geometry = this._measureElement.getBoundingClientRect(); const geometry = this._measureElement.getBoundingClientRect();
+3 -3
View File
@@ -414,7 +414,7 @@ export class SelectionService extends Disposable implements ISelectionService {
*/ */
public shouldForceSelection(event: MouseEvent): boolean { public shouldForceSelection(event: MouseEvent): boolean {
if (Browser.isMac) { if (Browser.isMac) {
return event.altKey && this._optionsService.options.macOptionClickForcesSelection; return event.altKey && this._optionsService.rawOptions.macOptionClickForcesSelection;
} }
return event.shiftKey; return event.shiftKey;
@@ -567,7 +567,7 @@ export class SelectionService extends Disposable implements ISelectionService {
* @param event the mouse or keyboard event * @param event the mouse or keyboard event
*/ */
public shouldColumnSelect(event: KeyboardEvent | MouseEvent): boolean { public shouldColumnSelect(event: KeyboardEvent | MouseEvent): boolean {
return event.altKey && !(Browser.isMac && this._optionsService.options.macOptionClickForcesSelection); return event.altKey && !(Browser.isMac && this._optionsService.rawOptions.macOptionClickForcesSelection);
} }
/** /**
@@ -993,7 +993,7 @@ export class SelectionService extends Disposable implements ISelectionService {
if (cell.getWidth() === 0) { if (cell.getWidth() === 0) {
return false; return false;
} }
return this._optionsService.options.wordSeparator.indexOf(cell.getChars()) >= 0; return this._optionsService.rawOptions.wordSeparator.indexOf(cell.getChars()) >= 0;
} }
/** /**
+1 -1
View File
@@ -34,7 +34,7 @@ export class SoundService implements ISoundService {
return; return;
} }
const bellAudioSource = ctx.createBufferSource(); const bellAudioSource = ctx.createBufferSource();
ctx.decodeAudioData(this._base64ToArrayBuffer(this._removeMimeType(this._optionsService.options.bellSound)), (buffer) => { ctx.decodeAudioData(this._base64ToArrayBuffer(this._removeMimeType(this._optionsService.rawOptions.bellSound)), (buffer) => {
bellAudioSource.buffer = buffer; bellAudioSource.buffer = buffer;
bellAudioSource.connect(ctx.destination); bellAudioSource.connect(ctx.destination);
bellAudioSource.start(0); bellAudioSource.start(0);
+2 -2
View File
@@ -247,7 +247,7 @@ export abstract class CoreTerminal extends Disposable implements ICoreTerminal {
} }
protected _setup(): void { protected _setup(): void {
if (this.optionsService.options.windowsMode) { if (this.optionsService.rawOptions.windowsMode) {
this._enableWindowsMode(); this._enableWindowsMode();
} }
} }
@@ -267,7 +267,7 @@ export abstract class CoreTerminal extends Disposable implements ICoreTerminal {
this.buffers.resize(this.cols, this.rows); this.buffers.resize(this.cols, this.rows);
break; break;
case 'windowsMode': case 'windowsMode':
if (this.optionsService.options.windowsMode) { if (this.optionsService.rawOptions.windowsMode) {
this._enableWindowsMode(); this._enableWindowsMode();
} else { } else {
this._windowsMode?.dispose(); this._windowsMode?.dispose();
+10 -10
View File
@@ -177,8 +177,8 @@ class DECRQSS implements IDcsHandler {
break; break;
case ' q': // DECSCUSR case ' q': // DECSCUSR
const STYLES: { [key: string]: number } = { 'block': 2, 'underline': 4, 'bar': 6 }; const STYLES: { [key: string]: number } = { 'block': 2, 'underline': 4, 'bar': 6 };
let style = STYLES[this._optionsService.options.cursorStyle]; let style = STYLES[this._optionsService.rawOptions.cursorStyle];
style -= this._optionsService.options.cursorBlink ? 1 : 0; style -= this._optionsService.rawOptions.cursorBlink ? 1 : 0;
this._coreService.triggerDataEvent(`${C0.ESC}P1$r${style} q${C0.ESC}\\`); this._coreService.triggerDataEvent(`${C0.ESC}P1$r${style} q${C0.ESC}\\`);
break; break;
default: default:
@@ -603,7 +603,7 @@ export class InputHandler extends Disposable implements IInputHandler {
let code: number; let code: number;
let chWidth: number; let chWidth: number;
const charset = this._charsetService.charset; const charset = this._charsetService.charset;
const screenReaderMode = this._optionsService.options.screenReaderMode; const screenReaderMode = this._optionsService.rawOptions.screenReaderMode;
const cols = this._bufferService.cols; const cols = this._bufferService.cols;
const wraparoundMode = this._coreService.decPrivateModes.wraparound; const wraparoundMode = this._coreService.decPrivateModes.wraparound;
const insertMode = this._coreService.modes.insertMode; const insertMode = this._coreService.modes.insertMode;
@@ -745,7 +745,7 @@ export class InputHandler extends Disposable implements IInputHandler {
if (id.final === 't' && !id.prefix && !id.intermediates) { if (id.final === 't' && !id.prefix && !id.intermediates) {
// security: always check whether window option is allowed // security: always check whether window option is allowed
return this._parser.registerCsiHandler(id, params => { return this._parser.registerCsiHandler(id, params => {
if (!paramToWindowOption(params.params[0], this._optionsService.options.windowOptions)) { if (!paramToWindowOption(params.params[0], this._optionsService.rawOptions.windowOptions)) {
return true; return true;
} }
return callback(params); return callback(params);
@@ -800,7 +800,7 @@ export class InputHandler extends Disposable implements IInputHandler {
*/ */
public lineFeed(): boolean { public lineFeed(): boolean {
this._dirtyRowService.markDirty(this._activeBuffer.y); this._dirtyRowService.markDirty(this._activeBuffer.y);
if (this._optionsService.options.convertEol) { if (this._optionsService.rawOptions.convertEol) {
this._activeBuffer.x = 0; this._activeBuffer.x = 0;
} }
this._activeBuffer.y++; this._activeBuffer.y++;
@@ -904,7 +904,7 @@ export class InputHandler extends Disposable implements IInputHandler {
} }
const originalX = this._activeBuffer.x; const originalX = this._activeBuffer.x;
this._activeBuffer.x = this._activeBuffer.nextStop(); this._activeBuffer.x = this._activeBuffer.nextStop();
if (this._optionsService.options.screenReaderMode) { if (this._optionsService.rawOptions.screenReaderMode) {
this._onA11yTab.fire(this._activeBuffer.x - originalX); this._onA11yTab.fire(this._activeBuffer.x - originalX);
} }
return true; return true;
@@ -1763,7 +1763,7 @@ export class InputHandler extends Disposable implements IInputHandler {
* @param term The terminal name to evaluate * @param term The terminal name to evaluate
*/ */
private _is(term: string): boolean { private _is(term: string): boolean {
return (this._optionsService.options.termName + '').indexOf(term) === 0; return (this._optionsService.rawOptions.termName + '').indexOf(term) === 0;
} }
/** /**
@@ -1929,7 +1929,7 @@ export class InputHandler extends Disposable implements IInputHandler {
* This is only active if 'SetWinLines' (24) is enabled * This is only active if 'SetWinLines' (24) is enabled
* through `options.windowsOptions`. * through `options.windowsOptions`.
*/ */
if (this._optionsService.options.windowOptions.setWinLines) { if (this._optionsService.rawOptions.windowOptions.setWinLines) {
this._bufferService.resize(132, this._bufferService.rows); this._bufferService.resize(132, this._bufferService.rows);
this._onRequestReset.fire(); this._onRequestReset.fire();
} }
@@ -2163,7 +2163,7 @@ export class InputHandler extends Disposable implements IInputHandler {
* This is only active if 'SetWinLines' (24) is enabled * This is only active if 'SetWinLines' (24) is enabled
* through `options.windowsOptions`. * through `options.windowsOptions`.
*/ */
if (this._optionsService.options.windowOptions.setWinLines) { if (this._optionsService.rawOptions.windowOptions.setWinLines) {
this._bufferService.resize(80, this._bufferService.rows); this._bufferService.resize(80, this._bufferService.rows);
this._onRequestReset.fire(); this._onRequestReset.fire();
} }
@@ -2740,7 +2740,7 @@ export class InputHandler extends Disposable implements IInputHandler {
* Ps >= 24 not implemented * Ps >= 24 not implemented
*/ */
public windowOptions(params: IParams): boolean { public windowOptions(params: IParams): boolean {
if (!paramToWindowOption(params.params[0], this._optionsService.options.windowOptions)) { if (!paramToWindowOption(params.params[0], this._optionsService.rawOptions.windowOptions)) {
return true; return true;
} }
const second = (params.length > 1) ? params.params[1] : 0; const second = (params.length > 1) ? params.params[1] : 0;
+1
View File
@@ -18,6 +18,7 @@ const userAgent = (isNode) ? 'node' : navigator.userAgent;
const platform = (isNode) ? 'node' : navigator.platform; const platform = (isNode) ? 'node' : navigator.platform;
export const isFirefox = userAgent.includes('Firefox'); export const isFirefox = userAgent.includes('Firefox');
export const isLegacyEdge = userAgent.includes('Edge');
export const isSafari = /^((?!chrome|android).)*safari/i.test(userAgent); export const isSafari = /^((?!chrome|android).)*safari/i.test(userAgent);
// Find the users platform. We use this to interpret the meta key // Find the users platform. We use this to interpret the meta key
+3 -2
View File
@@ -120,12 +120,13 @@ export class MockLogService implements ILogService {
export class MockOptionsService implements IOptionsService { export class MockOptionsService implements IOptionsService {
public serviceBrand: any; public serviceBrand: any;
public options: ITerminalOptions = clone(DEFAULT_OPTIONS); public readonly rawOptions: ITerminalOptions = clone(DEFAULT_OPTIONS);
public options: ITerminalOptions = this.rawOptions;
public onOptionChange: IEvent<string> = new EventEmitter<string>().event; public onOptionChange: IEvent<string> = new EventEmitter<string>().event;
constructor(testOptions?: Partial<ITerminalOptions>) { constructor(testOptions?: Partial<ITerminalOptions>) {
if (testOptions) { if (testOptions) {
for (const key of Object.keys(testOptions)) { for (const key of Object.keys(testOptions)) {
this.options[key] = testOptions[key]; this.rawOptions[key] = testOptions[key];
} }
} }
} }
+9 -4
View File
@@ -107,7 +107,7 @@ export class Buffer implements IBuffer {
return rows; return rows;
} }
const correctBufferLength = rows + this._optionsService.options.scrollback; const correctBufferLength = rows + this._optionsService.rawOptions.scrollback;
return correctBufferLength > MAX_BUFFER_SIZE ? MAX_BUFFER_SIZE : correctBufferLength; return correctBufferLength > MAX_BUFFER_SIZE ? MAX_BUFFER_SIZE : correctBufferLength;
} }
@@ -172,7 +172,7 @@ export class Buffer implements IBuffer {
if (this._rows < newRows) { if (this._rows < newRows) {
for (let y = this._rows; y < newRows; y++) { for (let y = this._rows; y < newRows; y++) {
if (this.lines.length < newRows + this.ybase) { if (this.lines.length < newRows + this.ybase) {
if (this._optionsService.options.windowsMode) { if (this._optionsService.rawOptions.windowsMode) {
// Just add the new missing rows on Windows as conpty reprints the screen with it's // Just add the new missing rows on Windows as conpty reprints the screen with it's
// view of the world. Once a line enters scrollback for conpty it remains there // view of the world. Once a line enters scrollback for conpty it remains there
this.lines.push(new BufferLine(newCols, nullCell)); this.lines.push(new BufferLine(newCols, nullCell));
@@ -252,7 +252,7 @@ export class Buffer implements IBuffer {
} }
private get _isReflowEnabled(): boolean { private get _isReflowEnabled(): boolean {
return this._hasScrollback && !this._optionsService.options.windowsMode; return this._hasScrollback && !this._optionsService.rawOptions.windowsMode;
} }
private _reflow(newCols: number, newRows: number): void { private _reflow(newCols: number, newRows: number): void {
@@ -367,6 +367,11 @@ export class Buffer implements IBuffer {
let srcCol = lastLineLength; let srcCol = lastLineLength;
while (srcLineIndex >= 0) { while (srcLineIndex >= 0) {
const cellsToCopy = Math.min(srcCol, destCol); const cellsToCopy = Math.min(srcCol, destCol);
if (wrappedLines[destLineIndex] === undefined) {
// Sanity check that the line exists, this has been known to fail for an unknown reason
// which would stop the reflow from happening if an exception would throw.
break;
}
wrappedLines[destLineIndex].copyCellsFrom(wrappedLines[srcLineIndex], srcCol - cellsToCopy, destCol - cellsToCopy, cellsToCopy, true); wrappedLines[destLineIndex].copyCellsFrom(wrappedLines[srcLineIndex], srcCol - cellsToCopy, destCol - cellsToCopy, cellsToCopy, true);
destCol -= cellsToCopy; destCol -= cellsToCopy;
if (destCol === 0) { if (destCol === 0) {
@@ -550,7 +555,7 @@ export class Buffer implements IBuffer {
i = 0; i = 0;
} }
for (; i < this._cols; i += this._optionsService.options.tabStopWidth) { for (; i < this._cols; i += this._optionsService.rawOptions.tabStopWidth) {
this.tabs[i] = true; this.tabs[i] = true;
} }
} }
+2 -2
View File
@@ -36,8 +36,8 @@ export class BufferService extends Disposable implements IBufferService {
@IOptionsService private _optionsService: IOptionsService @IOptionsService private _optionsService: IOptionsService
) { ) {
super(); super();
this.cols = Math.max(_optionsService.options.cols || 0, MINIMUM_COLS); this.cols = Math.max(_optionsService.rawOptions.cols || 0, MINIMUM_COLS);
this.rows = Math.max(_optionsService.options.rows || 0, MINIMUM_ROWS); this.rows = Math.max(_optionsService.rawOptions.rows || 0, MINIMUM_ROWS);
this.buffers = new BufferSet(_optionsService, this); this.buffers = new BufferSet(_optionsService, this);
} }
+2 -2
View File
@@ -62,7 +62,7 @@ export class CoreService extends Disposable implements ICoreService {
public triggerDataEvent(data: string, wasUserInput: boolean = false): void { public triggerDataEvent(data: string, wasUserInput: boolean = false): void {
// Prevents all events to pty process if stdin is disabled // Prevents all events to pty process if stdin is disabled
if (this._optionsService.options.disableStdin) { if (this._optionsService.rawOptions.disableStdin) {
return; return;
} }
@@ -83,7 +83,7 @@ export class CoreService extends Disposable implements ICoreService {
} }
public triggerBinaryEvent(data: string): void { public triggerBinaryEvent(data: string): void {
if (this._optionsService.options.disableStdin) { if (this._optionsService.rawOptions.disableStdin) {
return; return;
} }
this._logService.debug(`sending binary "${data}"`, () => data.split('').map(e => e.charCodeAt(0))); this._logService.debug(`sending binary "${data}"`, () => data.split('').map(e => e.charCodeAt(0)));
+1 -1
View File
@@ -46,7 +46,7 @@ export class LogService implements ILogService {
} }
private _updateLogLevel(): void { private _updateLogLevel(): void {
this.logLevel = optionsKeyToLogLevel[this._optionsService.options.logLevel]; this.logLevel = optionsKeyToLogLevel[this._optionsService.rawOptions.logLevel];
} }
private _evalLazyOptionalParams(optionalParams: any[]): void { private _evalLazyOptionalParams(optionalParams: any[]): void {
+6 -2
View File
@@ -10,7 +10,7 @@ describe('OptionsService', () => {
describe('constructor', () => { describe('constructor', () => {
const originalError = console.error; const originalError = console.error;
beforeEach(() => { beforeEach(() => {
console.error = () => {}; console.error = () => { };
}); });
afterEach(() => { afterEach(() => {
console.error = originalError; console.error = originalError;
@@ -26,7 +26,11 @@ describe('OptionsService', () => {
assert.equal(optionsService.getOption('cols'), 80); assert.equal(optionsService.getOption('cols'), 80);
}); });
it('uses default value if invalid constructor option value passed', () => { it('uses default value if invalid constructor option value passed', () => {
assert.equal(new OptionsService({tabStopWidth: 0}).getOption('tabStopWidth'), DEFAULT_OPTIONS.tabStopWidth); assert.equal(new OptionsService({ tabStopWidth: 0 }).getOption('tabStopWidth'), DEFAULT_OPTIONS.tabStopWidth);
});
it('object.keys return the correct number of options', () => {
const optionsService = new OptionsService({ cols: 80, rows: 25 });
assert.notEqual(Object.keys(optionsService.options).length, 0);
}); });
}); });
describe('setOption', () => { describe('setOption', () => {
+33 -28
View File
@@ -60,7 +60,7 @@ const FONT_WEIGHT_OPTIONS: Extract<FontWeight, string>[] = ['normal', 'bold', '1
export class OptionsService implements IOptionsService { export class OptionsService implements IOptionsService {
public serviceBrand: any; public serviceBrand: any;
private _options: ITerminalOptions; public readonly rawOptions: ITerminalOptions;
public options: ITerminalOptions; public options: ITerminalOptions;
private _onOptionChange = new EventEmitter<string>(); private _onOptionChange = new EventEmitter<string>();
@@ -68,12 +68,12 @@ export class OptionsService implements IOptionsService {
constructor(options: Partial<ITerminalOptions>) { constructor(options: Partial<ITerminalOptions>) {
// set the default value of each option // set the default value of each option
this._options = { ...DEFAULT_OPTIONS }; const defaultOptions = { ...DEFAULT_OPTIONS };
for (const key in options) { for (const key in options) {
if (key in this._options) { if (key in defaultOptions) {
try { try {
const newValue = options[key]; const newValue = options[key];
this._options[key] = this._sanitizeAndValidateOption(key, newValue); defaultOptions[key] = this._sanitizeAndValidateOption(key, newValue);
} catch (e) { } catch (e) {
console.error(e); console.error(e);
} }
@@ -81,34 +81,39 @@ export class OptionsService implements IOptionsService {
} }
// set up getters and setters for each option // set up getters and setters for each option
this.options = this._setupOptions(this._options); this.rawOptions = defaultOptions;
this.options = { ... defaultOptions };
this._setupOptions();
} }
private _setupOptions(options: ITerminalOptions): ITerminalOptions { private _setupOptions(): void {
const copiedOptions = { ... options }; const getter = (propName: string): any => {
for (const propName in copiedOptions) { if (!(propName in DEFAULT_OPTIONS)) {
Object.defineProperty(copiedOptions, propName, { throw new Error(`No option with key "${propName}"`);
get: () => { }
if (!(propName in DEFAULT_OPTIONS)) { return this.rawOptions[propName];
throw new Error(`No option with key "${propName}"`); };
}
return this._options[propName];
},
set: (value: any) => {
if (!(propName in DEFAULT_OPTIONS)) {
throw new Error(`No option with key "${propName}"`);
}
value = this._sanitizeAndValidateOption(propName, value); const setter = (propName: string, value: any): void => {
// Don't fire an option change event if they didn't change if (!(propName in DEFAULT_OPTIONS)) {
if (this._options[propName] !== value) { throw new Error(`No option with key "${propName}"`);
this._options[propName] = value; }
this._onOptionChange.fire(propName);
} value = this._sanitizeAndValidateOption(propName, value);
} // Don't fire an option change event if they didn't change
}); if (this.rawOptions[propName] !== value) {
this.rawOptions[propName] = value;
this._onOptionChange.fire(propName);
}
};
for (const propName in this.rawOptions) {
const desc = {
get: getter.bind(this, propName),
set: setter.bind(this, propName)
};
Object.defineProperty(this.options, propName, desc);
} }
return copiedOptions;
} }
public setOption(key: string, value: any): void { public setOption(key: string, value: any): void {
+6
View File
@@ -188,6 +188,12 @@ export const IOptionsService = createDecorator<IOptionsService>('OptionsService'
export interface IOptionsService { export interface IOptionsService {
serviceBrand: undefined; serviceBrand: undefined;
/**
* Read only access to the raw options object, this is an internal-only fast path for accessing
* single options without any validation as we trust TypeScript to enforce correct usage
* internally.
*/
readonly rawOptions: Readonly<ITerminalOptions>;
readonly options: ITerminalOptions; readonly options: ITerminalOptions;
readonly onOptionChange: IEvent<string>; readonly onOptionChange: IEvent<string>;
+35 -4
View File
@@ -5,10 +5,11 @@
import { deepStrictEqual, strictEqual, throws } from 'assert'; import { deepStrictEqual, strictEqual, throws } from 'assert';
import { Terminal } from 'headless/public/Terminal'; import { Terminal } from 'headless/public/Terminal';
import { ITerminalOptions } from 'xterm-headless';
let term: Terminal; let term: Terminal;
describe('Headless API Tests', function(): void { describe('Headless API Tests', function (): void {
beforeEach(() => { beforeEach(() => {
// Create default terminal to be used by most tests // Create default terminal to be used by most tests
term = new Terminal(); term = new Terminal();
@@ -119,13 +120,43 @@ describe('Headless API Tests', function(): void {
strictEqual(term.getOption('scrollback'), 50); strictEqual(term.getOption('scrollback'), 50);
}); });
describe('options', () => {
const termOptions = {
cols: 80,
rows: 24
};
beforeEach(async () => {
term = new Terminal(termOptions);
});
it('get options', () => {
const options: ITerminalOptions = term.options;
strictEqual(options.cols, 80);
strictEqual(options.rows, 24);
});
it('set options', async () => {
const options: ITerminalOptions = term.options;
throws(() => options.cols = 40);
throws(() => options.rows = 20);
term.options.scrollback = 1;
strictEqual(term.options.scrollback, 1);
term.options= {
fontSize: 12,
fontFamily: 'Arial'
};
strictEqual(term.options.fontSize, 12);
strictEqual(term.options.fontFamily, 'Arial');
});
});
describe('loadAddon', () => { describe('loadAddon', () => {
it('constructor', async () => { it('constructor', async () => {
term = new Terminal({ cols: 5 }); term = new Terminal({ cols: 5 });
let cols = 0; let cols = 0;
term.loadAddon({ term.loadAddon({
activate: (t) => cols = t.cols, activate: (t) => cols = t.cols,
dispose: () => {} dispose: () => { }
}); });
strictEqual(cols, 5); strictEqual(cols, 5);
}); });
@@ -133,7 +164,7 @@ describe('Headless API Tests', function(): void {
it('dispose (addon)', async () => { it('dispose (addon)', async () => {
let disposeCalled = false; let disposeCalled = false;
const addon = { const addon = {
activate: () => {}, activate: () => { },
dispose: () => disposeCalled = true dispose: () => disposeCalled = true
}; };
term.loadAddon(addon); term.loadAddon(addon);
@@ -145,7 +176,7 @@ describe('Headless API Tests', function(): void {
it('dispose (terminal)', async () => { it('dispose (terminal)', async () => {
let disposeCalled = false; let disposeCalled = false;
term.loadAddon({ term.loadAddon({
activate: () => {}, activate: () => { },
dispose: () => disposeCalled = true dispose: () => disposeCalled = true
}); });
strictEqual(disposeCalled, false); strictEqual(disposeCalled, false);
+51 -1
View File
@@ -7,19 +7,61 @@ import { IEvent } from 'common/EventEmitter';
import { BufferNamespaceApi } from 'common/public/BufferNamespaceApi'; import { BufferNamespaceApi } from 'common/public/BufferNamespaceApi';
import { ParserApi } from 'common/public/ParserApi'; import { ParserApi } from 'common/public/ParserApi';
import { UnicodeApi } from 'common/public/UnicodeApi'; import { UnicodeApi } from 'common/public/UnicodeApi';
import { IBufferNamespace as IBufferNamespaceApi, IMarker, IModes, IParser, ITerminalAddon, ITerminalOptions, IUnicodeHandling, Terminal as ITerminalApi } from 'xterm-headless'; import { IBufferNamespace as IBufferNamespaceApi, IMarker, IModes, IParser, ITerminalAddon, IUnicodeHandling, Terminal as ITerminalApi } from 'xterm-headless';
import { Terminal as TerminalCore } from 'headless/Terminal'; import { Terminal as TerminalCore } from 'headless/Terminal';
import { AddonManager } from 'common/public/AddonManager'; import { AddonManager } from 'common/public/AddonManager';
import { ITerminalOptions } from 'common/Types';
/**
* The set of options that only have an effect when set in the Terminal constructor.
*/
const CONSTRUCTOR_ONLY_OPTIONS = ['cols', 'rows'];
export class Terminal implements ITerminalApi { export class Terminal implements ITerminalApi {
private _core: TerminalCore; private _core: TerminalCore;
private _addonManager: AddonManager; private _addonManager: AddonManager;
private _parser: IParser | undefined; private _parser: IParser | undefined;
private _buffer: BufferNamespaceApi | undefined; private _buffer: BufferNamespaceApi | undefined;
private _publicOptions: ITerminalOptions;
constructor(options?: ITerminalOptions) { constructor(options?: ITerminalOptions) {
this._core = new TerminalCore(options); this._core = new TerminalCore(options);
this._addonManager = new AddonManager(); this._addonManager = new AddonManager();
this._publicOptions = { ... this._core.options };
const getter = (propName: string): any => {
return this._core.options[propName];
};
const setter = (propName: string, value: any): void => {
this._checkReadonlyOptions(propName);
this._core.options[propName] = value;
};
for (const propName in this._core.options) {
Object.defineProperty(this._publicOptions, propName, {
get: () => {
return this._core.options[propName];
},
set: (value: any) => {
this._checkReadonlyOptions(propName);
this._core.options[propName] = value;
}
});
const desc = {
get: getter.bind(this, propName),
set: setter.bind(this, propName)
};
Object.defineProperty(this._publicOptions, propName, desc);
}
}
private _checkReadonlyOptions(propName: string): void {
// Throw an error if any constructor only option is modified
// from terminal.options
// Modifications from anywhere else are allowed
if (CONSTRUCTOR_ONLY_OPTIONS.includes(propName)) {
throw new Error(`Option "${propName}" can only be set in the constructor`);
}
} }
private _checkProposedApi(): void { private _checkProposedApi(): void {
@@ -82,6 +124,14 @@ export class Terminal implements ITerminalApi {
wraparoundMode: m.wraparound wraparoundMode: m.wraparound
}; };
} }
public get options(): ITerminalOptions {
return this._publicOptions;
}
public set options(options: ITerminalOptions) {
for (const propName in options) {
this._publicOptions[propName] = options[propName];
}
}
public resize(columns: number, rows: number): void { public resize(columns: number, rows: number): void {
this._verifyIntegers(columns, rows); this._verifyIntegers(columns, rows);
this._core.resize(columns, rows); this._core.resize(columns, rows);
+4
View File
@@ -189,6 +189,10 @@ describe('API Integration Tests', function(): void {
assert.equal(await page.evaluate(`window.term.options.fontSize`), 30); assert.equal(await page.evaluate(`window.term.options.fontSize`), 30);
assert.equal(await page.evaluate(`window.term.options.fontFamily`), 'Arial'); assert.equal(await page.evaluate(`window.term.options.fontFamily`), 'Arial');
}); });
it('object.keys return the correct number of options', async () => {
await openTerminal(page);
assert.notEqual(await page.evaluate(`Object.keys(window.term.options).length`), 0);
});
}); });
describe('renderer', () => { describe('renderer', () => {
+23
View File
@@ -534,6 +534,29 @@ declare module 'xterm-headless' {
*/ */
readonly modes: IModes; readonly modes: IModes;
/**
* Gets or sets the terminal options. This supports setting multiple options.
*
* @example Get a single option
* ```typescript
* console.log(terminal.options.fontSize);
* ```
*
* @example Set a single option
* ```typescript
* terminal.options.fontSize = 12;
* ```
*
* @example Set multiple options
* ```typescript
* terminal.options = {
* fontSize: 12,
* fontFamily: 'Arial',
* };
* ```
*/
options: ITerminalOptions;
/** /**
* Natural language strings that can be localized. * Natural language strings that can be localized.
*/ */